1.Overview
The Birthday Paradox, also called the Birthday Problem, is the surpringly high probability that two people will have the same birthday even in a small group of people.
2. The Program in Action
(ll_env) maxwellpan@192 the_big_book_of_small_python_projects % ls -ltr
total 0
drwxr-xr-x 3 maxwellpan staff 96 Apr 22 17:13 1.Bagels
drwxr-xr-x 3 maxwellpan staff 96 Apr 22 20:27 2BirthdayParadox
(ll_env) maxwellpan@192 the_big_book_of_small_python_projects % cd 2BirthdayParadox
(ll_env) maxwellpan@192 2BirthdayParadox % ls -ltr
total 8
-rw-r--r-- 1 maxwellpan staff 3923 Apr 22 21:05 birthdayparadox.py
(ll_env) maxwellpan@192 2BirthdayParadox % python3 birthdayparadox.py
Birthday Paradox, by Al Sweigart al@inventwithpython.com
The Birthday Paradox shows us that in a group of N people, the odds that two of them have matching birthdays is surprisingly large.
This program does a Monte Carlo simulation (that is , repeated random simulations) to explore this concept.
(It's not acutally a paradox, it's just a surprising result.)
How many birthdays shall I generate? (Max 100)
> 23
Here are 23 birthdays:
, Mar31, May31, Jan11, Aug31, Aug18, Jun5, Jun9, Apr16, Apr14, Feb3, Mar23, Jul9, Oct11, Nov21, Dec5, Mar19, Apr29, Jan2, Jul3, May7, Jun22, Dec15
In this simulation, there are no matching birthdays.
Generating 23 random birthdays 100,000 times...
Press Enter to begin...
Let's run another 100,000 simulations.
0 simulations run ...
10000 simulations run ...
20000 simulations run ...
30000 simulations run ...
40000 simulations run ...
50000 simulations run ...
60000 simulations run ...
70000 simulations run ...
80000 simulations run ...
90000 simulations run ...
100,000 simulations run.
Out of 100,000 simulations of 23 people, there was a
matching birthday in that group 50851 times. This means
that 23 people have a 50.85 % chance of
having a matching birthday in their group.
That's probably more than you would think!
(ll_env) maxwellpan@192 2BirthdayParadox %
3. How It Works
"""Birthday Paradox Simulation, by Al Sweigart al@inventwithpython.com
Explore the surprising probabilities of the "Birthday Paradox".
More info at https://en.wikipedia.org/wiki/Birthday_problem
View this code at https://nostarch.com/big-book-small-python-projects
Tags: short, math, simulation"""
import datetime, random
def getBirthdays(numberOfBirthdays):
"""Returns a list of number random date objects for birthdays."""
birthdays = []
for i in range(numberOfBirthdays):
# The year is unimportant for our simulation, as long as all
# birthday have the same year.
startOfYear = datetime.date(2001, 1, 1)
# Get a random day into the year:
randomNumberOfDays = datetime.timedelta(random.randint(0, 364))
birthday = startOfYear + randomNumberOfDays
birthdays.append(birthday)
return birthdays
def getMatch(birthdays):
"""Returns the date object of a birthday that occurs more than once in the birthdays list."""
if len(birthdays) == len(set(birthdays)):
return None # All birthdays are unique, so return None.
# Compare each birthday to every other birthday:
for a, birthdayA in enumerate(birthdays):
for b, birthdayB in enumerate(birthdays[a + 1 :]):
if birthdayA == birthdayB:
return birthdayA # Return the matching birthday.
# Display the intro:
print('''Birthday Paradox, by Al Sweigart al@inventwithpython.com
The Birthday Paradox shows us that in a group of N people, the odds that two of them have matching birthdays is surprisingly large.
This program does a Monte Carlo simulation (that is , repeated random simulations) to explore this concept.
(It's not acutally a paradox, it's just a surprising result.)
''')
# Set up a tuple of month names in order:
MONTHS = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul','Aug','Sep','Oct','Nov','Dec')
while True: # Keep asking until the user enters a valid amount.
print('How many birthdays shall I generate? (Max 100)')
response = input('> ')
if response.isdecimal() and (0 < int(response) <= 100):
numBDays = int(response)
break # User has entered a valid amount.
print()
# Generate and display the birthdays:
print('Here are', numBDays, 'birthdays:')
birthdays = getBirthdays(numBDays)
for i, birthday in enumerate(birthdays):
if i != 0:
# Display a comma for each birthday after the first birthday.
print(', ', end='')
monthName = MONTHS[birthday.month - 1]
dateText = '{}{}'.format(monthName, birthday.day)
print(dateText, end='')
print()
print()
# Determine if there are two birthdays that match.
match = getMatch(birthdays)
# Display the results:
print('In this simulation, ', end='')
if match != None:
monthName = MONTHS[match.month - 1]
dateText = '{}{}'.format(monthName, match.day)
print('multiple people have a birthday on', dateText)
else:
print('there are no matching birthdays.')
print()
# Run through 100,000 simulations:
print('Generating', numBDays, 'random birthdays 100,000 times...')
input('Press Enter to begin...')
print('Let\'s run another 100,000 simulations.')
simMatch = 0 # How many simulations had matching birthdays in them.
for i in range(100_000):
# Report on the progress every 10,000 simulations:
if i % 10_000 == 0:
print(i, 'simulations run ...')
birthdays = getBirthdays(numBDays)
if getMatch(birthdays) != None:
simMatch = simMatch + 1
print('100,000 simulations run.')
# Display simulation results:
probability = round(simMatch / 100_000 * 100, 2)
print('Out of 100,000 simulations of', numBDays, 'people, there was a')
print('matching birthday in that group', simMatch, 'times. This means')
print('that', numBDays,'people have a', probability,'% chance of')
print('having a matching birthday in their group.')
print('That\'s probably more than you would think!')