假設(shè)有數(shù)組 array = [7, 18, 23, 17]
要求計(jì)算每個(gè)數(shù)字占總和的百分比
普通計(jì)算方式
計(jì)算總和 7 + 18 + 23 + 17 = 65
每個(gè)數(shù)依次除以總和并乘以100
7 / 65 * 100 = 10.76923076923077
18 / 65 * 100 = 27.692307692307693
23 / 65 * 100 = 35.38461538461539
17 / 65 * 100 = 26.153846153846157四舍五入保留兩位小數(shù)
10.76923076923077 => 10.77
27.692307692307693 => 27.69
35.38461538461539 => 35.38
26.153846153846157 => 26.15
最終結(jié)果 10.77 + 27.69 + 35.38 + 26.15 = 99.99 < 100
完整代碼
let array = [7, 18, 23, 17]
let sum = array.reduce((total, value) => total + value)
let result = array.map(val => ((val / sum) *100).toFixed(2))
為了解決上面的問(wèn)題搁胆,于是就有了最大余額法蔑赘,至于什么是最大余額法甸饱,百度上有续滋,這里只講過(guò)程
最大余額法
計(jì)算總和 7 + 18 + 23 + 17 = 65
計(jì)算總份額
由于計(jì)算的是百分比阐斜,所以需要擴(kuò)大100倍
百分比保留兩位小數(shù)名惩,所以再擴(kuò)大100倍(三位小數(shù)就是1000倍)
所以總份額為 100 * 100 = 10000按比例分配份額
7 / 65 * 10000 = 1076.923076923077
18 / 65 * 10000 = 2769.2307692307693
23 / 65 * 10000 = 3538.461538461539
17 / 65 * 10000 = 2615.3846153846157取分配份額的整數(shù)部分 [1076, 2769, 3538, 2615]
1076 + 2769 + 3538 + 2615 = 9998 < 10000
10000 - 9998 = 2 因此還剩兩份需要分配取分配份額的小數(shù)部分 [0.923076923077, 0.2307692307693, 0.461538461539, 0.3846153846157]
找出小數(shù)部分最大的兩個(gè)數(shù)(剩幾份找?guī)讉€(gè))蛮拔,下標(biāo)分別為0 和 2為整數(shù)部分下標(biāo)為0 和 2的數(shù)各加上1
[1076 + 1, 2769, 3538 + 1, 2615] => [1077, 2769, 3539, 2615]除以100得到最終的百分比
因?yàn)楸A?位小數(shù)碟摆,之前乘以了100晃财,所以最后要除以100
[1077, 2769, 3539, 2615] / 100 => [10.77, 27.69, 35.39, 26.15]
最終結(jié)果 10.77 + 27.69 + 35.39 + 26.15 = 100
完整代碼
const getPercentValue = (array, precision = 2) => {
// 如果不是數(shù)字則賦值為0
let arrayList = array.map(value => (isNaN(value) ? 0 : value))
// 計(jì)算總和
let sum = arrayList.reduce((total, value) => total + value)
// 0不能做除數(shù),直接返回
if (sum === 0) return
// 小數(shù)位擴(kuò)大倍數(shù)
let digits = Math.pow(10, precision)
// 總份額
let total = digits * 100
// 分配份額
let shareList = arrayList.map(val => (val / sum) * total)
// 取整數(shù)部分
let integerList = shareList.map(val => Math.trunc(val))
// 計(jì)算整數(shù)部分的總和
let shareSum = integerList.reduce((total, val) => total + val)
// 取小數(shù)部分
let decimalsList = shareList.map((value, index) => value - integerList[index])
// 整數(shù)部分總和小于總份額時(shí)
while (shareSum < total) {
let max = decimalsList[0]
let maxIndex = 0
// 找出小數(shù)位最大的下標(biāo)
for (let i = 1; i < decimalsList.length; i++) {
if (decimalsList[i] > max) {
max = decimalsList[i]
maxIndex = i
}
}
// 小數(shù)位最大的加1
integerList[maxIndex] += 1
// 加1后小數(shù)清零典蜕,不參與下次比較
decimalsList[maxIndex] = 0
// 總數(shù)也需要加1
shareSum += 1
}
// 返回每項(xiàng)占比
return integerList.map(value => (value / digits).toFixed(precision))
}