利用Python破解生日悖論問題
一、前言
別問我為啥題目是英文,因為…高大上(bushi。
刷視頻的時候偶然刷到了一個關(guān)于生日悖論的,當(dāng)場就覺得不可思議,直到上網(wǎng)查了查……
誒,怎么是真的?
這玩意兒居然還被設(shè)置到了密碼攻擊學(xué),就很離譜的。
密碼攻擊我倒是沒那個肝,就…檢驗一下這個悖論的合理性吧。
本文將會對生日悖論進行詳細的講解,寫作不易,支持一下?。ㄈf年不變客套話)
二、生日悖論是什么
可能有很多人不了解這個悖論,讓我們先了解一下。
生日悖論,就是23個人在一個房間,期間必然有兩個人生日相同的概率為50%,30個人的話概率是70%,60個人甚至上升到99%。
這本來不應(yīng)該是悖論,但是讓人感覺十分不合理,違反直覺,故此叫做悖論。
大家肯定會問:一共就23個人,不應(yīng)該是23/365的幾率嗎?怎么可能呢?
其實,是因為…好吧我也不知道,要是一個小學(xué)生都知道這就不叫悖論了。
反正這種想法肯定是錯誤的,百度上說因為是隨機的23個人,大腦不擅長處理非線性函數(shù),其實23個人就有很多種組合。
我們換一種想法。假設(shè)有23個人,那么如果想讓生日不相同,第一個人就可以是365天的任意一個就是365/365,而第二個人就必須是其他364個就是364/365,最后到了第23個人就是343/365。
最后我們把這些概率一乘,1一減,才是真正的概率。
三、公式破解
和上面說的想法一樣,我們可以用公式去破解這個悖論。
公式:
p為生日相同的概率,用1減去就是生日不相同的概率,這個我們是可以求的,如上所述。
思路:用上面的公式代入進去,先求分母相乘的結(jié)果,冪運算就行。
之后求分子相乘結(jié)果,for循環(huán)遞減人數(shù),結(jié)果相乘,結(jié)束。
之后用分子除以分母得到概率,用1一減,結(jié)果保留三位小數(shù),加百分號,初始化變量之后再次檢測。
he=1#初始化變量 he2=1#初始化變量 while 1:#死循環(huán) try:#錯誤捕捉 people=int(input())#獲取人數(shù) except:#如果發(fā)生錯誤 print("請輸入正確")#提示錯誤 else:#如果沒有發(fā)生錯誤 he=365**people#計算下面的所有365相乘,冪運算 jian=365#初始化遞減的變量 for xh in range(people):#for循環(huán)遞減 he2=he2*jian#計算 jian-=1#jian自減,模擬公式分子的遞減 print(round((1-he2/he),3),"%")#輸出保留三位小數(shù)后的結(jié)果 he2=1#初始化變量
這就是公式破解的方法。
注釋已經(jīng)放上去了,自己看。
四、隨機數(shù)破解
這種破解方法就是隨機生成輸入的人數(shù),檢測他們的生日是否相同。
思路:首先創(chuàng)建一個函數(shù),封裝產(chǎn)生隨機數(shù)和檢測是否有重合項的代碼。
之后死循環(huán),獲取人數(shù)信息并且進行多次實驗取平均值,輸出概率。
比較列表的每一項是否相同,還用上for循環(huán),肝了半個小時,突然想到集合可以去重,比較前后長度是否有改變就行了…
import random as r#導(dǎo)入隨機數(shù)模塊且命名r birthday=[]#保存不同生日的列表 shuliang=0#初始化變量 def jiancha():#創(chuàng)建函數(shù)來新建生日和檢查是否重合 global shuliang,birthday#全局兩個變量 for xh in range(people):#循環(huán)人數(shù)次 birthday.append(r.randint(1,365))#添加隨機數(shù)生日 if len(birthday)!=len(set(birthday)):#集合去重后與之前長度不一樣就是有重合項 shuliang+=1#數(shù)量加一 birthday.clear()#清空列表birthday while True:#死循環(huán) try:#異常捕獲 people=int(input())#獲取人數(shù) for xh2 in range(1000):#進行1000次檢測求平均值 jiancha()#調(diào)用函數(shù) print(shuliang/1000)#求平均值 shuliang=0#初始化變量 except:#如果發(fā)生錯誤 print("請輸入正確的數(shù)字")#提示
注釋也放上去了自己看。
五、添加功能:dict儲存及改變方式
接下來,我們添加一個功能:用dict儲存不同人數(shù)時的概率,并且先用字典保存所有再在字典里查找人數(shù)。
我們要用到字典方法:setdefault。再回顧一下詳解2:
用法:返回括號里鍵的值,如果沒有這個鍵就在字典里添加這個鍵,值為None,如果又填了一個值就是要添加的值。
還有g(shù)et,也是輸出對應(yīng)的值,沒有則返回第三個值。
聽不懂?看就完了!
import random as r#導(dǎo)入隨機數(shù)模塊且命名r birthday=[]#保存不同生日的列表 zidian={}#保存人數(shù)和概率的字典 shuliang,find=0,0#初始化變量 def jiancha():#創(chuàng)建函數(shù)來新建生日和檢查是否重合 global shuliang,birthday#全局兩個變量 for xh in range(people):#循環(huán)人數(shù)次 birthday.append(r.randint(1,365))#添加隨機數(shù)生日 if len(birthday)!=len(set(birthday)):#如果有重合項 shuliang+=1#數(shù)量加一 birthday.clear()#清空列表birthday while find<100:#大于等于100才退出循環(huán) try:#異常捕獲 find=int(input("要進行多少次檢測?數(shù)量越高準確度越高但是時間會變長,至少100次否則程序會出錯"))#獲取次數(shù) if find<100:#如果find小于100 raise(ValueError)#拋出異常 except:#如果異常 print("請正確輸入,查看自己輸入格式是否正確及數(shù)值是否大于99")#提示 for people in range(1,366):#每一個人數(shù) for xh2 in range(find):#進行find次檢測求平均值 jiancha()#調(diào)用函數(shù) if people%20==0:#如果可以被20整除 print("等待進度:",round(people/365*find,2),"%")#安慰進度條 zidian.setdefault(people,str(shuliang)+"%")#字典添加人數(shù)和概率 shuliang=0#初始化變量 zidian2=str(zidian)#zidian2為zidian的字符串形式 while True:#死循環(huán) try:#異常捕獲 cha=int(input("請輸入房間有多少個人,輸出全部請輸入114514"))#詢問人數(shù)或者全部輸出 if cha==114514:#如果選擇全部輸出 print(zidian2[1:-1])#截取字符串來去大括號 print("冒號前面是人數(shù),冒號后面是概率")#提示語 else:#如果選擇截取 print(zidian.get(cha,"請輸入1到365位"))#輸出對應(yīng)的value,如果沒有則提示范圍 except:#如果錯誤 print("請輸入正確")#提示
代碼解析還是看注釋。
到此這篇關(guān)于利用Python破解生日悖論問題的文章就介紹到這了,更多相關(guān)Python生日悖論內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章

利用Pycharm + Django搭建一個簡單Python Web項目的步驟

Python如何通過Flask-Mail發(fā)送電子郵件

python獲得linux下所有掛載點(mount points)的方法