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

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


(笔记模板由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.rhkb.cn/news/380158.html

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

相关文章

ranger审计日志对接CDH solr

作者&#xff1a;耀灵 一、准备条件 1、已安装完毕ranger-admin 2、已在CDH上部署solr&#xff08;注意在安装solr时更改下solr在zk上的节点信息&#xff09; 二、更改相关配置 1、修改ranger-2.1.0-admin/contrib/solr_for_audit_setup/install.properties SOLR_USERsolr …

FreeRTOS的中断管理、临界资源保护、任务调度

什么是中断&#xff1f; 简介&#xff1a;让CPU打断正常运行的程序&#xff0c;转而去处理紧急的事件&#xff08;程序&#xff09;&#xff0c;就叫中断。 中断优先级分组设置 ARM Cortex-M 使用了 8 位宽的寄存器来配置中断的优先等级&#xff0c;这个寄存器就是中断优先级…

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

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

基于 CNN(二维卷积Conv2D)+LSTM 实现股票多变量时间序列预测(PyTorch版)

前言 系列专栏:【深度学习&#xff1a;算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域&#xff0c;讨论了各种复杂的深度神经网络思想&#xff0c;如卷积神经网络、循环神经网络、生成对…

Element UI DatePicker选择日期范围区间默认显示前一个月和本月

要求&#xff1a;点击el-date-picker选择时间范围时&#xff0c;默认展开当月和上个月。 但是Element UI的组件默认展开的是本月和下一个月&#xff0c;如下图所示&#xff1a; 改为 <span click"changeInitCalendarRange"><el-date-picker v-model"r…

8月长沙学术会议:经济金融发展国际会议(ICEFD 2024)

经济金融发展国际会议&#xff08;ICEFD 2024&#xff09;将于2024年8月20-22日在长沙盛大举行。本次会议汇聚了国内外众多高校、科研机构、企业界及其他相关领域的专家学者&#xff0c;共同探讨经济金融发展的新趋势、新挑战和新机遇。 长沙&#xff0c;这座历史悠久的文化名…

Android车载MCU控制音量和ARM控制音量的区别和优缺点—TEF6686 FM/AM芯片

不要嫌前进的慢&#xff0c;只要一直在前进就好 文章目录 前言一、系统架构图1.MCU控制音量的架构图&#xff08;老方法&#xff09;2.ARM控制音量的架构图&#xff08;新方法&#xff09; 二、为啥控制音量不是用AudioManager而是执着去直接控制TDA7729&#xff1f;三、MCU控制…

wls2下的centos使用桥接模式连接宿主机网络独立静态ip

前提&#xff1a;wsl2已安装&#xff0c;可正常更新 1.在控制面板中&#xff0c;打开开启或关闭windows功能&#xff0c;将里面的 Hyper-V功能打开&#xff0c;此处涉及重启 2. 按一下win键&#xff0c;输入hy&#xff0c;上面可以看到Hyper-V Manager,点进去 3.选择右边的 Vi…

【C语言初阶】C语言数组基础:从定义到遍历的全面指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C语言 “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C语言函数 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀数组 &#x1f4d2;1. 什么是数组…

npm install报错:npm error ERESOLVE could not resolve

从git上拉取一个新vue项目下来&#xff0c;在npm install时报错&#xff1a;npm error ERESOLVE could not resolve 有网友分析原因是因为依赖冲突导致报错&#xff0c;解决方法如下&#xff1a; # --legacy-peer-deps&#xff1a;安装时忽略所有peerDependencies&#xff0c…

大鲸鱼docker-compose单机容器集群编排工具

目录 一、Docker-compose 概述 二、Docker-compose简介 三、YML文件格式及编写注意事项 1.yml文件是什么 2.yml问价使用注意事项 3.yml文件的基本数据结构 四、Docker-compose 配置 1.Docker-Compose 配置常用字段 2.Docker Compose常用命令 3.使用Docker-compose创建…

win10删除鼠标右键选项

鼠标右键菜单时&#xff0c;发现里面的选项特别多&#xff0c;找一下属性&#xff0c;半天找不到。删除一些不常用的选项&#xff0c;让右键菜单变得干净整洁。 1、按下键盘上的“winR”组合按键&#xff0c;调出“运行”对话框&#xff0c;输入“regedit”命令&#xff0c;点击…

创建鸿蒙手机模拟器(HarmonyOS Emulator)

文 | Promise Sun 一.前提条件&#xff1a; 鸿蒙项目开发需要使用模拟器进行开发测试&#xff0c;但目前想在DevEco Studio开发工具中使用模拟器就必须到华为官网进行报名申请&#xff0c;参加“鸿蒙模拟器&#xff08;HarmonyOS Emulator&#xff09;Beta活动申请”。 申请审…

gitlab新建仓库

总贴 每个git网站都有不同的创建项目的方式&#xff0c;现在举例gitlab&#xff0c;其他例如gitee&#xff0c;gitcode&#xff0c;都是差不多的&#xff0c;自行百度 1![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dae875d9048940c0aeb292c07d6a4a62.png)1和2是项…

C# 之工控机数据类型 高低位(大小端)、BitConverter、IsLittleEndian、字节数组转换(高低位)

八种基本数据类型&#xff1a;byte、short、int、long、float、double、boolean、char byte 8位、有符号的以二进制补码表示的整数 min : -128&#xff08;-2^7&#xff09; max: 127(2^7-1) default: 0 对应包装类&#xff1a;Byte short 16位、有符号的以二进制补码表示…

spark shell

1.进行shell命令行 spark-shell 2.创建RDD 2.1 读取文件创建RDD 2.1.1读取linux文件系统的文件创建RDD --需要保证每一个worker中都有该文件 val data1 sc.textFile("file:/opt/file/word.txt") 2.1.2读取hdfs文件系统上的文件创建RDD val data2sc.textFile("…

正点原子STM32(基于HAL库)6

目录 TFTLCD&#xff08;MCU 屏&#xff09;实验TFTLCD 简介TFTLCD 简介液晶显示控制器FSMC 简介FSMC 关联寄存器简介 硬件设计程序设计FSMC 和SRAM 的HAL 库驱动程序流程图程序解析 下载验证 LTDC LCD&#xff08;RGB 屏&#xff09;实验RGBLCD<DC 简介RGBLCD 简介LTDC 简介…

Proxyman for Mac v5.6.1 抓包调试工具

Mac分享吧 文章目录 效果一、下载软件二、功能三、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 四、运行测试1、打开软件 安装完成&#xff01;&#xff01;&#xff01; 效果 一…

虚拟机及其Debian(kali)安装

本机电脑为Windows10系统专业版&#xff0c;在此基础上安装VMware和系统&#xff08;Kali&#xff09; 步骤如下 一、安装 VMware Workstation Pro v16.2.4 安装步骤可参照网上博客&#xff0c;该步骤较简单&#xff0c;此处不做讲解。文件中共计两个&#xff0c;其中一个是激活…

.NET C# 配置 Options

.NET C# 配置 Options 使用 options 模式可以带来许多好处&#xff0c;包括清晰的配置管理、类型安全、易于测试和灵活性。但在使用过程中&#xff0c;也需要注意配置复杂性、性能开销和依赖框架等问题。通过合理设计和使用&#xff0c;可以充分发挥 options 模式的优势&#…