The Problem with Normal Voting
In a normal election with 5 candidates, someone can win with just 21% of the vote. That means 79% of people didn't want the winner. This is called the spoiler effect - similar candidates split their supporters' votes.
Example: Pizza election
Pepperoni: 30% Cheese: 25% Margherita: 20%
Hawaiian: 15% Veggie: 10%
Pepperoni "wins" but 70% wanted something else!
How RCV Fixes This
Instead of picking just one option, you rank all options from favorite to least favorite. Then the algorithm simulates multiple rounds of voting.
Count first choices
Everyone's #1 pick gets counted. If someone has more than 50%, they win immediately.
Eliminate the loser
The option with the fewest first-choice votes is eliminated.
Redistribute votes
Anyone whose #1 was eliminated now has their #2 pick count instead. Their vote isn't wasted!
Repeat
Keep eliminating and redistributing until someone crosses 50%.
Walkthrough: 10 Voters, 4 Options
Imagine 10 students voting on the next project:
| Voter | #1 | #2 | #3 |
|---|---|---|---|
| 4 voters | Snake Game | Chat App | Todo App |
| 3 voters | Chat App | Snake Game | Weather |
| 2 voters | Weather | Chat App | Snake Game |
| 1 voter | Todo App | Chat App | Snake Game |
Round 1 - Count first choices
Snake Game: 4 | Chat App: 3 | Weather: 2 | Todo App: 1
Need 6 to win (>50% of 10). Nobody has it.
Todo App eliminated (1 vote, fewest).
Round 2 - Redistribute
That 1 Todo voter's #2 was Chat App, so Chat App gets +1.
Snake Game: 4 | Chat App: 4 | Weather: 2
Still need 6. Nobody has it.
Weather eliminated (2 votes, fewest).
Round 3 - Final redistribution
The 2 Weather voters' next active pick is Chat App.
Snake Game: 4 | Chat App: 6
Chat App wins with 6/10 (60%)!
Notice: Snake Game had the most first-choice votes, but Chat App was the most broadly acceptable choice. That's the power of RCV.
The Algorithm (Pseudocode)
function ranked_choice_vote(ballots):
active_candidates = all candidates
while true:
# Count first-choice votes
counts = {}
for ballot in ballots:
first_active = first candidate in ballot
that is still active
if first_active exists:
counts[first_active] += 1
total = sum of all counts
threshold = floor(total / 2) + 1
# Check for winner
for candidate in counts:
if counts[candidate] >= threshold:
return candidate # WINNER!
# No winner - eliminate last place
loser = candidate with min(counts)
remove loser from active_candidates
# Votes automatically redistribute
# next loop iteration
Why RCV Matters
No spoiler effect
Voting for your true favorite never hurts you. If they lose, your vote transfers.
Majority winner
The winner always has more than 50% support (among remaining voters).
One ballot
No need for runoff elections. Everything happens in a single vote.
Real world use
Used in NYC, Alaska, Australia, Ireland, and many organizations worldwide.
💻 Coding Challenge
Try implementing RCV yourself! Here's the function signature in Python:
def run_rcv(candidates: dict[int, str],
ballots: list[list[int]]) -> int | None:
"""
candidates: {1: "Snake Game", 2: "Chat App", ...}
ballots: [[2, 1, 3], [1, 3, 2], ...]
each ballot is candidate IDs in preference order
Returns: winning candidate ID, or None
"""
pass # Your code here!
Hint: You need a while loop, a way to count first-choice votes,
and a way to eliminate the loser each round.