Making the comparisons:
Totaling:
So Carlos is awarded the scholarship. However, the committee then discovers that Dimitry was not eligible for the scholarship (he failed his last math class). Even though this seems like it shouldn’t affect the outcome, the committee decides to recount the vote, removing Dimitry from consideration. This reduces the preference schedule to:
# Math in Society 2.11 -- Independence of Irrelevant Alternatives
# A committee awards one scholarship to Anna, Brian, Carlos or Dimitry.
from itertools import combinations
schedule = [(5, "DACB"), (5, "ACBD"), (6, "CBDA"), (4, "BDAC")] # EDIT any column
who = {"A": "Anna", "B": "Brian", "C": "Carlos", "D": "Dimitry"}
def copeland(s, show=False):
cs = sorted({c for _, r in s for c in r})
total = sum(n for n, _ in s)
pts = {c: 0.0 for c in cs}
for x, y in combinations(cs, 2):
a = sum(n for n, r in s if r.index(x) < r.index(y))
b = total - a
if a > b: pts[x] += 1
elif b > a: pts[y] += 1
else: pts[x] += .5; pts[y] += .5
if show:
verdict = f"{x} +1" if a > b else (f"{y} +1" if b > a else "tie, +1/2 each")
print(f" {x} vs {y}: {a:>3} to {b:<3} {verdict}")
best = max(pts.values())
winners = sorted(c for c in cs if pts[c] == best)
return winners, pts
def drop(s, gone):
return [(n, r.replace(gone, "")) for n, r in s]
print("All four candidates on the ballot:")
winners, pts = copeland(schedule, show=True)
print(" totals: " + ", ".join(f"{who[c]} {pts[c]:g}" for c in sorted(pts)))
print(f" -> the scholarship goes to {' and '.join(who[c] for c in winners)}\n")
print("Now remove one LOSING candidate at a time and recount the same ballots:")
for gone in sorted(who):
if gone in winners:
continue
w2, p2 = copeland(drop(schedule, gone))
flag = "" if w2 == winners else " <-- IIA VIOLATION: the winner changed"
totals = ", ".join(f"{who[c]} {p2[c]:g}" for c in sorted(p2))
print(f" without {who[gone]:<8} totals {totals}"
f" -> {' and '.join(who[c] for c in w2)}{flag}")
print("\nDimitry never had a chance of winning, so withdrawing him should not matter.")
print("It does. Removing an irrelevant alternative rearranges who beats whom in the")
print("remaining head-to-head comparisons, and a different student gets the money.")
print("\nTry it: change the 5-voter column from 'DACB' to 'DCAB' and re-run. Five")
print("voters swapping their 2nd and 3rd choices is enough to make Carlos beat Anna")
print("head to head, and then no withdrawal can change the outcome at all.")
Totaling:
Suddenly Anna is the winner! This leads us to another fairness criterion.