<jupyter_start><jupyter_text>Solving the birthday problem using simple counting> Counting trials instead of using a PnC formula- toc:true- badges: true- comments: true- author: <NAME>- categories: [python, data-analysis] This problem and discussion herein was adapted from Computational Thinking course offered by MIT. [Link to course page](https://computationalthinking.mit.edu/Fall20/)**Question from First Course in Probability (Sheldon Ross):**Given 20 people, what is the probability that, among the 12 months in the year, there are 4 months containing exactly 2 birthdays and 4 containing exactly 3 birthdays? [Link to problem](https://www.slader.com/discussion/question/given-20-people-what-is-the-probability-that/)<jupyter_code>import os 
import time
import numpy as np 

np.random.seed(42)<jupyter_output><empty_output><jupyter_text>Problem setupRather than using a formula to estimate the probability we can setup an experiment using for-loops to mimic the trials and then count the actual number of cases where the condition is met.<jupyter_code>month_number = np.arange(1, 13) # array of months 
number_of_people = 20 #Number of people considered
sample = np.random.choice(month_number, size=number_of_people, replace=True, )<jupyter_output><empty_output><jupyter_text>Here `sample` is the randomized set of 20 birthdays. Each entry is a month. Length of sample is same as number of people in the experiment, 20 in this case. **Highlight cases which satisfy the constraints:**1. 4 months have exactly 2 birthdays 2. 4 months have exactly 3 birthdaysFor that first create an array with frequency of birthdays in each month:<jupyter_code>frequency = np.zeros(len(month_number)) #This is zeros frequency array
for _entry in sample:
    _index = _entry - 1
    frequency[_index] = frequency[_index] + 1
sample
frequency<jupyter_output><empty_output><jupyter_text>`frequency` array shows how many birthday are there in a month (Jan - Dec). Once we have that, now we have to consider the conditions given in the problem. We can either do this using `for/if` statement or using `numpy.argwhere`, where the number of months satisfying the conditions is estimated directly.<jupyter_code>len(np.argwhere(frequency == 2))<jupyter_output><empty_output><jupyter_text>Now putting it all together and doing multiple trials. In this case, I do 1,500,000 trials and count the ratio of successes to the total trials. While this process takes time, it is better than remembering permutation and combination formulae ;P This is a variant of monte-carlo simulation.<jupyter_code>%%time
# putting it all togther 
_number_of_trials = 1_500_000
_success = 0 
  
for _ in range(_number_of_trials):

    sample = np.random.choice(month_number, size=number_of_people, replace=True)
    
    frequency = np.zeros( len(month_number) )
    for _entry in sample:
        if _entry in month_number:
            _index = _entry - 1
            frequency[_index] = frequency[_index] + 1
    
    if ( len(np.argwhere(frequency == 2)) == 4 ) and ( len(np.argwhere(frequency == 3)) == 4 ): 
        _success = _success + 1

print('Probability of success = {}'.format(_success / _number_of_trials))<jupyter_output>Probability of success = 0.00106
CPU times: user 3min 8s, sys: 2.08 s, total: 3min 10s
Wall time: 3min 10s
{'fraction_non_alphanumeric': 0.08009708737864078, 'fraction_numerical': 0.02154126213592233, 'mean_word_length': 4.856127886323268, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 22, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '559448', 'license': 'Apache-2.0', 'n_tokens_mistral': 1014, 'n_tokens_neox': 928, 'n_words': 379, 'path': '_notebooks/2021-01-10-PnCbirthday.ipynb', 'repo_name': 'pgg1610/blog_fastpages', 'size': '3309'}