<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>
</html>
/**
Rules -
https://zh.wikipedia.org/wiki/%E8%81%94%E7%AB%8B%E5%88%B6
https://en.wikipedia.org/wiki/Mixed-member_proportional_representation
1)各政黨總當選名額,以全國總應選名額依各政黨在第二張投政黨名單的得票比率分配。(假設使用與目前不分區算法相同的最大餘數法)
2)扣除該黨在選舉區已得議席
3)其差額再由政黨比例代表名額中補足。
@param {Number} totalSeat - 總席次數量,正整數。
@param {Number} threshold - 小黨門檻(5 代表 5%)。程式本身沒有檢查說加起來要 = 100%,未滿 100% 的部分,視同未過門檻的小黨們的和。
@param {Number[]} partyVotePercentages - 各黨政黨票(不分區)得票率百分比(34 表示 34%)
@param {Number[]} localSeats - 各黨區域立委席次
@return {Object()} list of seats - 各黨總席次
*/
function calculateSeatsMMP(totalSeat, threshold, partyVotePercentages, localSeats) {
let expectedSeats = calculateSeats(totalSeat, partyVotePercentages, threshold).map(party => party.seat)
return localSeats.map((localSeat, idx) => expectedSeats[idx] < localSeat ? localSeat : expectedSeats[idx])
}
// Test cases.
//
console.table([
{
// The one in wikipedia ( https://zh.wikipedia.org/wiki/%E8%81%94%E7%AB%8B%E5%88%B6 )
//
actual:calculateSeatsMMP(100, 5, [25, 5, 40, 30], [15, 6, 19, 20]),
expected: [25, 6, 40, 30]
},
{
// 增額增到爆炸:小黨都沒超過門檻(6%),單一大黨過門檻;
// 小黨的區域當選,全都變成增額名額,大黨把席次全吃下來。
//
actual:calculateSeatsMMP(10, 6, [5,5,90], [1,2,5]),
expected: [1,2,10]
},
{
// 測試 threshold。沒有增額。
//
actual:calculateSeatsMMP(41, 5, [2,3,50,45], [0,0,10,10]),
expected: [0,0,22,19]
}
])
// Libraries
//
// From: https://github.com/g0v/partyvote2016/blob/gh-pages/main.js
//
function calculateSeats(totalSeat, stage1votes, threshold) {
// Apply rule 5 & rule 1
//
var stage1sum = stage1votes.reduce(function(s, p){
return s + (p >= threshold ? p : 0)
}, 0);
var stage2votes = stage1votes.map(function(p){
return p >= threshold ? +(p * 100 / stage1sum).toFixed(2) : 0
});
// Apply rule 2
//
var stage2totalSeat = 0
var partiesData = stage2votes.map(function(p, idx){
var seat = totalSeat * p / 100, flooredSeat = Math.floor(seat);
stage2totalSeat += flooredSeat;
return {
id: idx, // partiesData will be sorted later, thus requires idx
seat: flooredSeat,
remain: seat - flooredSeat
}
})
var result = stage2votes.map(function(p, idx){
return {
stage1votes: stage1votes[idx],
value: p,
seat: partiesData[idx].seat
}
});
// Apply rule 3
//
shuffle(partiesData).sort(function(a, b){return b.remain-a.remain})
while(stage2totalSeat < totalSeat) {
var partyData = partiesData.shift();
result[partyData.id].seat += 1;
stage2totalSeat += 1;
}
return result;
}
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |