Back to basics: Coin flips
By Michael Doornbos
- 5 minutes read - 1028 wordsBack to basics: Coin flips
Ever wondered about the odds of flipping a coin 10 times and getting all heads or all tails? Instead of calculating the probability mathematically (spoiler: it’s 1 in 512), let’s do what any self-respecting retro computing enthusiast would do – program a simulation on a Commodore 64!
The Probability Problem
The question is simple: If you flip a fair coin 10 times in a row, how often will you get either all heads or all tails? This is a classic probability problem that demonstrates the concept of independent events.
For each set of 10 flips, there are 2^10 = 1,024 possible outcomes. Only two of these outcomes satisfy our condition: all heads (HHHHHHHHHH) or all tails (TTTTTTTTTT). That gives us a probability of 2/1024 = 1/512 ≈ 0.00195, or about 0.2%.
But instead of trusting mathematics, let’s verify with actual (simulated) coin flips!
The Code
Here’s our Commodore 64 BASIC program that simulates this experiment:
5 TI$="000000"
10 C=0:T=2+10:F=10
20 FORI=1TOT
30 B=INT(RND(.)*2)
35 A=1: REM ALL MATCH
40 FORJ=2TOF
45 IFINT(RND(.)*2)<>BTHEN A=0:J=F
50 NEXTJ
55 IFA=1THEN PRINT"ALL "F" OF THEM!":PRINT"GOT A MATCH AT"I:C=C+1
60 NEXTI
70 PRINT"TOTAL MATCHES:"C
80 PRINT"TOOK"TI/60"SECONDS"
Breaking Down the Code
Some might say our variable names aren’t exactly self-documenting. But remember, on the Commodore 64, shorter variable names actually make your code run faster! Here’s what each line does:
- Line 5: Reset the system timer so we can measure execution time
- Line 10: Initialize our counter (C=0), total tries (T=12), and flips per try (F=10)
- Line 20: Start our main loop that runs T times
- Line 30: Flip our first coin (0 or 1)
- Line 35: Set our “all match” flag to true (1)
- Line 40: Start inner loop for flips 2 through 10
- Line 45: Flip another coin and check if it matches the first; if not, set our flag to false (0) and break out of the inner loop
- Line 50: End of inner loop
- Line 55: If all flips matched, print a success message and increment our counter
- Line 60: End of main loop
- Line 70: Print final count of successful matches
- Line 80: Print execution time in seconds
Performance Tweaks
This program has several optimizations typical of Commodore BASIC:
- Single-letter variables: Using A instead of ALLMATCH saves memory and speeds execution
- Multiple statements per line: Using colons to combine statements reduces program size
- Early loop termination: Setting J=F to break out of the inner loop as soon as we find a mismatch
- Minimal spaces: Removing unnecessary spaces like in
BTHENinstead ofB THENspeeds tokenization
The Results
When running this program, I got 3 matches out of 12 tries, which took about 38.4 seconds to complete. That’s a success rate of 25% – significantly higher than the expected 0.2%!
Either I got extremely lucky, or RND(.) isn’t as random as we might hope. Of course, with only 12 tries, we’re dealing with a very small sample size. To get more accurate results, we could increase T to a much larger number, but then we’d be sitting there watching our C64 crunch numbers for quite a while.
Python Version
For those who prefer a more modern approach, here’s the same simulation implemented in Python:
import random
import time
def coin_flip_experiment(total_tries=12, flips_per_try=10):
start_time = time.time()
matches = 0
for i in range(1, total_tries + 1):
# First flip
first_flip = random.randint(0, 1)
all_match = True
# Remaining flips
for j in range(2, flips_per_try + 1):
if random.randint(0, 1) != first_flip:
all_match = False
break
if all_match:
print(f"ALL {flips_per_try} OF THEM!")
print(f"GOT A MATCH AT {i}")
matches += 1
elapsed_time = time.time() - start_time
print(f"TOTAL MATCHES: {matches}")
print(f"TOOK {elapsed_time:.6f} SECONDS")
return matches, elapsed_time
# Run the experiment
coin_flip_experiment(12, 10)
This Python version will run significantly faster than the Commodore BASIC implementation, but it captures the same logic. Unlike the BASIC version, Python doesn’t benefit from single-letter variables or removing spaces, but it does benefit from the early termination of the inner loop using break.
Extra Credit: Speeding Things Up
If you’re looking to optimize this program further, here are some approaches that would work on both the BASIC and Python versions:
1. Skip Unnecessary Comparisons
Instead of generating a first flip and then comparing each subsequent flip to it, we could just decide in advance which value we’re looking for (all 0s or all 1s). This saves one comparison per iteration:
20 FORI=1TOT
25 V=0: REM VALUE WE'RE LOOKING FOR (0 OR 1)
30 A=1: REM ALL MATCH
40 FORJ=1TOF
45 IFINT(RND(.)*2)<>VTHEN A=0:J=F
50 NEXTJ
2. Mathematical Shortcut
Since we’re just checking if all flips match, we could take advantage of a mathematical property. Instead of flipping 10 coins and checking if they’re all the same, we could:
- Decide whether we’re looking for all heads or all tails (50% chance of each)
- For each flip, check if it matches our target
- If any flip doesn’t match, we don’t have all matching flips
This approach is more elegant and potentially faster in some languages.
3. Parallel Processing
For a modern approach, you could run multiple trials simultaneously using parallel processing. In Python, this would use the multiprocessing module to spread the workload across CPU cores.
4. Pre-calculate Random Numbers
On systems with slow random number generators (like the Commodore 64), pre-calculating a batch of random numbers can be faster than generating them one at a time:
5 TI$="000000"
10 C=0:T=2+10:F=10:DIM R(T*F)
15 FOR I=1 TO T*F:R(I)=INT(RND(.)*2):NEXT
20 FORI=1TOT
30 B=R((I-1)*F+1)
35 A=1
40 FORJ=2TOF
45 IFR((I-1)*F+J)<>BTHEN A=0:J=F
50 NEXTJ
55 IFA=1THEN PRINT"ALL "F" OF THEM!":PRINT"GOT A MATCH AT"I:C=C+1
60 NEXTI
70 PRINT"TOTAL MATCHES:"C
80 PRINT"TOOK"TI/60"SECONDS"
This approach trades memory for speed—a classic computing tradeoff. On the Commodore 64, with its limited memory, you’d need to be careful about how large you make your arrays.
Going Further
Want to experiment with this yourself? Here are some ideas:
- Increase T to 1000 for more statistically significant results
- Change F to see how probability changes with different numbers of flips
- Replace INT(RND(.)*2) with a different random number generator
- Track and display the longest streak of matching flips
Happy coding, and may the probability gods smile upon your BASIC experiments!
https://claude.ai/chat/5d6923ca-5387-4054-94f7-97104aba0710