我试图在JavaScript中实现IRV,但是每5秒只能获得10对投票(仅用于开发测试),因此,每当有新的一对投票添加到数组时,将其添加到数组中是不切实际的客户。因此,我想知道在接收到数据时是否有任何方法可以处理这些数据,并与将要接收的新数据保持一致。
const candidates = ['candidate1', 'candidate2', 'candidate3'];
// I Will Receive more votes after 5 secs (or any in prod) that will replace this
let votes = [['candidate1', 'candidate3', 'candidate2'],
['candidate2', 'candidate1', 'candidate3'] /* etc*/];
// I Want to calculate the winner at the time
// But after 5 secs the array won't have the previous votes
我要问当时是否有任何计算IRV Winner的算法,知道几秒钟后以前的值都消失了,因此无法以正常方式进行计算。
我们有什么办法可以根据他们的位置给他们分,可以增加他们的价格,但仍然可以赢得赢家?
这是我想出的,但是没有按预期运行。但这应该可以让您了解我的需求。
// The Base Votes (only one for the demo)
let votes = [['candidate1', 'candidate3', 'candidate2']];
// Stores the Ranks Per Candidate
let ranked = [0, 0, 0];
// Calculates the IRV
function irv() {
votes.forEach(vote => {
let points = vote.length;
vote.forEach((candidate, i) => {
ranked[i] += points;
points--;
});
});
}
// Calculate the Winner at the time
irv();
// Add new Votes
votes = [['candidate2', 'candidate1', 'candidate3']];
// ReCalculate the Winner
irv();
// Show the Winner
console.log(ranked);
在IRV之后,应在候选1和候选2之间建立平局,此方法将候选1作为获胜者。您能帮我解决这个问题还是给我一个更好的算法来解决这个问题?
答案 0 :(得分:1)
这是一个IRV实现(可能过于简单):
const irv = (ballots) => {
const candidates = [... new Set (ballots .flat())]
const votes = Object .entries (ballots .reduce (
(votes, [v]) => {votes [v] += 1; return votes},
Object .assign (... candidates .map (c => ({[c]: 0})))
))
const [topCand, topCount] =
votes .reduce (([n, m], [v, c]) => c > m ? [v, c] : [n, m], ['?', -Infinity])
const [bottomCand, bottomCount] =
votes .reduce (([n, m], [v, c]) => c < m ? [v, c] : [n, m], ['?', Infinity])
return topCount > ballots .length / 2
? topCand
: irv (ballots .map (ballot => ballot .filter (c => c != bottomCand)) .filter (b => b.length > 0))
}
const ballots = [
['A', 'C', 'B', 'E', 'D'],
['B', 'D', 'A', 'F', 'G'],
['C', 'B', 'D'],
['B', 'D', 'E', 'A', 'H'],
['A', 'B', 'G', 'H', 'F'],
['G', 'E', 'B', 'H', 'D'],
['E', 'C', 'D', 'B', 'G'],
['A', 'B'],
['D', 'G'],
['F', 'E', 'C'],
['F', 'C', 'G', 'A', 'E'],
['B', 'C', 'G', 'A', 'E'],
['A', 'B', 'F', 'G'],
]
console .log (irv (ballots))
(请注意,根据我的评论,我不会尝试在两次调用之间保持任何特定状态。如果您需要累积选票,我建议您单独进行此操作,然后在这些选票到达时运行此功能。如果太贵了,那么可以根据到目前为止收到的选票按固定的时间表运行。)
我们的选票只是候选人选择的有序列表。最后,我们选择候选人B,方法是依次淘汰第一名得票最少的候选人,并将其余选票上的候选人再上移一位。
代码首先通过拉平选票列表并选择集合的所有唯一成员来收集候选人。然后,我们计算每个候选人的第一票。格式为[["A", 4], ["C", 1],["B", 3], ["E", 1], ["D", 1], ["F", 2], ["G", 1], ["H", 0]]
,这使得在接下来的两个步骤中,使用基于reduce
的最小选择器和最大选择器,最容易找到顶部和底部候选人。
这里的算法很幼稚,只需选择具有该值的第一个即可打破平局,该值取决于投票顺序。完整的IRV系统将具有比这更好的机制。
然后,最后,如果排名靠前的候选人的总数超过总数的一半,我们将退还该候选人。如果没有,我们将从所有选票中删除最底层的候选人,然后使用其余候选人重新运行该功能。
此版本是递归的,因为它可以使代码更简洁。但请注意,除非您与成千上万的候选人竞争,否则递归深度不太可能成为问题,因为每个递归步骤都会减少候选人的数量。
在第一轮中,我们进行了以下投票:
ACBED, BDAFG, CBD, BDEAH, ABGHF, GEBHD, ECDBG, AB, DG, FEC, FCGAE, BCGAE, ABFG
具有以下第一名的投票计数:
{A: 4, B: 3, C: 1, D: 1, E: 1, F: 2, G: 1, H: 0}
我们还没有多数表决。由于H
的计数最低,因此我们将其从每次投票中删除:
ACBED, BDAFG, CBD, BDEAH, ABGHF, GEBHD, ECDBG, AB, DG, FEC, FCGAE, BCGAE, ABFG
ACBED, BDAFG, CBD, BDEA, ABGF, GEBD, ECDBG, AB, DG, FEC, FCGAE, BCGAE, ABFG
这些第一名的投票数:
{A: 4, B: 3, C: 1, D: 1, E: 1, F: 2, G: 1}
我们随机选择首位计数最低的一个C
,然后将其删除:
ABED, BDAFG, BD, BDEA, ABGF, GEBD, EDBG, AB, DG, FE, FGAE, BGAE, ABFG
{A: 4, B: 4, D: 1, E: 1, F: 2, G: 1}
然后我们删除E
:
ABD, BDAFG, BD, BDA, ABGF, GBD, DBG, AB, DG, F, FGA, BGA, ABFG
{A: 4, B: 4, D: 2, F: 2, G: 1}
然后G
:
ABD, BDAF, BD, BDA, ABF, BD, DB, AB, D, F, FA, BA, ABF
{A: 4, B: 5, D: 2, F: 2}
还有D
:
AB, BAF, B, BA, ABF, B, B, AB, F, FA, BA, ABF
{A: 4, B: 6, F: 2}
然后是F
:
AB, BA, B, BA, AB, B, B, AB, A, BA, AB
{A: 5, B: 6}
在这里,我们终于有了多数,然后返回B
。