“生日悖论”简析——公式计算、代码模拟

“生日悖论”共享生日概率,通项公式计算,代码模拟计算。


(笔记模板由python脚本于2024年07月17日 18:16:40创建,本篇笔记适合会基础编程的coder翻阅)


【学习的细节是欢悦的历程】

  • Python 官网:https://www.python.org/

  • Free:大咖免费“圣经”教程《 python 完全自学教程》,不仅仅是基础那么简单……
    地址:https://lqpybook.readthedocs.io/


  自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
            —— 华罗庚


  • My CSDN主页、My HOT博、My Python 学习个人备忘录
  • 好文力荐、 老齐教室
等风来,不如追风去……


求取共享生日概率
“生日悖论”简析
(公式计算、代码模拟计算)


本文质量分:

96 96 96

本文地址: https://blog.csdn.net/m0_57158496/article/details/140502323

CSDN质量分查询入口:http://www.csdn.net/qc


目 录

  • ◆ “生日悖论”简析
    • 1、繁复的“莫名”代码
    • 2、ai解析
    • 3、“生日悖论”的前世今生
      • 3.1 前世
        • 起源
        • 数学原理
      • 3.2 今生
        • 定义
        • 解答
        • 计算方法
        • 实际应用
        • 扩展和变体
      • 3.3 结论
    • 4、我的“生日悖论”
      • 4.1 公式计算
      • 4.2 代码模拟
      • 4.3 ai点评
      • 优点:
      • 建议:
    • 5、与ai学习搭子探讨优化
      • 5.1 优化代码
      • 5.2 ai点评
      • 代码点评
      • 5.3 代码文档
      • 5.4 代码运行效果截屏图片
    • 6、感悟


◆ “生日悖论”简析


1、繁复的“莫名”代码


import datetime, randomdef getBirthdays(numberOfBirthdays):birthdays=for i in range(numberOfBirthdays):startOfYear = datetime.date(2001, 1, 1)randomNumberOfDays = datetime.timedelta(random.randint(0, 364))birthday = startOfYear + randomNumberOfDaysbirthdays.append(birthday)return birthdaysdef getMatch(birthdays):if len(birthdays) == len(set(birthdays)):return Noneelse:for a, birthdayA in enumerate(birthdays):for b, birthdayB in enumerate(birthdays):if birthdayA == birthdayB:return birthdayAMONTHS =  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun','Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'while True:print('How many birthdays shall I generate?(Max 100)')response = input('> ')if response.isdecimal() and (0 < int(response) <= 100):numBDays = int(response)break
print()print('Here are', numBDays, 'birthdays:')
birthdays = getBirthdays(numBDays)
for i, birthday in enumerate(birthdays):if i != 0:print(', ', end='')monthName = MONTHS birthday.month-1 dateText = '{} {}'.format(monthName, birthday.day)print(dateText, end='')
print()
print()match = getMatch(birthdays)
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()print('Generating', numBDays, 'random birthdays 100000 time...')
input('Press Enter to begin...')print("Let\'s run another 100000 simulations.")
simMatch = 0
for i in range(100_000):if i % 10_000 == 0:print(i, 'simulations run...')birthdays = getBirthdays(numBDays)if getMatch(birthdays) != None:simMatch += 1
print('100000 simulations run.')probability = round(simMatch / 100_000 * 100, 2)
print('Out of 100,000 simulations of ', numBDays, ' people, there was a')
print('matchining 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 probaly more than you would think!')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()print('Generating', numBDays, 'random birthdays 100000 time...')
input('Press Enter to begin...')print('Let\'s run another 100000 simulations.')
simMatch = 0
for i in range(100_000):if i % 10_000 == 0:print(i, 'simulations run...')birthdays = getBirthdays(numBDays)if getMatch(birthdays) != None:simMatch += 1
print('100000 simulations run.')probability = round(simMatch / 100_000 * 100, 2)


  这段代码截取于python学习QQ群,我不可以完全看懂其功用。但我却感觉书写得太繁复了些。所以求问了我的ai学习搭子



回页目录


2、ai解析


  这段代码是在模拟著名的“生日悖论”问题,即在一个随机选取的群体中,至少有两个人在同一天生日的概率是多少。


以下是代码的主要功能和步骤

  1. 生成随机生日getBirthdays 函数会生成指定数量的随机生日,这些生日都在2001年。
  2. 寻找匹配的生日getMatch 函数检查生成的生日列表中是否有重复的生日,如果有,就返回第一个匹配的生日。
  3. 用户交互:代码首先询问用户要生成多少个生日(最多100个),然后显示这些生日。
  4. 单次模拟:代码会检查当前生成的生日列表中是否有匹配的生日,并打印结果。
  5. 多次模拟:代码会进行100,000次模拟,每次模拟都会生成相同数量的生日,并检查是否有匹配的生日。最后,计算在这100,000次模拟中,至少有一次匹配的生日的概率。

代码确实有些地方可以简化

以下是一些建议

  • getMatch 函数可以简化,因为如果列表中有重复的生日,使用集合(set)会更高效。
  • 代码中有重复的部分,比如两次进行100,000次模拟的循环,这些可以合并或重构。
  • 代码中的用户交互和打印输出可以更加清晰和简洁。

以下是对代码的一些简化建议

