Dividing Seats

How Voting Systems Work

Chapter 7 ยท Dividing Seats

How Voting Systems Work 1 to 6 asked how to pick a winner from ballots. A different question comes up whenever a fixed number of seats must be shared between groups of unequal size: states in a federation, regions in a parliament, parties after a list vote. This is apportionment. It sounds like simple division, and it would be if people came in fractions. The trouble is that a state cannot have 5.2 seats, so some rounding rule must decide, and every rounding rule has a character. The maths stays at division and comparison; the Python does the bookkeeping.

What was run, and what to trust
Every Python snippet in this chapter and in the three exercise solutions was executed, and the output in the comments is what Python printed. Historical and theoretical claims come from two web searches whose results were encyclopedia mirrors, a think-tank article, a maths-society column and papers; fetched pages are often truncated or model-summarised, so treat the wording as a paraphrase. The sources conflict on dates: some place the Alabama paradox in 1880, others in 1882; they differ on when each method was in use in the United States (one says Webster’s method was adopted in 1842 and replaced by Hamilton’s in 1852, others give 1900 to 1901 or the 1910 census), so I give only the dates they agree on. All populations in the code are invented or drawn from an assumed random model, not real census figures. Statements marked (check) were not confirmed against a source.

The Quota and the Rounding Problem

The fair share of a state is its quota: the number of seats times the state’s population, divided by the total population. If four invented states hold 5,200, 2,700, 1,300 and 800 people and the house has 10 seats, the total is 10,000 and the quotas are 5.2, 2.7, 1.3 and 0.8. Those add up to 10, but you cannot hand out 0.8 of a seat. Each state is also normally guaranteed at least one seat (true of the United States House; check for other systems), which already pulls against pure proportion.

The methods split into two families. Quota methods (Hamilton) start from the whole part of each quota and hand out leftovers. Divisor methods (Jefferson, Webster, Huntington-Hill) pick a divisor, divide every population by it, round, and adjust the divisor until the rounded numbers sum to the seat total. The only difference between the divisor methods is how they round.

MethodFamilyRounding ideaTends to favour
Hamiltonquota (largest remainder)whole part of quota, then biggest fractions get the spare seatsno steady bias, but paradoxes
Jeffersondivisoralways round downlarge states
Websterdivisorround to the nearest whole numbernearly neutral
Huntington-Hilldivisorround up if the quotient is above the geometric mean of the two nearest whole numberssmall states (slightly)

One Example, Four Methods

The code below implements all four. The divisor methods are written in the “one seat at a time” form: every state has a priority p / f(n), where p is its population and n the seats it holds, and the best priority takes the next seat. The function f is what differs: n + 1 gives Jefferson, n + 0.5 gives Webster, and sqrt(n(n+1)) gives Huntington-Hill. That square root is the geometric mean of n and n + 1. It is zero when n is zero, so Huntington-Hill must give every state one seat to start with. As far as I know the one-seat-at-a-time form gives the same result as the divisor form (check), apart from tie-breaks; here ties go to the alphabetically first name.

from math import floor, sqrt def quotas(pops, seats): """Each state's exact share of the seats: seats * population / total.""" total = sum(pops.values()) return {s: seats * p / total for s, p in pops.items()} def hamilton(pops, seats): """Largest remainder: everyone gets the whole part of the quota, leftovers go to the biggest fractions.""" q = quotas(pops, seats) 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], reverse=True)[:left]: alloc[s] += 1 return alloc def divisor(pops, seats, priority, start=0): """Highest-averages form of a divisor method: hand out seats one at a time to the best priority.""" alloc = {s: start for s in pops} for _ in range(seats - start * len(pops)): best = max(sorted(pops), key=lambda s: priority(pops[s], alloc[s])) alloc[best] += 1 return alloc def jefferson(pops, seats): return divisor(pops, seats, lambda p, n: p / (n + 1)) def webster(pops, seats): return divisor(pops, seats, lambda p, n: p / (n + 0.5)) def huntington_hill(pops, seats): return divisor(pops, seats, lambda p, n: p / sqrt(n * (n + 1)), start=1) # Illustrative numbers: four invented states and a 10-seat house. pops = {"Ardan": 5200, "Brex": 2700, "Cole": 1300, "Dunn": 800} print("quota ", {s: round(v, 2) for s, v in quotas(pops, 10).items()}) for name, rule in (("Hamilton", hamilton), ("Jefferson", jefferson), ("Webster", webster), ("Huntington-Hill", huntington_hill)): print(f"{name:<16}", rule(pops, 10)) # quota {'Ardan': 5.2, 'Brex': 2.7, 'Cole': 1.3, 'Dunn': 0.8} # Hamilton {'Ardan': 5, 'Brex': 3, 'Cole': 1, 'Dunn': 1} # Jefferson {'Ardan': 6, 'Brex': 3, 'Cole': 1, 'Dunn': 0} # Webster {'Ardan': 5, 'Brex': 3, 'Cole': 1, 'Dunn': 1} # Huntington-Hill {'Ardan': 5, 'Brex': 3, 'Cole': 1, 'Dunn': 1}

