下面的代碼使用蠻力的方法(它不是最佳的),但也相當健壯:
#!usr/bin/env python
import random
import collections
# Candidates:
candidates = ['John', 'Max', 'Philip', 'Eric', 'Jane']
def simul_ballots(num_voters):
"""
Returns the (random) ballots of num_voters voters.
"""
ballots = []
choice = candidates[:]
for _ in range(num_voters):
random.shuffle(choice)
ballots.append(choice[:]) # Copy
return ballots
def get_counts(ballots):
"""
Returns the number of votes for each candidate placed first in the
ballots.
Candidates present in the ballots but found in any first ballot
places are given a count of zero.
"""
counts = dict()
for ballot in ballots:
vote = ballot[0]
if vote in counts:
counts[vote] += 1
else:
counts[vote] = 1
# Python 2.7+ replacement for the above code:
# counts = collections.Counter(ballot[0] for ballot in ballots)
candidates = set()
for ballot in ballots:
candidates.update(ballot)
for not_represented in set(candidates)-set(counts):
counts[not_represented] = 0
return counts
def get_winners(ballots):
"""
Returns the winners in the given ballots (lists of candidates), or
[] if there is no winner.
A winner is a candidate with 50 % or more of the votes, or a
candidate with as many votes as all the other candidates.
"""
counts = get_counts(ballots)
max_count = max(counts.values())
num_counts = sum(counts.values())
potential_winners = [candidate for (candidate, count) in counts.items()
if count == max_count]
if max_count >= num_counts/2. or len(potential_winners) == len(counts):
return potential_winners
else:
return []
def get_losers(ballots):
"""
Returns the loser(s) of the ballots, i.e. the candidate(s) with the
fewest voters.
Returns [] if all candidates have the same number of votes.
"""
counts = get_counts(ballots)
min_count = min(counts.values())
potential_losers = [candidate for (candidate, count) in counts.items()
if count == min_count]
if len(potential_losers) == len(counts):
return []
else:
return potential_losers
def remove_candidate(ballots, candidate):
"""
Removes the given candidate from the ballots.
"""
for ballot in ballots:
ballot.remove(candidate)
if __name__ == '__main__':
ballots = simul_ballots(20)
while True:
print "* Votes:"
for ballot in ballots:
print '-', ballot
print "=> Counts:", get_counts(ballots)
winners = get_winners(ballots)
if winners:
break
# The losers are removed:
losers = get_losers(ballots)
print '=> Losers:', losers
for loser in losers:
remove_candidate(ballots, loser)
print "Winners: ", winners
輸出是這樣的(帶有4個候選):
* Votes:
- ['Max', 'John', 'Eric', 'Philip']
- ['Philip', 'Max', 'Eric', 'John']
- ['Eric', 'Philip', 'John', 'Max']
- ['Philip', 'John', 'Max', 'Eric']
- ['Eric', 'Max', 'Philip', 'John']
- ['Max', 'Philip', 'John', 'Eric']
- ['Max', 'John', 'Eric', 'Philip']
- ['Eric', 'Philip', 'Max', 'John']
- ['Max', 'Eric', 'Philip', 'John']
- ['Philip', 'Max', 'Eric', 'John']
- ['John', 'Eric', 'Max', 'Philip']
- ['Philip', 'Eric', 'Max', 'John']
- ['Max', 'Philip', 'John', 'Eric']
- ['Philip', 'Max', 'John', 'Eric']
- ['Philip', 'Eric', 'Max', 'John']
- ['John', 'Philip', 'Eric', 'Max']
- ['John', 'Max', 'Philip', 'Eric']
- ['Eric', 'Philip', 'John', 'Max']
- ['John', 'Eric', 'Philip', 'Max']
- ['Philip', 'John', 'Max', 'Eric']
=> Counts: Counter({'Philip': 7, 'Max': 5, 'John': 4, 'Eric': 4})
=> Losers: ['John', 'Eric']
* Votes:
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Philip', 'Max']
- ['Philip', 'Max']
- ['Max', 'Philip']
- ['Max', 'Philip']
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Philip', 'Max']
- ['Philip', 'Max']
- ['Max', 'Philip']
- ['Philip', 'Max']
- ['Philip', 'Max']
- ['Philip', 'Max']
=> Counts: Counter({'Philip': 12, 'Max': 8})
Winners: ['Philip']
這代碼也可以使用Python 2.7+的集合模塊,如註釋中所示。
關係自動處理(所有並列候選人被宣佈爲獲勝者)。
可能的優化包括通過選票將選民分組(如果選民數量多於可能的選票),並通過重新分配來自失敗者的計數來更新計數(而不是重新進行完整的重新計數)。上述實現提供了一個參考實現,其結果可以與優化版本進行比較。 :)
哦,哇,你做了所有事情:D謝謝我非常感謝所付出的努力。get_counnts函數如何工作? – danem 2011-04-26 03:12:25
@佩特:謝謝你批准這個答案。我爲'get_count()'添加了Python <2.7代碼,它在候選人的第一個位置計數候選人。我還修復了一個罕見的錯誤,一個候選人不能出現在任何第一個位置,並作爲失敗者錯過。 – EOL 2011-04-26 09:02:58
要清楚,這不是即時徑流投票。考慮投票[['rock','paper','scissors'],['scissors','paper','rock']]。這個節目將宣佈岩石和剪刀之間的聯繫。根據IRV正確的贏家是'紙'。 – dmd 2016-05-31 19:27:08