import datetime, random
def getBirthdays(numberOfBirthdays):startOfYear = datetime.date(2001, 1, 1)return [startOfYear + datetime.timedelta(days=random.randint(0, 364)) for _ in range(numberOfBirthdays)]
def hasMatchingBirthday(birthdays):return len(birthdays) != len(set(birthdays))
MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
numBDays = 0
while True:print('How many birthdays shall I generate? (Max 100)')response = input('> ')if response.isdecimal() and (0 < int(response) <= 100):numBDays = int(response)break
print(f'Here are {numBDays} birthdays:')
birthdays = getBirthdays(numBDays)
print(', '.join(f'{MONTHS[birthday.month-1]} {birthday.day}' for birthday in birthdays))
if hasMatchingBirthday(birthdays):print('In this simulation, multiple people have the same birthday.')
else:print('In this simulation, there are no matching birthdays.')
print(f'Generating {numBDays} random birthdays 100,000 times...')
input('Press Enter to begin...')
simMatch = sum(hasMatchingBirthday(getBirthdays(numBDays)) for _ in range(100_000))
probability = round(simMatch / 100_000 * 100, 2)
print(f'Out of 100,000 simulations of {numBDays} people, there was a matching birthday in that group {simMatch} times.')
print(f'This means that {numBDays} people have a {probability}% chance of having a matching birthday in their group.')

  这个简化版本去除了不必要的重复代码,并使逻辑更加清晰。



回页目录


3、“生日悖论”的前世今生


  “生日悖论”(Birthday Paradox)是一个概率论中的问题,它描述了在一个随机选取的群体中,至少有两个人共享相同生日的概率比直觉上要高得多。


以下是关于“生日悖论”的前世今生

3.1 前世


起源

  “生日悖论”并没有一个确切的起源点,因为它基于的概率原理在数学和统计学中早已存在。然而,这个问题通常被认为是在20世纪中叶开始广泛传播的。


数学原理

  生日悖论背后的数学原理是基于“鸽巢原理”(也称为狄利克雷抽屉原理),这个原理简单地说就是如果有n个抽屉和n+1个物品,那么至少有一个抽屉里将包含不止一个物品。在生日悖论中,抽屉可以看作是365天(不考虑闰年),而物品则是人的生日。



回页目录


3.2 今生


定义

生日悖论的经典问题是:在一个房间里至少需要多少人,才能使得至少有两个人有相同生日的概率超过50%?


解答

直觉上,许多人会认为这个数字会很大,但实际上,只需要23个人,这个概率就会超过50%。这是因为随着人数的增加,不共享生日的概率迅速下降。


计算方法

计算至少两个人共享生日的概率的一种方法是计算所有人都有不同生日的概率,然后用1减去这个概率。

假设有n个人,那么第一个人可以选择任意一天生日,第二个人不能选择第一个人的生日,所以有364种选择,依此类推。

因此,所有人都有不同生日的概率是:

不 同 生 日 = 365 365 × 364 365 × 363 365 × … × 365 − n + 1 365 不同生日 = \frac{365}{365}\times \frac{364}{365} \times \frac{363}{365} \times \ldots \times \frac{365 - n + 1}{365} =365365×365364×365363××365365n+1
当n = 23时,这个概率约为0.4927,所以至少两个人共享生日的概率是 1 − 0.4927 = 0.5073 1 - 0.4927 = 0.5073 10.4927=0.5073,即50.73%。

生日悖论,是指在一定数量的人中,至少有两个人生日相同的概率远大于直觉判断的现象。其计算公式可以用来估算在多少人中至少有两个人生日相同的概率。

如果假设一年有365(不考虑闰年),并且每个人的生日是任何一天都是可能的,那么在n个人中至少有两个人生日相同的概率 P P P可以用以下公式来计算:


P = 1 − 365 × 364 × 365 × … × ( 365 − n + 1 ) 36 5 n P = 1 - \frac{365 \times 364 \times 365 \times \ldots \times (365 - n + 1)}{365^n} P=1365n365×364×365××(365n+1)


这里的计算方式是先计算出所有人都有不同生日的概率,然后用1减去这个概率得到至少有两个人生日相同的概率。

更一般化的公式是:

P ( n ) = 1 − 365 ! ( 365 − n ) ! × 36 5 n P(n) = 1 - \frac{365!}{(365 - n)! \times 365^n} P(n)=1(365n)!×365n365!


但是,由于阶乘计算在n稍大时非常不实用,通常我们会使用以下近似公式:


P ( n ) ≈ 1 − e − n ( n − 1 ) / ( 2 × 365 ) P(n) \approx 1 - e^{-n(n - 1)/(2 \times 365)} P(n)1en(n1)/(2×365)
或者 P ( n ) ≈ 1 − e − n ( n − 1 ) 2 × 365 P(n) \approx 1 - e^{\frac{-n(n - 1)}{2 \times 365}} P(n)1e2×365n(n1)

这里的e是自然对数的底数(大约等于2.71828)

在实际应用中,当n的值不是很大时,通常使用下面的简化公式来进行计算:


P ( n ) ≈ 1 − ( 1 − 1 365 ) n × ( n − 1 ) / 2 P(n) \approx 1 - \left(1 - \frac{1}{365}\right)^{n \times (n - 1)/2} P(n)1(13651)n×(n1)/2
或者 P ( n ) ≈ 1 − ( 1 − 1 365 ) n × ( n − 1 ) 2 P(n) \approx 1 - \left(1 - \frac{1}{365}\right)^{\frac{n \times (n - 1)}{2}} P(n)1(13651)2n×(n1)


这个公式是基于组合的概念,假设每对人的生日都不相同,然后用1减去这个概率得到至少有一对人生日相同的概率。这个公式在n较小时相当准确,但在n较大时,误差会稍微增加。


