Skip to content
On this page

工作中遇到的算法

最大余额法

可以保证百分比总和是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);

Updated at: