-
Notifications
You must be signed in to change notification settings - Fork 0
/
election.py
93 lines (67 loc) · 2.6 KB
/
election.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
from voter import Voter
import random
class Election:
def __init__(self, candidates, voters):
self.candidates = [Voter() for _ in range(candidates)]
self.voters = [Voter() for _ in range(voters)]
def set_candidates(self, candidates):
self.candidates = candidates
def set_voters(self, voters):
self.voters = voters
def election_quality(self, winner):
return sum([v.candidate_distance(winner) for v in self.voters])
def vote(self):
tally = {i: 0 for i in range(len(self.candidates))}
for v in self.voters:
tally[v.vote(self.candidates)] += 1
tally_items = list(tally.items())
tally_items.sort(key=lambda x: x[1], reverse=True)
return self.candidates[tally_items[0][0]]
def approval(self):
tally = {candidate: 0 for candidate in self.candidates}
for v in self.voters:
for c in v.approval(self.candidates):
tally[c] += 1
tally_items = list(tally.items())
tally_items.sort(key=lambda x: x[1], reverse=True)
return tally_items[0][0]
def ranked_choice(self):
remaining = set(self.candidates)
while len(remaining) > 1:
votes = {r: 0 for r in remaining}
for v in self.voters:
votes[v.rank(remaining)[0]] += 1
min_val, min_key = max(votes.values()), None
for k, v in votes.items():
if v <= min_val:
min_val, min_key = v, k
remaining.remove(min_key)
return remaining.pop()
def random(self):
return random.choice(self.candidates)
def test(trials, candidates, voters):
"""
Create an election with the given number of candidates and voters.
Run the election for the given number of trials.
Return the average election quality of vote, approval, ranked choice, and random vote
"""
e = Election(candidates, voters)
results = {
e.vote: 0,
e.approval: 0,
e.ranked_choice: 0,
e.random: 0,
}
for _ in range(trials):
run = normalize([e.election_quality(m()) for m in results.keys()])
for i, m in enumerate(results):
results[m] += run[i]
return list(sorted({k.__name__: v / trials for k, v in results.items()}.items(), key=lambda x: x[1]))
def normalize(values):
smallest = min(values)
largest = max(values)
if smallest == largest:
return [1] * len(values)
return [((v - smallest) / (largest - smallest)) for v in values]
if __name__ == '__main__':
print(test(100, 25, 1000))