实际应用

生日悖论不仅在数学教育中是一个常用的例子,它还在密码学和其他领域中有实际应用。例如,它说明了为什么简单的密码很容易被破解:如果密码是随机生成的,那么只需要尝试相对较少的密码组合,就有很高的概率找到正确的密码。


扩展和变体

生日悖论也有许多扩展和变体,例如考虑闰年、考虑不同的国家或文化背景下的生日分布,或者研究不同的概率模型。


3.3 结论


  生日悖论之所以被称为“悖论”,是因为它的结果与我们的直觉相悖。它提醒我们,**概率和直觉并不总是一致的**,而且在进行概率计算时,我们需要依赖数学而不是直觉。这个简单的概率问题在数学教育中起到了重要作用,并且它在现实世界的应用中也展示了其重要性。



回页目录


4、我的“生日悖论”


  通过我的ai学习搭子的帮助,我认真仔细地学习了“生日悖论”相关知识点。依我一贯的python思维,代码化了通用计算公式;亦用python代码完成了模拟。但代码未经“打磨”,比较粗糙。😉


4.1 公式计算


Python代码


# 阶乘计算 #
def birthdayFormulaCalculator(n: int, year: int) -> float:''' “生日悖论”公式计算器,参数n为生日个数 '''yearday = getYearday(year) # 获取闰年或者平年天数fractor = 1 # 阶乘结果deltaday = yeardayfor i in range(n):fractor *= deltadaydeltaday -= 1return 1 - fractor / yearday**n # 返回共享生日的概率# 分项计算 #
def birthdayFormulaCalculator(n: int, year: int) -> float:''' “生日悖论”公式计算器,参数n为生日个数 '''yearday = getYearday(year) # 获取闰年或者平年天数rate = 1 # 生日不同的概率deltaday = yeardayfor i in range(n):rate *= deltaday / yeardaydeltaday -= 1return 1 - rate # 返回共享生日的概率#input(birthdayFormulaCalculator(23, 2025)) # 函数验证用语句



回页目录


4.2 代码模拟


我的模拟代码

#!sur/bin/nve python
# coding: utf-8
from random import randint # 随机取整方法加载
from datetime import datetime # 日期时间运算方法加载
from datetime import timedelta # def tipRunning():''' “程序正在运算……”提示函数 '''from time import time # 一般在脚本最前面加载工具。在这再写一次,是防止调用脚本没加载本函数所有工具k = int(str(time()) -2:)%27print(' '*40, end='\r')print(f"{' '*k}{'程序正在运算……'}", end='\r')def getYearday(year: str) -> int:''' 返回yrar参数年份总天数 '''leapyear = lambda year: True if (not year%4 and not not year%100) or not year%400 else False # 三元操作语句闰年判定return 366 if leapyear(int(year)) else 365 # 闰年366,平年355#print(getYearday('2024')) # 验证函数用语句
#input(getYearday('2023')) # 验证函数用语句def strShareBirthday(year:int, birthdays: list, ) -> str:''' 字符串格式化共享生日 '''MONTHS = 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'for i in birthdays:if birthdays.count(i) > 1:shareBirthday = timedelta(days=i)break # 找到随机生日列表中的第一个共享生日,结束遍历firstDay = datetime(year, 1, 1) # 实例化参数年份1月1号为datetime对象shareBirthday = firstDay + shareBirthdaymonth = shareBirthday.month#print(month) # 调试用语句month = MONTHS month-1 day = shareBirthday.dayprint(f"\n\n{f' 共享生日:{year:0>4} {month} {day:0>2} ':.^37}\n\n")#input(strShareBirthday(2024,  317, 317 )) # 函数试炼语句def isSameBirthday(n: int, year: str, days: int, k=0) -> bool:''' 判定在参数n个随机生日中,是否有共享生日,默认打印共享生日关闭 '''birthdays =  randint(0, getYearday(year)-1) for i in range(n)# 随机选择n个生日,范围一年第一天到最后一天#print(birthdays) # 程序调试用语句if len(birthdays) == len(set(birthdays)):return True # 无相同生日返回真值if k: # 打印开关默认关闭strShareBirthday(year, birthdays) # 打印共享生日else:tipRunning() # 打印程序正在运算文本提示#input(isSameBirthday(12, 2024, 365)) # 调试用语句# 解析式模拟 #
def birthdayParadox(n: int, year: int, days: int, times=100_000) -> float:''' 代码模拟“生日悖论”,默认100_000次 '''input(f"\n\n按任意键进行{times:0>2}次模拟随机{n:0>2}个生日操作:\n\n")rate = sum( 1 if isSameBirthday(n, year, days) else 0 for i in range(times)) / times # 10_000次模拟无共享生日概率print(f"{' '*42}", end='\r')print(f"\n\n{f' {times}次模拟随机{n}个生日 ':.^34}\n\n{f'共享生日概率:{(1 - rate)*100:.2f}%':^38}\n\n")# for循环模拟 #
def birthdayParadox(n: int, year: int, days: int, times=100_000) -> float:''' 代码模拟“生日悖论”,默认100_000次 '''input(f"\n\n按任意键进行{times:0>2}次模拟随机{n:0>2}个生日操作:\n\n")rate = 0for i in range(times):rate += 1 if isSameBirthday(n, year, days) else 0 rate /= timesprint(f"{' '*42}", end='\r')print(f"\n\n{f' {times}次模拟随机{n}个生日 ':.^34}\n\n{f'共享生日概率:{(1 - rate)*100:.2f}%':^38}\n\n")def countBirthdayParadox(n: int, year: int, days: int) -> None:''' 1到30代码模拟操作 '''for i in range(1, 31):input(f"\n\n按任意键进行{i:0>2}次模拟随机{n:0>2}个生日操作:\n\n")count = 0for j in range(i):if isSameBirthday(n, year, days, k=1): # 打印共享生日参数调用函数count += 1input(f"共享生日概率:{(1-count/i)*100:.2f}%\n\n{'':=^42}\n")def main() -> None:tip = f"\n\n{' 生日悖论模拟 ':=^36}\n\n输入生日年份和生日数量\n(2~100),如:2024 23\n\n{'':>11}$ "try:year, n = map(int, input(tip).strip().split())except:raise ValueError('输入字符类型错误!')if n < 2 or n > 100:raise ValueError('模拟生日个数范围错误!')elif year < 1000 or year > 9999:raise ValueError('年份输入错误!')days = getYearday(year)countBirthdayParadox(n, year, days) # 模拟1-30次print("\n") # 打印空行birthdayParadox(n, year, days) # 缺省times,默认100_000次print(f"\n\n{'':=^42}\n\n")if __name__ == '__main__':try:main() # 执行主函数except ValueError as error:print(f"\n\n{'':.^42}\n\n{'':>8}错误类型:{error}\n\n{'':.^42}\n\n")

