# How Voting Systems Work 2, Exercise 1: find the spoilers # A club of 21 members ranks four candidates (assumed, invented numbers). # A candidate is a spoiler here if they do not win, but their withdrawal # changes who wins among the others. from collections import Counter profile = ( [("Ana", "Bo", "Cy", "Di")] * 6 + [("Bo", "Ana", "Cy", "Di")] * 5 + [("Cy", "Bo", "Ana", "Di")] * 7 + [("Di", "Cy", "Bo", "Ana")] * 3 ) def plurality_winner(ballots): counts = Counter(b[0] for b in ballots) top = max(counts.values()) return "/".join(sorted(c for c, v in counts.items() if v == top)) def without(ballots, candidate): return [tuple(c for c in b if c != candidate) for b in ballots] base = plurality_winner(profile) print("first choices:", dict(sorted(Counter(b[0] for b in profile).items()))) print("winner with everyone standing:", base) for c in sorted(profile[0]): if c == base: continue new = plurality_winner(without(profile, c)) verdict = "SPOILER" if new != base else "no effect" print(f" {c} withdraws -> winner {new:3s} ({verdict})") # Output when run: # first choices: {'Ana': 6, 'Bo': 5, 'Cy': 7, 'Di': 3} # winner with everyone standing: Cy # Ana withdraws -> winner Bo (SPOILER) # Bo withdraws -> winner Ana (SPOILER) # Di withdraws -> winner Cy (no effect)