我正在一个项目中,我需要分配一个数组的值,这些值的总和必须为100。
示例:
[70, 34, 92]
和总值100
。输出为[35.71, 17.35, 46.94]
,因为35.71 + 17.35 + 46.94 = 100
。[86, 99.5, 100]
和总值100
。输出为[30.12, 34.85, 35.03]
。[96, 37]
和总值100
。输出为[72.18, 27.82]
。[98, 76.5, 68.5, 63.5, 38.5]
和总值100
。输出为[28.41, 22.17, 19.86, 18.41, 11.15]
= 100(sum)。到目前为止,我的解决方案是
.toFixed(2)
。“公式/计算”:+(((valueOfArray * 100) / totalSumOfGivenArray).toFixed(2))
我的第一个解决方案(有小数点的问题,所以不可接受):
prioritySum = 0;
controlsKey.forEach((value) => {
priorityVal = this.getDistributedValue(+form.controls[formId].value, totalCatSum); // return +(((valueOfArray * 100) / totalSumOfGivenArray).toFixed(2))
prioritySum = prioritySum + priorityVal;
});
此解决方案有时不能完全返回100值。输出各种内容,例如99.99、99.999999、100(大部分),100.0000000001、99.999978、100.09999999、99.000009。此处的十进制数字存在一些问题。
我使用的另一种方法是:
let i = 0;
for(i = 0; i < controlsKey.length-1; i++){
let formId = controlsKey[i];
relValue = this.getDistributedValue(+form.controls[formId].value, totalActualSum); // returns formula result
form.controls[formId].setValue(relValue))
relativeSum = relativeSum + relValue;
}
relValue = 100 - relativeSum;
form.controls[controlsKey[i]].setValue(relValue)
这很好,但是这个解决方案嗯...
所以我的问题是否有解决这个问题的优雅方法?
第一种解决方案对我来说还可以,但是即使我使用了.toFixed(2)
答案 0 :(得分:1)
因此,最大的问题是浮点数固有的不准确性。如果您不使用toFixed
,则无法解决的问题-即使您确定了错误,然后选择一个元素并将其增加/减少错误的数量,您也会只需沿 other 方向减去该量,因为浮点数不能在所有操作中保持必要的精度:
function getDistribution(values) {
var sum = values.reduce((carry, current) => carry + current, 0)
var distribution = values.map(v => 100 * v / sum)
var err = distribution.reduce((carry, current) => carry+current, 0) - 100;
console.log({distribution, err});
distribution[0] -= err;
err = distribution.reduce((carry, current) => carry+current, 0) - 100;
console.log({distribution, err});
}
getDistribution([98, 76.5, 68.5, 63.5, 38.5]);
答案 1 :(得分:1)
为防止出现不必要的浮点值,可以使用整数值并从基数10000
(100和两个位置)中减去该值,并创建一个字符串数组,在该字符串数组中将点插入到获取格式化的字符串。
const nice = s => s.toString().replace(/\d\d$/, '.$&');
function get100(array) {
var sum = array.reduce((a, b) => a + b),
offset = 1e4;
return array.map((v, j, { length }) => {
if (j + 1 === length) return nice(offset);
var i = Math.round(v * 1e4 / sum);
offset -= i;
return nice(i);
});
}
console.log(...get100([70, 34, 92])); // [35.71, 17.35, 46.94]
console.log(...get100([86, 99.5, 100])); // [30.12, 34.85, 35.03].
console.log(...get100([96, 37])); // [72.18, 27.82].
console.log(...get100([98, 76.5, 68.5, 63.5, 38.5])); // [28.41, 22.17, 19.86, 18.41, 11.15]