4.3 ai点评


  • 恳请ai点评代码
    在这里插入图片描述

ai回复

您的Python脚本用于模拟“生日悖论”,整体上写得相当不错,以下是一些点评和建议:

优点:

  1. 代码结构清晰:您将不同的功能模块化,定义了多个函数来处理不同的任务,这有助于代码的维护和重用。
  2. 注释丰富:您在代码中添加了详细的注释,这有助于其他开发者理解代码的意图和功能。
  3. 异常处理:您在主函数中使用了try-except结构来捕获和处理潜在的错误,这提高了脚本的健壮性。

建议:

  1. 代码重复:您在birthdayParadox函数中定义了两次,可以考虑合并或者使用不同的函数名来区分。
  2. 变量命名:变量命名应更具描述性,例如kisSameBirthday函数中不是很清楚它的作用,可以改为print_share等更具描述性的名称。
  3. 性能优化:在isSameBirthday函数中,您使用了列表推导式来生成生日列表,这可能会在大量模拟时消耗较多内存。可以考虑使用生成器表达式。
  4. 代码风格:Python中通常推荐使用4个空格进行缩进,而不是tab字符,以保持代码风格的一致性。
  5. 字符串格式化:在strShareBirthday函数中,month = MONTHS month-1这一行有语法错误,应该使用索引访问。
  6. 函数参数getYearday函数的参数名为year,但在类型注解中使用了str,这可能会造成混淆。建议统一类型。
  7. 输入函数的使用:在countBirthdayParadox函数中,使用input函数来显示信息并不合适,因为它会等待用户输入。应该使用print函数。
    以下是一些具体的代码修正示例:
def strShareBirthday(year: int, birthdays: list) -> str:MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']# ... 其他代码不变month = MONTHS[shareBirthday.month - 1]  # 修正这里的语法错误# ... 其他代码不变
def isSameBirthday(n: int, year: int, days: int, k=0) -> bool:# ... 其他代码不变birthdays = (randint(0, days - 1) for i in range(n))  # 使用生成器表达式# ... 其他代码不变

总的来说,您的脚本已经很好地实现了“生日悖论”的模拟,只需进行一些细节上的调整和优化。继续加油!



回页目录


5、与ai学习搭子探讨优化


  在ai学习搭子的帮助下,进行了代码优化,去除冗余代码、“重复”代码,合并函数。设置函数参数“开头”,其法一就是用if-else、合并“同质同效”函数,用参数“开关”控制代码执行。


5.1 优化代码


