|
|
@@ -2601,20 +2601,24 @@
|
|
|
// 反向调价逼近
|
|
|
reverseTenderApproach(callback){
|
|
|
let me = this;
|
|
|
- let G_DIGIT = 0.01; // 系数调整步距(0.1最终结果误差大。0.001目标金额与逼前金额差距大时无法有效逼近)
|
|
|
- let times = 100; // 逼近轮数
|
|
|
|
|
|
- let quickModel = false; // 快速模式
|
|
|
- let qmTimes = 50; // 快速模式下的极限次数
|
|
|
- let qmValue = 10; // 快速模式下允许的误差值上限
|
|
|
-
|
|
|
- let isTest = true; // 测试
|
|
|
+ let G_DIGIT = 0.01; // 系数调整步距(0.1最终结果误差大。0.001目标金额与逼前金额差距大时无法有效逼近)
|
|
|
+ let times = 200; // 逼近计算的极限次数。正常情况下“单位系数金额”列表中的结点耗尽即退出,这里指定轮数是最后保险阀,防止无限死循环。
|
|
|
+ let calcModel = 1; // 计算模式:1 精度优先(差值不接近0不停,直到结点用完或极限次数用完。时间长精度高)2 速度优先(达到指定差值范围即熔断逼近。时间短差值大)
|
|
|
+ let diffProp = 0.0001; // 速度优先模式下的:差值比例
|
|
|
+ let isTest = true; // 测试
|
|
|
|
|
|
// 取树结点的调价系数。
|
|
|
function getCoe(node){
|
|
|
return (calcTools.isVP_or_GLJR(node)) ? node.data.rationQuantityCoe : node.data.quantityCoe.labour;
|
|
|
}
|
|
|
|
|
|
+ // 按指定的比例获取可接受的差值:如目标金额的万分之一。
|
|
|
+ function getPropV(node){
|
|
|
+ let v = parseFloat((node.data.targetTotalFee * diffProp).toFixed(0)); // node.data.feesIndex.common.totalFee
|
|
|
+ return Math.max(v, 1);
|
|
|
+ }
|
|
|
+
|
|
|
// 取根结点的:调后金额跟目标金额差值,看还有多少误差需要处理。
|
|
|
function getRootDiff() {
|
|
|
let root = tender_obj.tenderTree.roots[0];
|
|
|
@@ -2633,7 +2637,7 @@
|
|
|
if (coe!= 0) {
|
|
|
let diff = Math.abs(node.data.feesIndex.common.tenderTotalFee - node.data.feesIndex.common.totalFee);
|
|
|
node.data.tender_diffValuePerCoe = (diff * G_DIGIT / coe).toDecimal(decimalObj.process);
|
|
|
- node.data.tender_rowNo = i + 1;
|
|
|
+ node.data.tender_rowNo = i + 1; // node在UI上显示的行号
|
|
|
arr.push(node);
|
|
|
}
|
|
|
}
|
|
|
@@ -2668,8 +2672,8 @@
|
|
|
return {idx: index, node: arr[index]}
|
|
|
}
|
|
|
|
|
|
- // 单轮逼近。arr 参考取值列表。
|
|
|
- // 返回{type, node, nodeIdx}。type:1正常,2无结点,3结点过调。node:结点。nodeIdx:结点在列表中的索引位置。
|
|
|
+ // 逼近(单轮)。arr 参考取值列表。返回{type, node, nodeIdx}。
|
|
|
+ // type:1正常,2无结点,3结点过调。node:结点。nodeIdx:结点在列表中的索引位置。node.data.tender_rowNo 在UI上的行号。
|
|
|
function approach(arr){
|
|
|
let v = getRootDiff();
|
|
|
let obj = getCloseNode(arr, Math.abs(v)); // {idx, node} 极端:{0, undefind}
|
|
|
@@ -2716,20 +2720,31 @@
|
|
|
if (isTest) console.log(` [索引${obj.nodeIdx} 行${obj.node.data.tender_rowNo}]过调,已回退`);
|
|
|
}
|
|
|
|
|
|
+ let root = tender_obj.tenderTree.roots[0];
|
|
|
let vArr = getNodeDiffs();
|
|
|
+ let propV = getPropV(root);
|
|
|
let d1 = getRootDiff();
|
|
|
- let root = tender_obj.tenderTree.roots[0];
|
|
|
|
|
|
- if (isTest) console.log(`调前${root.data.feesIndex.common.totalFee}|目标${root.data.targetTotalFee}|反向调价后不逼近的调后金额${root.data.feesIndex.common.tenderTotalFee}|差值${d1}`);
|
|
|
+ if (isTest){
|
|
|
+ let tq = root.data.feesIndex.common.totalFee;
|
|
|
+ let mb = root.data.targetTotalFee;
|
|
|
+ let bbj = root.data.feesIndex.common.tenderTotalFee;
|
|
|
+ let ms = (calcModel == 2) ? `速度优先(0~${propV})` : "精度优先";
|
|
|
+ console.log(`调前${tq}|目标${mb}|调后无逼近${bbj}|差值${d1}|${ms}`);
|
|
|
+ }
|
|
|
|
|
|
// 多轮逼进
|
|
|
for (let i = 1; i <= times; i++) {
|
|
|
- if (Math.abs(d1) < 1) break; // 误差在1以内,结果与目标金额完全一致,完美。
|
|
|
- if (quickModel){ // 快速模式:在指定的计算轮数内,能达到指定的差值范围内,也很理想。这个条件用于减少计算轮数,提高效率。
|
|
|
- if ((Math.abs(d1) < qmValue) && (times > qmTimes)) break;
|
|
|
+ // 与目标金额差值在1以内,整数部分已相同,结果很棒。很多时候能达到完美的差值0,但不能用0判断,因为金额取小数时有问题。
|
|
|
+ if (Math.abs(d1) < 1) break;
|
|
|
+
|
|
|
+ // 速度优先模式下,结果到达差值范围内,即熔断逼近。
|
|
|
+ if (calcModel == 2){
|
|
|
+ if (Math.abs(d1) < propV) break;
|
|
|
};
|
|
|
+
|
|
|
let obj = approach(vArr);
|
|
|
- if (obj.type == 2) break; // 列表已清空,无结点可调。
|
|
|
+ if (obj.type == 2) break; // 结点耗完,列表已清空,无结点可调。
|
|
|
if (obj.type == 3) { // 结点过调,该结点任务已完成,不再参与调节,从列表中清除
|
|
|
vArr.splice(obj.nodeIdx, 1);
|
|
|
continue;
|
|
|
@@ -2807,7 +2822,7 @@
|
|
|
this.reverseTenderInitDatas();
|
|
|
this.prepareForDistribute(tender_obj.tenderTree.roots[0]);
|
|
|
this.distributeTargetTotalFee(tender_obj.tenderTree.roots[0]);
|
|
|
- this.calcAllNodes(calcAllType.catAll, tender); // 先来个反算,得到基本的调后值(此值误差大,需要逼近)
|
|
|
+ this.calcAllNodes(calcAllType.catAll, tender); // 先来个反算(计算coe),得到基本的调后值(此值误差大,需要逼近)
|
|
|
this.reverseTenderApproach(callback); // 逼近上述调后值
|
|
|
}
|
|
|
else{
|