Jefferson’s round-down habit hands Ardan 6 seats against a quota of 5.2 and leaves Dunn with none, though Dunn holds 8% of the people. Hamilton, Webster and Huntington-Hill all agree on 5, 3, 1, 1 here; one example shows the shape of the differences, not their frequency.

A Little History

The sources I found agree on the following outline. Hamilton’s largest-remainder method was passed by Congress but vetoed by Washington (the first presidential veto, if I recall correctly; check), after which Jefferson’s method was used for decades. Webster’s method and Hamilton’s each had periods of use; sources disagree on the years, as the tip above says. Hamilton’s method was abandoned for the states, one source says in 1901, after it produced paradoxes. The House was fixed at 435 seats, and Huntington-Hill, proposed by the Census Bureau’s Joseph Hill and refined by the mathematician Edward Huntington, has been used since the 1941 apportionment. Sources note that some critics still prefer Webster’s method.

The Alabama Paradox

The paradox is named for a case in which, sources say, increasing the House from 299 to 300 seats would have cut Alabama from 8 seats to 7 (the discovery is dated 1880 by some sources and 1882 by others). More seats and a state loses one: it sounds impossible, but Hamilton’s method decides by remainders, and a bigger house changes every remainder. The code below recreates the effect with three invented states, growing the house one seat at a time.

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], reverse=True)[:left]: alloc[s] += 1 return alloc # Illustrative numbers: three invented states. We grow the house one seat at a time and watch for a state losing a seat. pops = {"Ana": 6000, "Bo": 6000, "Cy": 2000} previous = hamilton(pops, 2) for size in range(3, 16): now = hamilton(pops, size) for s in pops: if now[s] < previous[s]: print(f"house {size - 1} -> {size}: {s} falls from {previous[s]} to {now[s]}") print(f" size {size - 1}:", previous) print(f" size {size}:", now) previous = now # house 3 -> 4: Cy falls from 1 to 0 # size 3: {'Ana': 1, 'Bo': 1, 'Cy': 1} # size 4: {'Ana': 2, 'Bo': 2, 'Cy': 0} # house 10 -> 11: Cy falls from 2 to 1 # size 10: {'Ana': 4, 'Bo': 4, 'Cy': 2} # size 11: {'Ana': 5, 'Bo': 5, 'Cy': 1}

At 3 seats the quotas are about 1.29, 1.29 and 0.43. The whole parts give 1 seat each to Ana and Bo; the last seat goes to the biggest fraction, which is Cy’s 0.43. At 4 seats the quotas are about 1.71, 1.71 and 0.57, the whole parts are 1, 1 and 0, and the two spare seats go to the biggest fractions, 0.71 and 0.71, which belong to Ana and Bo. Cy’s quota grew from 0.43 to 0.57 and its seat still vanished, because the other two fractions grew faster.

Common mistakes
Treating the example above as typical when it was built to fail; assuming Jefferson’s bias means it is “rigged” (it is a rounding rule with a predictable tilt, which is a different thing); forgetting that tie-breaks and the one-seat minimum are rules too; and mixing up the Alabama paradox (a bigger house costs a state a seat) with the population paradox (a state that grows faster loses a seat to one that grows slower), which Exercise 3 hunts for.

Why There Is No Perfect Method

Hamilton’s method always keeps each state within its quota rounded down or up (the quota rule), but it can suffer the Alabama paradox and the population paradox, and, sources note, the new-states paradox. The divisor methods avoid those paradoxes but can violate the quota rule; Exercise 2 finds a Jefferson violation. The Balinski–Young impossibility theorem (1982, per the sources I found) says that for four or more states no method can always satisfy the quota rule and avoid the population paradox. A recent paper adds a way round it: randomised methods can satisfy both ideas in expectation, which a deterministic rule cannot. Which flaw to tolerate is a value judgement, not something mathematics settles.