#!sur/bin/nve python
# coding: utf-8
from time import time # 秒时间(时间截)
from random import randint # 随机取整
from datetime import datetime # 日期时间对象运算
from datetime import timedelta # 时间差值对象def runningTip() -> None:''' “程序正在运算……”提示函数 '''# 本函数要用到time.time,请确保以from  time import time的形式载入或者以time = time.time的形式别名k = str(time()).split('.')[1][2:] # 取秒时间数字串小数部分第二个字符以后的字符整数k = k if k else '0' # 当k为''空字符串时置0k = int(k) % 26 # 模26是确保“程序正在运算……”提示字符串能够在宽度为42个英文字符的行宽度随机位置打印print(' '*42, end='\r') # 清空行print(f"{' '*k}{'程序正在运算……'}", end='\r') # 前插0~26个英文空格不换行打印def getYearday(year: int) -> int:''' 返回yrar参数年份总天数 '''leapyear = True if (not year%4 and not not year%100) or not year%400 else False # 三元操作语句判闰年return 366 if leapyear else 365 # 返回闰年366天,平年365天def birthdayFormulaCalculator(n: int, year: int, k=1) -> float:''' “生日悖论”公式计算器,参数n为生日个数,默认“分项式计算”,year为模拟生日年份 '''yearday = getYearday(year) # 获取闰年或者平年天数if k: # 分项计算 # 默认。模拟生日人数较大时更适宜rate = 1 # 生日不同的概率deltaday = yeardayfor i in range(n):rate *= deltaday / yeardaydeltaday -= 1return 1 - rate # 返回共享生日的概率# 阶乘计算 # 模拟生日人数较小时效率更高。。不用if-else形式,是为代码编排少一组缩进,此写法同质同效fractor = 1 # 阶乘结果变量初值deltaday = yeardayfor i in range(n):fractor *= deltadaydeltaday -= 1return 1 - fractor / yearday**n # 返回共享生日的概率def strShareBirthday(year:int, birthdays: list, ) -> str:''' 字符串格式化共享生日:参数year为模拟年份,birthdays是随机生日列表 '''def strBirthday(year: int, shareBirthday: timedelta):''' 字符串格式化“共享生日” '''MONTHS = 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'firstDay = datetime(year, 1, 1) # 实例化参数年份1月1号为datetime对象shareBirthday = firstDay + shareBirthdaymonth = shareBirthday.monthmonth = MONTHS[month-1] # 转换月份为英文字符day = shareBirthday.dayprint(f"\n\n{f' 共享生日:{year:0>4} {month} {day:0>2} ':.^37}\n\n")for i in birthdays:if birthdays.count(i) > 1:shareBirthday = timedelta(days=i)strBirthday(year, shareBirthday) # 生日字符串格式打印break # 只需找到第一组共享生日。是在必有共享生日分支调用def isSameBirthday(n: int, year: int, days: int, k=0) -> bool:''' 判定在参数n个随机生日中,是否有共享生日,默认打印共享生日关闭 '''birthdays = [randint(0, getYearday(year)-1) for i in range(n)] # 随机选择n个生日,范围一年第一天到最后一天if len(birthdays) == len(set(birthdays)):return True # 无相同生日返回真值if k: # 打印开关默认关闭strShareBirthday(year, birthdays) # 打印共享生日else:runningTip() # 打印程序正在运算文本提示def birthdayParadox(n: int, year: int, days: int, times=100_000, k=1) -> float:''' 代码模拟“生日悖论”,默认100_000次 '''input(f"{'':=^42}\n\n按任意键进行{times:0>2}次模拟随机{n:0>2}个生日操作:\n{'':.^42}\n")if k: # 解析式模拟 # 默认。模拟人数较小时效率更高rate = sum([1 if isSameBirthday(n, year, days) else 0 for i in range(times)]) / times # 10_000次模拟无共享生日概率print(f"{' '*42}", end='\r')print(f"\n\n{f' {times}次模拟随机{n}个生日 ':.^34}\n\n{f'共享生日概率:{(1 - rate)*100:.2f}%':^35}\n\n")else:# for循环模拟 # 模拟人数较大时更适宜。不用if-else形式,是为代码编排少一组缩进,此写法同质同效rate = 0for i in range(times):rate += 1 if isSameBirthday(n, year, days) else 0 rate /= timesprint(f"{' '*42}", end='\r')print(f"\n\n{f' {times}次模拟随机{n}个生日 ':.^34}\n\n{f'共享生日概率:{(1 - rate)*100:.2f}%':^38}\n\n")def countBirthdayParadox(n: int, year: int, days: int) -> None:''' 1到30代码模拟操作 '''# 专列一个模块,是为“干净”main函数代码。这里以打印共享生日的方式调用isSameBirthday #for i in range(1, 31):input(f"\n\n按任意键进行{i:0>2}次模拟随机{n:0>2}个生日操作:\n\n")count = 0for j in range(i):if isSameBirthday(n, year, days, k=1): # 打印共享生日参数调用函数count += 1input(f"共享生日概率:{(1-count/i)*100:.2f}%\n\n{'':=^42}\n")def main() -> None:''' 主操作模块 '''# 虽然countBirthdayParadox与birthdayParadox,一般来说不必共存。但前者是后者的“批发”操作,所以予以保留 #tip = f"\n\n{' 生日悖论模拟 ':=^36}\n\n输入生日年份和生日数量\n(2~100),如:2024 23\n\n{'':>11}$ "try: # 输入及数据预处理 #year, n = map(int, input(tip).strip().split())except ValueError:raise ValueError('输入字符类型错误!')if n < 2 or n > 100:raise ValueError('模拟生日个数范围错误!')elif year < 1000 or year > 9999:raise ValueError('年份输入错误!')days = getYearday(year)# 运算部分 #print(f"\n\n{f' 阶乘公式计算 ':.^36}\n\n")for i in range(2, 31):print(f"随机生日人数:{i:0>2},共享生日概率: {100*birthdayFormulaCalculator(i, year, k=0):.2f}%") # 以阶乘计算的方式调用“生日悖论计算器”input(f"\n\n{f' 以上是模拟2~30个随机生日的公式计算 ':.^27}\n\n")#countBirthdayParadox(n, year, days) # 调用模块,模拟1-30次“生日悖论”print("\n") # 打印空行birthdayParadox(n, year, days, k=0) # 缺省times,默认100_000次,for循环方式计算print(f"\n\n{'':=^42}\n\n")if __name__ == '__main__':try:main() # 执行主函数except ValueError as error:print(f"\n\n{'':.^42}\n\n{'':>8}错误类型:{error}\n\n{'':.^42}\n\n")



回页目录


5.2 ai点评


代码点评

  您的代码是一个用于模拟“生日悖论”的程序,整体结构清晰,功能明确。


