-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathelectorate.py
More file actions
91 lines (82 loc) · 3.25 KB
/
electorate.py
File metadata and controls
91 lines (82 loc) · 3.25 KB
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
from graph import Graph
import random
class Electorate:
def __init__(self, districts):
self.graph = Graph(districts ** 2)
self._add_edges()
self.votes = [random.random() > 0.5 for _ in range(self.number_of_voters())]
def _add_edges(self):
v = self.number_of_voters()
d = self.district_size() # This is also the number of districts
for r in range(d):
for c in range(d):
i = r * d + c
if c < d - 1:
self.graph.add_edge(i, i + 1) # Edge to right
if r < d - 1:
if r % 2 == 0: # Even row
if c > 0:
self.graph.add_edge(i, i + d - 1) # Edge to lower left
self.graph.add_edge(i, i + d) # Edge to lower right
else: # Odd row
self.graph.add_edge(i, i + d) # Edge to lower left
if c < d - 1:
self.graph.add_edge(i, i + d + 1) # Edge to lower right
def number_of_voters(self):
return len(self.graph.adj)
def district_size(self): # This is also the number of districts
return int(self.number_of_voters() ** 0.5)
def graph_with_only_within_district_edges(self, districts):
g = Graph(self.number_of_voters())
d = self.district_size()
for district in districts:
for i in range(d):
for j in range(i + 1, d):
if district[j] in self.graph.neighbors(district[i]):
g.add_edge(district[i], district[j])
return g
def _number_of_connected_components(self, g):
def dfs(i):
found[i] = True
for j in g.neighbors(i):
if not found[j]:
dfs(j)
v = self.number_of_voters()
found = [False] * v
result = 0
for i in range(v):
if not found[i]:
dfs(i)
result += 1
return result
def is_valid_map(self, districts):
v = self.number_of_voters()
district_size = self.district_size()
sum = 0
counted = [False] * v
for district in districts:
if len(district) != district_size:
return False # District is wrong size
for voter in district:
if not (0 <= voter <= v):
return False # Invalid voter
if counted[voter]:
return False # Voter already assigned to another district
counted[voter] = True
sum += 1
if sum != v:
return False # Not enough voters counted
g = self.graph_with_only_within_district_edges(districts)
if self._number_of_connected_components(g) != district_size:
return False # Wrong number of districts or noncontiguous districts
return True
def get_wins(self, districts, party):
"""
Returns the number of districts won by party.
"""
result = 0
district_size = self.district_size()
for d in districts:
if sum(self.votes[i] == party for i in d) > district_size / 2:
result += 1
return result