最終結(jié)果呈現(xiàn)
怎么保證所有占比之和等于100%,什么是最大余額法?
echarts的餅圖的百度通過傳入數(shù)據(jù)自動算出每個項在總數(shù)當(dāng)中所占的比例大小堕花,且保證所有的比率相加等于100%未舟,那他是如何計算百分比的呢圈暗。通過閱讀源碼我們可以得知他使用最大余額法,那什么是最大余額法呢裕膀?
概念說明
那什么是最大余額方法呢员串?以下概念摘自維基百科,有一點(diǎn)晦澀難懂昼扛,下一節(jié)通過具體例子分析一下就好懂多了最大余額方法(英文:Largest Remainder Method)又稱數(shù)額制寸齐,是比例代表制投票制度下,一種議席分配的方法,相對于最高均數(shù)方法渺鹦。
透過最大余額方法扰法,候選人須以名單參選,每份名單的人數(shù)最多可達(dá)至相關(guān)選區(qū)內(nèi)的議席數(shù)目毅厚。候選人在名單內(nèi)按優(yōu)先次序排列迹恐。選民投票給一份名單,而不是個別候選人卧斟。投票結(jié)束后殴边,把有效選票除以數(shù)額(quota,見下)珍语。一份名單每取得數(shù)額1倍的票數(shù)锤岸,便能獲分配一個議席。每份名單的候選人按原先訂立的順序當(dāng)選板乙。
如此類推是偷、將議席分配至每份名單的余額,均比數(shù)額為低的時候募逞,則從最大余額者順序分配余下議席蛋铆;最大余額方法因而得名。
具體例子
假設(shè)選舉投票人次100,000放接,分配10個議席刺啦。選舉結(jié)果:
黑爾數(shù)額為黑爾數(shù)額為 100000/10 = 10000張選票,即每張名單每獲得10,000張選票纠脾,便能首先得到1個議席:
因此玛瘸,名單丙、丁苟蹈、戊各得1席糊渊,名單己得4席。余下3席慧脱,則對比各個余額渺绒。其中名單乙、戊菱鸥、己的余額最大宗兼,因此分別獲選其余3席。
換言之采缚,在最大余額方法之下针炉,名單乙、丙扳抽、丁各得1席篡帕,名單戊得2席殖侵,名單己得5席。
源碼解析
看了以上的圖應(yīng)該對最大余額法有一點(diǎn)的了解了镰烧。那我們來看看源碼是如何通過最大余額法來進(jìn)行百分比分配的拢军。
以下通過一組例子數(shù)據(jù)來
數(shù)據(jù)值列表:[2, 4, 3]
精度:2(代表百分?jǐn)?shù)的值最多保留2位小數(shù))
期望結(jié)果: [ 22.22, 44.45, 33.33 ]
/**
*
* 給定一個精度值,計算某一項在一串?dāng)?shù)據(jù)中占據(jù)的百分比怔鳖,確保百分比總和是1(100%)
* 使用最大余額法
* @param {Array.} valueList a list of all data 一列數(shù)據(jù)
* @param {number} idx index of the data to be processed in valueList 索引值(數(shù)組下標(biāo))
* @param {number} precision integer number showing digits of precision 精度值
* @return {number} percent ranging from 0 to 100 返回百分比從0到100
*/
function getPercentWithPrecision (valueList, idx, precision) {
if (!valueList[idx]) {
return 0
? ? }
var sum = valueList.reduce(function (acc, val) {
return acc + (isNaN(val) ?0 : val)
}, 0)
if (sum ===0) {
return 0
? ? }
console.log('sum', sum)
// sum 9
? ? var digits = Math.pow(10, precision)// digits 100
? ? console.log('digits', digits)
var votesPerQuota = valueList.map(function (val) {
return (isNaN(val) ?0 : val) / sum * digits *100 // 擴(kuò)大比例茉唉,這樣可以確保整數(shù)部分是已經(jīng)確定的議席配額,小數(shù)部分是余額
? ? })
console.log('votesPerQuota', votesPerQuota)
// votesPerQuota [ 2222.222222222222, 4444.444444444444, 3333.333333333333 ] 每一個項獲得的議席配額结执,整數(shù)部分是已經(jīng)確定的議席配額度陆,小數(shù)部分是余額
? ? var targetSeats = digits *100 // targetSeats 10000 全部的議席
? ? console.log('targetSeats', targetSeats)
var seats = votesPerQuota.map(function (votes) {
// Assign automatic seats.
? ? ? ? return Math.floor(votes)
})
console.log('seats', seats)
// seats [ 2222, 4444, 3333 ] 獲取配額的整數(shù)部分
? ? var currentSum = seats.reduce(function (acc, val) {
return acc + val
}, 0)
console.log('currentSum', currentSum)
// 9999 表示已經(jīng)配額了9999個議席,還剩下一個議席
? ? var remainder = votesPerQuota.map(function (votes, idx) {
return votes - seats[idx]
})
console.log('remainder', remainder)
// [ 0.2222222222221717, 0.4444444444443434, 0.33333333333303017 ]得到每一項的余額
// Has remainding votes. 如果還有剩余的坐席就繼續(xù)分配
? ? while (currentSum < targetSeats) {
// Find next largest remainder. 找到下一個最大的余額
? ? ? ? var max = Number.NEGATIVE_INFINITY
var maxId =null
? ? ? ? for (var i =0, len = remainder.length; i < len; ++i) {
if (remainder[i] > max) {
max = remainder[i]
maxId = i
}
}
// max: 0.4444444444443434, maxId 1
// Add a vote to max remainder.
? ? ? ? ++seats[maxId]// 第二項献幔,即4的占比的坐席增加1
? ? ? ? remainder[maxId] =0
? ? ? ? ++currentSum// 總的已分配的坐席數(shù)也加1
? ? }
return seats[idx] / digits
}