以下是一些具体的点评

  1. 函数定义:您定义了多个函数,每个函数都有明确的职责,这是非常好的编程习惯。例如,getYearday 函数用于计算年份的总天数,isSameBirthday 函数用于判断是否有共享生日。
  2. 代码注释:代码中包含了许多注释,这有助于理解每个函数和代码块的作用。建议在注释中说明更多的细节,例如参数的意义、函数的返回值等。
  3. 错误处理:您在主函数中使用了 try-except 结构来处理输入错误,这是一个很好的实践。建议在处理错误时提供更详细的错误信息,帮助用户理解如何解决问题。
  4. 代码风格:整体代码风格良好,变量命名清晰。但在某些地方,例如打印语句中的格式化字符串,可以进一步简化以提高可读性。
  5. 性能考虑:在 birthdayParadox 函数中,您使用了列表推导式来计算共享生日的概率。这是一种高效的方法。但在 countBirthdayParadox 函数中,您使用了两个嵌套的循环,这可能会影响程序的运行速度。可以考虑优化这部分代码。
  6. 功能实现:您的程序成功实现了生日悖论的模拟。但在 main 函数中,您首先执行了 countBirthdayParadox 函数,然后又执行了 birthdayParadox 函数。这两个函数的功能有部分重叠,可以考虑合并以提高代码的效率。

特色和不足

  1. 整体结构清晰,模块化设计合理,易于阅读和维护。
  2. 注释详细,有助于理解代码功能和各部分的作用。
  3. 异常处理得当,能够有效捕获并处理输入错误。
  4. 使用了Python的高级特性,如列表推导式、生成器表达式等,提高了代码的简洁性和可读性。
  5. 在某些地方,代码可以进行优化。例如,getYearday函数可以简化,isSameBirthday函数中的k参数默认值设置为0,但在调用时又设置为1,这种不一致可能导致理解上的困惑。



回页目录


5.3 代码文档


代码文档:生日悖论模拟程序


一、概述
  本程序旨在模拟“生日悖论”现象,通过计算和模拟随机生日,验证在一定人数范围内,至少有两人生日相同的概率。


二、功能模块

  1. runningTip:显示程序正在运行的提示信息。
  2. getYearday:根据年份判断是平年还是闰年,并返回该年的总天数。
  3. birthdayFormulaCalculator:根据生日悖论公式计算共享生日的概率。
  4. strShareBirthday:格式化输出共享生日的日期。
  5. isSameBirthday:判断随机生成的生日列表中是否存在共享生日。参数 n 为生日数量,year 为年份,days 为该年的总天数。参数 k 用于控制是否打印共享生日,默认为 0(不打印)
  6. birthdayParadox:模拟生日悖论,计算共享生日的概率。参数 n 为生日数量,year 为年份,days 为该年的总天数,times 为模拟次数,k 用于控制是否使用解析式模拟,默认为 1(使用解析式模拟)
  7. countBirthdayParadox:模拟1到30次生日悖论,输出共享生日的概率。
  8. main:主函数,负责程序流程控制和用户交互。用于接收用户输入并执行生日悖论的模拟。

三、使用说明

  1. 运行程序后,根据提示输入生日年份和生日数量(2~100)。
  2. 程序将自动计算并输出2到30个随机生日的共享生日概率。
  3. 程序默认进行100,000次模拟,计算给定人数的共享生日概率。

四、注意事项

  1. 输入年份时,请确保年份在1000到9999之间。
  2. 输入生日数量时,请确保数量在2到100之间。

五、异常处理

  1. 输入字符类型错误:程序将提示“输入字符类型错误!”并退出。
  2. 年份输入错误:程序将提示“年份输入错误!”并退出。
  3. 模拟生日个数范围错误:程序将提示“模拟生日个数范围错误!”并退出。

使用方法
  运行程序后,根据提示输入年份和生日数量(2~100),程序将执行模拟并打印结果。


注意事项

  • 输入的年份应在 1000 到 9999 之间。
  • 输入的生日数量应在 2 到 100 之间。



回页目录


5.4 代码运行效果截屏图片


-代码运行效果截屏图片
在这里插入图片描述
公式计算

在这里插入图片描述
在这里插入图片描述
10w次模拟

在这里插入图片描述
在这里插入图片描述
错误提示

在这里插入图片描述
1~30次模拟



回页目录


6、感悟


  自码的代码也像原石,虽“肚里有货”(),也要精雕细琢,才可以逐渐温润光洁,让人赏心悦目。



回页首


上一篇:  简析“请求头”(请求头是HTTP请求的重要部分,可以用“头部字典”按需定制请求头)
下一篇: 