Measuring the Tilt

Is Jefferson really biased towards large states? The simulation below draws 15 states with an assumed spread of populations (lognormal, which means a few large states and many smaller ones), shares 60 seats, and records how far the largest and smallest state land from their quotas on average, over 2,000 seeded trials.

import random from math import floor, sqrt def divisor(pops, seats, priority, start=0): alloc = {s: start for s in pops} for _ in range(seats - start * len(pops)): best = max(sorted(pops), key=lambda s: priority(pops[s], alloc[s])) alloc[best] += 1 return alloc 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], reverse=True)[:left]: alloc[s] += 1 return alloc methods = { "Hamilton": hamilton, "Jefferson": lambda p, k: divisor(p, k, lambda x, n: x / (n + 1)), "Webster": lambda p, k: divisor(p, k, lambda x, n: x / (n + 0.5)), "Huntington-Hill": lambda p, k: divisor(p, k, lambda x, n: x / sqrt(n * (n + 1)), start=1), } # Assumed model: 15 states with populations drawn from a lognormal spread, 60 seats, 2,000 seeded trials. random.seed(7) seats, trials = 60, 2000 gap = {m: [0.0, 0.0] for m in methods} # [largest state, smallest state]: average (seats - quota) for _ in range(trials): pops = {f"S{i:02d}": int(random.lognormvariate(0, 1) * 1000) + 1 for i in range(15)} total = sum(pops.values()) big, small = max(pops, key=pops.get), min(pops, key=pops.get) for m, rule in methods.items(): a = rule(pops, seats) gap[m][0] += a[big] - seats * pops[big] / total gap[m][1] += a[small] - seats * pops[small] / total for m in methods: print(f"{m:<16} largest state: {gap[m][0] / trials:+.3f} seats vs quota smallest: {gap[m][1] / trials:+.3f}") # Hamilton largest state: +0.001 seats vs quota smallest: -0.078 # Jefferson largest state: +1.429 seats vs quota smallest: -0.414 # Webster largest state: -0.003 seats vs quota smallest: -0.061 # Huntington-Hill largest state: -0.410 seats vs quota smallest: +0.488

On average Jefferson gave the largest state about 1.4 seats more than its quota. Webster and Hamilton sat almost exactly on the quota for the largest state, and Huntington-Hill gave the largest state about 0.4 seats less than its quota and the smallest 0.49 more. That last number is partly the one-seat minimum: with 60 seats and 15 unequal states, the smallest state often has a quota well under one seat but still gets one. The figures belong to this model; a real country’s population spread would change them, and the direction of each tilt is the lesson, not the size.

Exercise 1

Five invented states share 12 seats under Huntington-Hill. Start each state with one seat, then print each remaining seat as it is awarded with the winning priority and the runner-up’s. Which seat was closest?

๐Ÿ“„ View solution
Exercise 2

Search seeded random profiles of five states and 10 seats for a case where Jefferson’s method gives a state more than its quota rounded up (a quota-rule violation). Print the populations, the quota and the allocation.

๐Ÿ“„ View solution
Exercise 3

Search random four-state profiles with 20 seats for a population paradox under Hamilton’s method: state X grows by a larger percentage than state Y, yet X loses a seat while Y gains one.

๐Ÿ“„ View solution

Quick Reference

  • Quota: seats × state population ÷ total population
  • Hamilton: whole parts first, spare seats to the largest fractions; can show the Alabama and population paradoxes
  • Jefferson: priority p / (n + 1); rounds down; favours large states
  • Webster: priority p / (n + 0.5); rounds to nearest
  • Huntington-Hill: priority p / sqrt(n(n + 1)); every state starts with one seat; used for the US House since 1941
  • Alabama paradox: a larger house costs a state a seat
  • Balinski–Young: no deterministic method always meets the quota rule and avoids the population paradox (four or more states)
  • Always state: the populations, seat total, tie-break and minimum-seat rule when quoting a result
Coming next
How Voting Systems Work 8 is about districts and gerrymandering: drawing boundaries, packing and cracking, and measuring compactness and the efficiency gap.