Solution: von Neumann Extractor
// Simulated biased coin (you'd have actual coin in real scenario)
function biasedCoin(p = 0.7) {
return Math.random() < p ? "H" : "T";
}
function fairResult(coinFlip = biasedCoin) {
while (true) {
const flip1 = coinFlip();
const flip2 = coinFlip();
if (flip1 === "H" && flip2 === "T") {
return "Heads";
}
if (flip1 === "T" && flip2 === "H") {
return "Tails";
}
// HH or TT - try again
}
}
// Test with biased coin (p=0.7)
function testFairness(trials = 10000) {
let heads = 0;
for (let i = 0; i < trials; i++) {
if (fairResult(() => biasedCoin(0.7)) === "Heads") {
heads++;
}
}
console.log(`Heads: ${heads}/${trials} (${(heads/trials * 100).toFixed(2)}%)`);
// Should be close to 50%, even with biased coin!
}
testFairness();
// Output: Heads: 5012/10000 (50.12%)
Why This Works: Math Proof
Let p = probability of heads on biased coin.
For two flips:
- P(HT) = p Ć (1-p)
- P(TH) = (1-p) Ć p
- P(HH) = p à p = p²
- P(TT) = (1-p) à (1-p) = (1-p)²
Given we get different outcomes (HT or TH):
- P(HT | different) = p(1-p) / [p(1-p) + (1-p)p] = 1/2
- P(TH | different) = (1-p)p / [p(1-p) + (1-p)p] = 1/2
Perfect 50/50 split!
Expected Number of Flips
function expectedFlips(p) {
// Probability we get different results in 2 flips
const pDifferent = 2 * p * (1 - p);
// Expected pairs of flips until success
const expectedPairs = 1 / pDifferent;
// Total flips = pairs Ć 2
return expectedPairs * 2;
}
console.log(expectedFlips(0.5)); // 2.0 (fair coin)
console.log(expectedFlips(0.7)); // 4.76
console.log(expectedFlips(0.9)); // 11.11 (very biased!)
Optimized Version (Fewer Flips)
You can extract more than one fair bit per round:
function fairResultOptimized(coinFlip = biasedCoin) {
// Can extract multiple fair bits from one round
// This is more complex but uses fewer flips on average
while (true) {
const flip1 = coinFlip();
const flip2 = coinFlip();
if (flip1 !== flip2) {
return flip1 === "H" ? "Heads" : "Tails";
}
// Could continue extracting more bits here
// Advanced: Peres extraction method
}
}
Real-World Applications
This isn't just theoretical:
- Cryptographic random number generation
- Removing bias from physical random sources
- Fair leader election with biased channel
Follow-Up: Generate 0.25 Probability
My interviewer asked: "What if you want 25% probability?"
function generateQuarter(coinFlip = biasedCoin) {
// Use fairResult twice to get 4 equally likely outcomes
const bit1 = fairResult(coinFlip) === "Heads" ? 0 : 1;
const bit2 = fairResult(coinFlip) === "Heads" ? 0 : 1;
// 00 = 25%, return true
// 01, 10, 11 = 75%, return false
return bit1 === 0 && bit2 === 0;
}
Interview tip: This problem tests probability thinking more than coding. Make sure you can explain the math clearly.
Related Problems