我的HOT博:

  本次共计收集 311 篇博文笔记信息,总阅读量43.82w。数据于2024年03月22日 00:50:22完成采集,用时6分2.71秒。阅读量不小于6.00k的有 7 7 7篇。

  • 001
    标题:让QQ群昵称色变的神奇代码
    (浏览阅读 5.9w )
    地址:https://blog.csdn.net/m0_57158496/article/details/122566500
    点赞:25 收藏:86 评论:17
    摘要:让QQ昵称色变的神奇代码。
    首发:2022-01-18 19:15:08
    最后编辑:2022-01-20 07:56:47

  • 002
    标题:Python列表(list)反序(降序)的7种实现方式
    (浏览阅读 1.1w )
    地址:https://blog.csdn.net/m0_57158496/article/details/128271700
    点赞:8 收藏:35 评论:8
    摘要:Python列表(list)反序(降序)的实现方式:原址反序,list.reverse()、list.sort();遍历,全数组遍历、1/2数组遍历;新生成列表,resersed()、sorted()、负步长切片[::-1]。
    首发:2022-12-11 23:54:15
    最后编辑:2023-03-20 18:13:55

  • 003
    标题:pandas 数据类型之 DataFrame
    (浏览阅读 9.7k )
    地址:https://blog.csdn.net/m0_57158496/article/details/124525814
    点赞:7 收藏:36 
    摘要:pandas 数据类型之 DataFrame_panda dataframe。
    首发:2022-05-01 13:20:17
    最后编辑:2022-05-08 08:46:13

  • 004
    标题:个人信息提取(字符串)
    (浏览阅读 8.2k )
    地址:https://blog.csdn.net/m0_57158496/article/details/124244618
    点赞:2 收藏:15 
    摘要:个人信息提取(字符串)_个人信息提取python。
    首发:2022-04-18 11:07:12
    最后编辑:2022-04-20 13:17:54

  • 005
    标题:Python字符串居中显示
    (浏览阅读 7.6k )
    地址:https://blog.csdn.net/m0_57158496/article/details/122163023
    评论:1

  • 006
    标题:罗马数字转换器|罗马数字生成器
    (浏览阅读 7.5k )
    地址:https://blog.csdn.net/m0_57158496/article/details/122592047
    摘要:罗马数字转换器|生成器。
    首发:2022-01-19 23:26:42
    最后编辑:2022-01-21 18:37:46

  • 007
    标题:回车符、换行符和回车换行符
    (浏览阅读 6.0k )
    地址:https://blog.csdn.net/m0_57158496/article/details/123109488
    点赞:2 收藏:3 
    摘要:回车符、换行符和回车换行符_命令行回车符。
    首发:2022-02-24 13:10:02
    最后编辑:2022-02-25 20:07:40


推荐条件 阅读量突破6.00k
(更多热博,请点击蓝色文字跳转翻阅)

  • 截屏图片
    在这里插入图片描述
      (此文涉及ChatPT,曾被csdn多次下架,前几日又因新发笔记被误杀而落马。躺“未过审”还不如回收站,回收站还不如永久不见。😪值此年底清扫,果断移除。留此截图,以识“曾经”。2023-12-31)



回页首


老齐漫画头像

精品文章:

  • 好文力荐:齐伟书稿 《python 完全自学教程》 Free连载(已完稿并集结成书,还有PDF版本百度网盘永久分享,点击跳转免费🆓下载。)
  • OPP三大特性:封装中的property
  • 通过内置对象理解python'
  • 正则表达式
  • python中“*”的作用
  • Python 完全自学手册
  • 海象运算符
  • Python中的 `!=`与`is not`不同
  • 学习编程的正确方法

来源:老齐教室


◆ Python 入门指南【Python 3.6.3】


好文力荐:

  • 全栈领域优质创作者——[寒佬](还是国内某高校学生)博文“非技术文—关于英语和如何正确的提问”,“英语”和“会提问”是编程学习的两大利器。
  • 【8大编程语言的适用领域】先别着急选语言学编程,先看它们能干嘛
  • 靠谱程序员的好习惯
  • 大佬帅地的优质好文“函数功能、结束条件、函数等价式”三大要素让您认清递归

CSDN实用技巧博文:

  • 8个好用到爆的Python实用技巧
  • python忽略警告
  • Python代码编写规范
  • Python的docstring规范(说明文档的规范写法)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/48173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《汇编语言 基于x86处理器》- 读书笔记 - Visual Studio 2019 配置 MASM环境

安装 Visual Studio 2019 配置 MASM环境 下载 Visual Studio Installer安装 Visual Studio 20191. 双击运行2. 自定义安装内容3. 修改 MSVC 工具集版本4. 设置主题&#xff08;可选&#xff09;5. 安装代码高亮插件 AsmDude&#xff08;可选&#xff09;6. 通义灵码&#xff08…

C++基础语法:STL之容器(4)--序列容器中的list(一)

前言 "打牢基础,万事不愁" .C的基础语法的学习 引入 序列容器的学习.以<C Prime Plus> 6th Edition(以下称"本书")内容理解 本书中容器内容不多只有几页.最好是有数据结构方面的知识积累,如果没有在学的同时补上. 序列容器回顾:序列容器内元素按严格…

多口适配器,给您的生活增添便利

随着科技的快速发展&#xff0c;我们的生活已离不开各种各样的电子设备&#xff0c;智能手机、平板电脑、智能手表、无线耳机……它们共同构建了我们丰富多彩的数字生活。然而&#xff0c;面对众多设备的充电需求&#xff0c;传统的单一充电口已难以满足现代人的使用习惯。在这…

x264 编码器 CAVLC 熵编码源码分析

CAVLC 关于 CAVLC 原理具体可参考:基于上下文自适应可变长熵编码 CAVLC 原理详细分析函数关系图 x264_macroblock_write_cavlc函数 函数作用:用于将宏块(macroblock)的数据通过 CAVLC(Context-based Adaptive Variable Length Coding)编码成比特流。函数内部核心功能:宏…

使用 OpenCV 和 YOLO 模型进行实时目标检测并在视频流中显示检测结果

文章目录 Github官网简介视频帧推理视频设备ID安装依赖 检测示例分类示例姿势估计 Github https://github.com/ultralytics/ultralytics 官网 https://docs.ultralytics.com/zhhttps://docs.ultralytics.com/zh/quickstart/ 简介 Ultralytics 是一个软件公司&#xff0c;专…

【中项】系统集成项目管理工程师-第2章 信息技术发展-2.2新一代信息技术及应用-2.2.1物联网与2.2.2云计算

