工作中遇到的算法
最大余额法
可以保证百分比总和是100%,用于计算
echart
的百分比
js
const getPercentValue = (valueList, index, precision) => {
if (!valueList[index]) {
return 0;
}
//求和
let sum = valueList.reduce((acc, val) => {
return acc + (isNaN(val) ? 0 : val);
}, 0);
if (sum === 0) {
return 0;
}
//10的2次幂是100,用于计算精度。
let digits = Math.pow(10, precision);
// 扩大比例 100
// [1428.5714285,2857.142857142857,5714.285714285714]
let votesPerQuota = valueList.map(val => {
return (isNaN(val) ? 0 : val) / sum * digits * 100;
});
//总数,扩大比例意味的总数要扩大
let targetSeats = digits * 100; // 10000
//再向下取值,组成数组 [1428, 2857, 5714]
let seats = votesPerQuota.map(votes => {
return Math.floor(votes);
});
// 获取余数 [0.5714285714284415, 0.142857142856883, 0.285714285713766]
let remainder = votesPerQuota.map((votes, index) => {
return votes - seats[index];
});
// 计算去除余数之后的合,用于判断与总数量是否相同,相同则占比会100%
let currentSum = seats.reduce((acc, val) => {
return acc + val;
}, 0);
while (currentSum < targetSeats) {
let max = Number.NEGATIVE_INFINITY; // -Infinity
let maxId = null;
// 找到差距最大的 item
for (let i = 0, len = remainder.length; i < len; ++i) {
if (remainder[i] > max) {
max = remainder[i];
maxId = i;
}
}
//对最大项余额加1
++seats[maxId];
// 已经增加最大余数加1,则下次判断就可以不需要再判断这个余额数。
// 因为 max 不可能比 0 还要小
remainder[maxId] = 0;
//总的也要加1,为了判断是否总数是否相同,跳出循环。
++currentSum;
}
// [1429, 2857, 5714]
// 这时候的 seats 就会总数占比会100%
return seats[index] / digits;
};
// array , index(下标), digits(精度)
getPercentValue([1,2,4],2,2);