8. Парадокс дней рождения.

(открыть в новой вкладке)

Условие:

Парадокс дней рождения, также известный как задача о днях рождения, заключается в удивительно высокой вероятности того, что у двух человек совпадает день рождения даже в относительно небольшой группе людей. В группе из 70 человек вероятность совпадения дней рождения у двух людей составляет 99,99%. Но даже в группе всего лишь из 23 человек вероятность совпадения дней рождения составляет 50%. Приведённая программа производит несколько экспериментов, чтобы определить процентные соотношения для групп различного размера.
Пример вывода программы:

Код:

"""Парадокс дней рождения, также известный как задача о днях рождения, заключается в удивительно высокой вероятности того,
что у двух человек совпадает день рождения даже в относительно небольшой группе людей. В группе из 70 человек вероятность 
совпадения дней рождения у двух людей составляет 99,99%. Но даже в группе всего лишь из 23 человек вероятность совпадения
дней рождения составляет 50%. Приведённая программа производит несколько экспериментов, чтобы определить процентные соотношения
для групп различного размера."""

import datetime, random


def get_match(birthdays):                                                                                                                   # возвращает объекты даты дня рождения, встречающегося несколько раз с списке дней рождения
    if len(birthdays) == len(set(birthdays)):                                                                                               # проверяет на отсутствие дубликатов дней рождения
        return None  
    repeat_dict = {}
    for i in birthdays:
        if i not in repeat_dict:
            repeat_dict[i] = 1
        else:
            repeat_dict[i] += 1
    repeat_dict = {a: b for a, b in repeat_dict.items() if b > 1}                                                                           # в этом словаре дата и количество повторов равное 2 и больше
    return repeat_dict

def get_birthdays(number_of_birthdays):                                                                                                     # сгенерируем список дней рождения
    birthdays = []                                                                                                                          # пока этот список пуст
    for i in range(number_of_birthdays):                                                        
        start_of_year = datetime.date(2000, 1, 1)                                                                                           # здесь не важно какой именно год, начнём с высокосного         
        random_number_of_days = datetime.timedelta(random.randint(0, 1460))                                                                 # 365 дней для высокосного (+ 1 день это 366), т.е 365 + 365 * 3 = 1460 дней
        birthday = start_of_year + random_number_of_days                                                                                    # определим очередное число для i-го дня рождения                                                                 
        birthdays.append(str(birthday.month) + "." + str(birthday.day))                                                                     # добавим в список birthdays очередную дату дня рождения, но только без года, для этого обратимся к объекту birthday класса date, конкретнее к его атрибутам month и day (тема ООП)
    return birthdays


def main():                                                                                                                                 # основная функция
    print(f"""
    Birthday Paradox
    """)

    while True:                                                                                                                             # спрашиваем в цикле, пока не будет введён правильный
        print('Сколько дней рождения нужно сгенерировать? Максимум 100.')
        response = input('Ответ: ')
        if response.isdecimal() and (0 < int(response) <= 100):                                                                             # если первая часть условия выполняется, то и преобразование второй в int исполнится 
            num_b_days = int(response)
            break 
    print()

    birthdays = get_birthdays(num_b_days)                                                                                                   # передадим в качестве параметра функции количество дней для генерации списка дней рождения
    for i in birthdays:                                                                                                                     # выведем на печать все дни рождения
        print(i, end=" / ")
    print()

    match = get_match(birthdays)                                                                                                            # найдём дубликаты дней рождения
    
    if match != None:
        for a, b in match.items():
            print(f"Дата: {a: >5}. Количество дней рождений: {b}.")
    else:
        print('Нет совпадающих дней рождений.')
    print()

    print('Генерация', num_b_days, 'дней рождений 100,000 раз...')
    input('Press Enter to begin...')

    sim_match = 0
    for i in range(100_000):        
        if i % 10000 == 0:
            print(i, 'симуляций')
        birthdays = get_birthdays(num_b_days)
        if get_match(birthdays) != None:
            sim_match += 1

    probability = round(sim_match / 100_000 * 100, 2)                                                                                       # процент выпадания дубликатов
    print(f"""
При количестве {num_b_days} человек в группе вероятность совпадения хотя бы двух дней рождений составляет {probability}%.
""")
    

if __name__ == '__main__':
    main()