# How Voting Systems Work 10, Exercise 1: add a top-two runoff to the toolkit and compare it. # Assumed model: the chapter's uniform electorates (25 voters, 5 candidates, 2000 elections, seed 31). # Needs votelab.py (the chapter's toolkit) in the same folder. import random from votelab import make_electorate, make_ballots, condorcet, plurality, irv, regret def runoff(b): """Two rounds: the two largest first-place totals meet; each voter backs whichever they rank higher.""" t = [0] * b["n"] for r in b["rank"]: t[r[0]] += 1 order = sorted(range(b["n"]), key=lambda c: (-t[c], c)) a, c = order[0], order[1] a_votes = sum(1 for r in b["rank"] if r.index(a) < r.index(c)) return a if a_votes * 2 >= len(b["rank"]) else c rng = random.Random(31) contenders = {"plurality": plurality, "runoff": runoff, "irv": irv} has_cw, agree, reg = 0, {k: 0 for k in contenders}, {k: 0.0 for k in contenders} runs = 2000 for _ in range(runs): voters, cands = make_electorate(rng, "uniform") b = make_ballots(voters, cands) cw = condorcet(b) has_cw += cw is not None for name, m in contenders.items(): w = m(b) reg[name] += regret(voters, cands, w) agree[name] += (cw is not None and w == cw) for name in contenders: print(f"{name:>9}: picks Condorcet winner {agree[name] / has_cw:.1%}, mean regret {reg[name] / runs:.4f}") # Output from a real run: # plurality: picks Condorcet winner 54.4%, mean regret 0.0289 # runoff: picks Condorcet winner 74.4%, mean regret 0.0131 # irv: picks Condorcet winner 77.2%, mean regret 0.0111