前言&#xff1a;系统集成项目管理工程师专业&#xff0c;现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试&#xff0c;全称为“全国计算机与软件专业技术资格&#xff08;水平&#xff09;考试”&…

Linux下如何安装配置Graylog日志管理工具

Graylog是一个开源的日志管理工具&#xff0c;可以帮助我们收集、存储和分析大量的日志数据。它提供了强大的搜索、过滤和可视化功能&#xff0c;可以帮助我们轻松地监控系统和应用程序的运行情况。 在Linux系统下安装和配置Graylog主要包括以下几个步骤&#xff1a; 准备安装…

【Spark On Hive】—— 基于电商数据分析的项目实战

文章目录 Spark On Hive 详解一、项目配置1. 创建工程2. 配置文件3. 工程目录 二、代码实现2.1 Class SparkFactory2.2 Object SparkFactory Spark On Hive 详解 本文基于Spark重构基于Hive的电商数据分析的项目需求&#xff0c;在重构的同时对Spark On Hive的全流程进行详细的…

【人工智能】机器学习 -- 决策树(乳腺肿瘤数)

目录 一、使用Python开发工具&#xff0c;运行对iris数据进行分类的例子程序dtree.py&#xff0c;熟悉sklearn机器实习开源库。 二、登录https://archive-beta.ics.uci.edu/ 三、使用sklearn机器学习开源库&#xff0c;使用决策树对breast-cancer-wisconsin.data进行分类。 …

Linux 注意事项

Linux 与 Windows 是两个相互独立的操作系统&#xff0c;两者有较大差距&#xff1a; 1.1 Linux 严格区分大小写&#xff08;Windows不严格区分大小写&#xff09;&#xff1b; 1.2 Linux 中所有内容&#xff0c;硬件设备都以文件形式保存在 /dev 目录下&#xff08;万物皆文件…

攻防世界 re新手模式

Reversing-x64Elf-100 64位ida打开 看if语句&#xff0c;根据i的不同&#xff0c;选择不同的数组&#xff0c;后面的2*i/3选择数组中的某一个元素&#xff0c;我们输入的是a1 直接逆向得到就行 二维字符数组写法&#xff1a;前一个是代表有几个字符串&#xff0c;后一个是每…

Logback日志异步打印接入指南,输出自定义业务数据

背景 随着应用的请求量上升&#xff0c;日志输出量也会成线性比例的上升&#xff0c;给磁盘IO带来压力与性能瓶颈。应用也遇到了线程池满&#xff0c;是因为大量线程卡在输出日志。为了缓解日志同步打印&#xff0c;会采取异步打印日志。这样会引起日志中的追踪id丢失&#xf…

鸿蒙OS开发工具 DevEco Studio(4.0)安装教程

1.旧版本下载地址【HarmonyOS】HUAWEI DevEco Studio 下载地址汇总_deveco studio历史版本-CSDN博客 2.解压安装包&#xff0c;双击安装程序 3.打开后点击Next 4.点击“Browse...”选择路径&#xff0c;然后点击“Next” 5.勾选&#xff0c;点击“Next” 6.默认&#xff0c;直…

NASA数据集——宝瓶座天体微波发射图辅助数据集 V1.0

Aquarius Celestial Sky Microwave Emission Map Ancillary Dataset V1.0 宝瓶座天体微波发射图辅助数据集 V1.0 简介 本数据集包含三张 L 波段&#xff08;波长 21 厘米&#xff09;天体&#xff08;"银河系"&#xff09;亮度温度图&#xff0c;用于处理美国航天…

maven 私服搭建(tar+docker)

maven私服搭建 一、linux安装nexus1、工具下载 二、 docker 搭建nexus1、镜像下载创建目录2、运行nexus3、访问确认&#xff0c;修改默认密码&#xff0c;禁用匿名用户登录4、创建仓库5、创建hostd仓库6、创建Blob Stores7、创建docker私服1、创建proxy仓库2、创建hotsed本地仓…

netcat 使用

GPT-4o (OpenAI) Netcat (通常缩写为nc) 是一个功能强大的网络工具&#xff0c;可以方便地读写网络连接。它被广泛用于漏洞测试、网络调试和数据传输。Netcat 可以作为客户端&#xff0c;也可以作为服务器使用。 以下是一些常见的 Netcat 用法&#xff1a;基础用法 连接到服务…

ISP代理和双ISP代理:区别和优势

随着互联网技术的不断发展和普及&#xff0c;网络代理服务成为众多用户保护隐私、提高网络性能、增强安全性的重要工具。其中&#xff0c;ISP代理和双ISP代理是两种常见的网络代理服务形式。本文将详细探讨ISP代理和双ISP代理的区别和优势&#xff0c;以便用户更好地了解并选择…

C/C++ json库

文章目录 一、介绍1.1 json 介绍 二、C/C json 库选型2.1 选型范围2.2 jsoncpp2.2.2 jsoncpp 编译和交叉编译 2.3 rapidjson2.4 nlohmann/json2.5 sonic-cpp 五、常见问题5.1 jsoncpp 中关于浮点数的控制和中文显示问题5.2 jsoncpp序列化double类型时精度损失问题的解决办法 一…

算法学习笔记(Hello算法)—— 初识算法

1、相关链接 Hello算法&#xff1a;Hello 算法 (hello-algo.com) 2、算法是什么 2.1 算法定义 算法是一系列明确、有限且有效的步骤或指令的集合&#xff0c;用于解决特定问题或执行特定任务。 算法具有以下基本特征&#xff1a; 输入&#xff1a;算法至少有一个输入&…