# How Voting Systems Work 7, Exercise 3: look for the population paradox under Hamilton's method. Two of four states: X grows faster than B, # yet X loses a seat and Y gains one. Populations and growth rates are drawn at random (assumed model). import random from math import floor def hamilton(pops, seats): total = sum(pops.values()) q = {s: seats * p / total for s, p in pops.items()} alloc = {s: floor(v) for s, v in q.items()} left = seats - sum(alloc.values()) for s in sorted(q, key=lambda s: (-(q[s] - alloc[s]), s))[:left]: alloc[s] += 1 return alloc random.seed(12) seats = 20 for attempt in range(1, 200001): old = {"A": random.randint(1000, 9000), "B": random.randint(1000, 9000), "C": random.randint(1000, 9000), "D": random.randint(1000, 9000)} grow = {s: random.randint(0, 600) for s in old} new = {s: old[s] + grow[s] for s in old} a, b = hamilton(old, seats), hamilton(new, seats) for x in old: for y in old: if x != y and new[x] / old[x] > new[y] / old[y] and b[x] < a[x] and b[y] > a[y]: print("attempt", attempt) print("old populations", old, "->", a) print("new populations", new, "->", b) print(f"{x} grew {new[x] / old[x] - 1:.2%}, {y} grew {new[y] / old[y] - 1:.2%}; {x} lost a seat, {y} gained one") raise SystemExit print("none found") # attempt 52 # old populations {'A': 2507, 'B': 7336, 'C': 3555, 'D': 5474} -> {'A': 2, 'B': 8, 'C': 4, 'D': 6} # new populations {'A': 2529, 'B': 7440, 'C': 3928, 'D': 5984} -> {'A': 3, 'B': 7, 'C': 4, 'D': 6} # B grew 1.42%, A grew 0.88%; B lost a seat, A gained one