有目錄,不迷路
- 出題
- 回答
- 資訊熵
- 二進制
- 破案
- 回歸數學
- 最后
出題
好吧,我承認我有些標題黨了,醒醒吧,你哪來的小學妹,作為程式猿的你身邊明明都是大老爺們!!!

言歸正傳,下面分享一道很有意思的面試題:
從前有個國王要舉辦一個宴會,準備了1000瓶酒,有個刺客要刺殺國王,在一瓶酒中下了毒,然后就被抓住了,刺客招供有一瓶酒中有毒,但是刺客自己也不知道在哪瓶酒里下了毒,國王不想終止宴會,就想到一個辦法找死囚過來試酒從而找出毒酒,但是距離晚宴還有2個小時就要開始,而毒發身亡的時間需要1個半小時(意味著只有一次毒發的機會),國王最后想了一個辦法,只用了極少數的死囚數量就試出了毒酒,請問用了多少個死囚?是怎么試的?(假設不管怎么喝每瓶酒都不會喝光)
當然,本題可以說是一個思想實驗,因此忽略掉道德評判的因素,
回答
看到這個題目,大家可能就八仙過海 —— 各顯神通了,
有些人可能就會說了:這個問題還不簡單嗎?本國王財大氣粗,人力資源豐富,立馬召集1000名死囚,一人一瓶喝一口,一個半小時后死的那名死囚喝的那瓶酒就是毒酒,什么?極少數的死囚?都說了本國王財大氣粗,1000名死囚對于本國王來說就是極少數的死囚,
當然,也有人會說既然距離晚宴還有兩個小時,而毒發時間為一個半小時,也就是說我們還有半個小時也就是30分鐘喝酒的時間,那我們來卡時間好了,我們按照保守估計來算:
假設一名死囚需要花30秒喝一瓶酒里面的一口酒,然后再等待1分鐘喝下一瓶酒,也就是說一名死囚喝一瓶酒需要一分半的時間,三十分鐘能喝20瓶酒,因此1000瓶酒需要 1000/20 約等于 50名死囚,這還只是保守估計,比上個憨憨1000的答案不知道少了多少,然后,我們只需要看是哪個死囚死了,再根據他的死亡時間推算即可,簡直完美有木有?
首先,答案是1000的哪怕當了國王也是個憨憨國王,雖然這種方法一定可以試出哪瓶是毒酒,其次,根據死亡時間推斷的肯定是偵探懸疑題材類的小說或者影視作品看多了,根據死亡時間推斷似乎有一定的道理,但是每個人的體質是不一樣的,所以毒發的時間也是不一樣的, 一個半小時的時間只是個大概的時間,所以50的答案也不完美,甚至很可能會出錯,而一旦出錯,就是幾百上千條人命,

那么這個題目該如何解呢?
我們都知道一般的解題教程都是先給出思路以及解題程序,再得出答案,這里我們反其道而行之,先給出答案:
試出1000瓶葡萄酒中的一瓶毒酒所需要的死囚數為:log1000(以2為底),答案應該為9點多,但是人不能論半個,所以向上取整也就是10名,
這個時候可能大家就會有一個疑問了:log1000(以2為底)是9點多是怎么算出來的???
懂對數運算的小伙伴們下面就可以忽略了
這里先給大家簡單介紹一下對數運算,以下是百度百科的官方解釋:

我們看這兩句就好了:
- 對數是對求冪的逆運算,
- 如果a的x次方等于N(a>0,且a≠1),那么數x叫做以a為底N的對數(logarithm),記作x=loga N,其中,a叫做對數的底數,N叫做真數,
舉個栗子好了:

log1(以2為底)的值是多少?在這個栗子中2為底數,1為真數,答案為x,也就是2的多少次方是1,答案就是多少,而我們都知道除0以外的任何數的0次方都為1,所以log1(以2為底)的值是0,
以下是logx(以2為底)的函式圖(右半部分):

那么,log1000(以2為底)到底是多少呢?
答:2的多少次方是1000就是多少!!!
而2的9次方是512,2的10次方是1024(熟悉的1024),所以log1000(以2為底)位于 9 - 10 之間,也就是9點多,
看到這里你可能還是很懵,好吧,log1000(以2為底)這個的值我知道是怎么算出來的了,但是這個log1000(以2為底)是怎么得出來的,而且這個答案為10比起1000或者說是50也太少了吧,簡直不科學,不可能呀!
不要急,這個就是正確答案,至于個中緣由,請聽我接下來娓娓道來!!!
資訊熵
如果用一句話來概括本文中的問題:無非是國王想要弄清楚1000瓶酒中哪一瓶是毒酒,國王不確定1000瓶酒中哪一瓶是毒酒 好像是句廢話 ,我們往上走一層,也就是說國王想要弄清楚一件非常非常不確定的事情,
如果從資訊的角度來理解,一件事情的不確定性就等于它的資訊量,而我們通常用資訊熵來實作資訊的度量(具體可以參照吳軍博士《數學之美》的第6章<資訊的度量和作用>)
《數學之美》pdf 百度網盤鏈接分享
鏈接:https://pan.baidu.com/s/1dQ08nWsSmyuMDhesIYv_Vw
提取碼:yxaf
看起來資訊熵的概念似乎很玄學,但是沒關系,吳軍博士在《數學之美》中舉了一個很通俗易懂的例子,以下是原文(寫不動了,最近缺乏小伙伴們的支持和鼓勵,容我偷個小懶):
來看一個例子,2010 年舉行了世界杯足球賽,大家都很關心誰會是冠軍,假如我錯過了看世界杯,賽后我問一個知道比賽結果的觀眾“哪支球隊是冠軍”?他不愿意直接告訴我,而讓我猜,并且我每猜一次,他要收一元錢才肯告訴我是否猜對了,那么我需要付給他多少錢才能知道誰是冠軍呢?我可以把球隊編上號,從1到32,然后提問:“ 冠軍的球隊在1-16號中嗎?”假如他告訴我猜對了,我會接著問:“冠軍在 1-8號中嗎?”假如他告訴我猜錯了,我自然知道冠軍隊在9-16號中,這樣只需要五次,我就能知道哪支球隊是冠軍,所以,誰是世界杯冠軍這條訊息的資訊量只值5塊錢,
是不是跟二分查找的理念是一樣的呢?
二分查找的話,可以查看我的這篇博客的第一章就是了:
肝了幾萬字,送給看了《演算法圖解》卻是主攻Java的你和我(上篇)
而二分查找的時間復雜度為logN(以2為底),在上文中,假設每支球隊奪冠概率相同的話,如果要保證一定能猜出32只球隊中的冠軍隊伍,我只需要5次,也就是log32(以2為底),那么,國王想要一定能找出1000瓶毒酒中的哪一瓶毒酒,只需要查找log1000(以2為底)次,也就是9點多次,因為一定要找出,9次可能找不完,所以需要10次,
這個時候可能很多小伙伴就開始躍躍欲試了:我知道了,我知道了
我們來把1000瓶酒按照 1 - 1000進行編號,那我們只需要10次就可以試出毒酒了:
- 第一次,安排1名死囚,分別品嘗編號為 1-500 的酒,如果這名死囚毒發身亡,則毒酒在編號為1 - 500酒中;反之,則在另外500瓶酒中,
- 第二次,再安排1名死囚,分別品嘗編號為 1 - 250 的酒,(在這里,我們也假設這名死囚毒發身亡),
- 第三次,再次安排1名死囚,分別品嘗編號為 1 - 125瓶的酒,(這里,我們還是假設這名死囚毒發身亡),
- 第四次,再再次安排1名死囚,分別品嘗編號為 1 - 63瓶的酒,(在這里,我們還是假設這名死囚毒發身亡),
- 第五次,再再再次安排1名死囚,分別品嘗編號為 1 - 32瓶的酒,(在這里,我們還是假設這名死囚毒發身亡,如果沒有人毒發身亡毒酒就在另外的31瓶酒里,),
好了,到了久違的32,根據上文我們可以知道還需要5次就一定可以判斷出毒酒是哪瓶!總次數,也就是 5 + 5 = 10次!!!
那么,按照這種方法需要的死囚人數最多為10名(畢竟如果這名死囚沒有毒發身亡的話,就可以接著試酒),
哦,原來這個就是正確答案的由來呀!!!
不過這個時候可能有小伙伴就會發現不對勁了
等等,這個方法我確實是理解了,但是這樣的話:
- 耗時:一個半小時 * 10 = 15個小時,
what???需要十五個小時,那我等到能吃酒席的話,黃花菜都涼了,
所以本章節講的全都是廢話嗎?
當然不是!
那如果不是廢話的話,為什么完全無法達到想要的效果呢?
因為我們現在還停留在十進制的世界,
下面,歡迎來到二進制的世界!!!
二進制

如果你看到上圖中的話,不禁心中生出一個疑問:那另外八種人呢?那么不好意思,你屬于不懂二進制的人,因為二進制中的2表示為 10,
曾幾何時,我以為這句話只是單純用來裝(A和C之間)來用的,
曾幾何時,我也天真的認為十進制天然要比二進制要高級得多 ,
但其實不然!
當然,在我的這篇博客 實作兩個數互換的八種方法 的<異或>章節中曾經提到過:
計算機中沒有我們所謂的2、3、4、5 … 100 … 1000 … ,計算機中有的只是0和1,逢二便進一,
那么,二進制和資訊熵有啥子關聯嗎?我們試著追根溯源:原來,資訊熵的概念是1948年香農在他那非常著名的論文《通信的數學原理》中提出來的,而香農并不是用次數(比如猜出32球隊中的冠軍隊伍為5次、1000瓶毒酒需要10次)來度量資訊量的,而是用位元(Bit)這個概念!
那么問題來了,什么是位元呢?
其實,一個位元就是一個二進制位!
就拿吳軍博士舉的這個球隊的栗子好了,

這里我們為了節省空間,就假設只有8只球隊好了,32支球隊、64支球隊都可以以此類推,
經過上述分析,猜出8支隊伍中冠軍隊伍的資訊熵為log8(以2為底),也就是3位元,3個二進制位,而獲得冠軍隊伍可能的結果:總共為8種情況,剛好可以用3個二進制位表示,
如下表,3個二進制位剛好可以表示8種情況:
| 十進制 | 二進制 |
|---|---|
| 0 | 0 0 0 |
| 1 | 0 0 1 |
| 2 | 0 1 0 |
| 3 | 0 1 1 |
| 4 | 1 0 0 |
| 5 | 1 0 1 |
| 6 | 1 1 0 |
| 7 | 1 1 1 |
或者說,我們剛好可以用3個二進制位給這8支球隊編號,(二進制是從0開始計數)
那么,按照二進制的思維該如何解這個“國王試毒酒”問題呢?
破案
大家好,我是狄仁杰,我來破案了,

為了方便大家思考,我們從頭開始分析
- 假設是1瓶酒,因為這瓶酒肯定是毒酒,所以需要0名死囚品嘗這一瓶酒,
- 假設是2瓶酒,我們不確定哪瓶酒有毒,只需要一名死囚品嘗其中一瓶酒:若死囚身亡,則品嘗的酒有毒;反之,則另一瓶酒有毒,
- 假設是3瓶酒,我們不確定哪瓶酒有毒,這個時候一名死囚明顯就不夠用了撒,就需要再加一名死囚,
所以,我們通過以上分析可以得出:一名死囚所能試出毒酒的最多瓶數為2瓶,也就是2的1次方,換言之,2瓶酒的資訊熵為log2(以2為底),也就是1位元,為一個二進制位,
那么,到底該怎么試呢?
這里,我們將一名死囚對應一個二進制位,將酒分別用二進制進行編號,如下表:
| 十進制 | 二進制位1 |
|---|---|
| 酒的編號 | 死囚甲 |
| 0 | 0 |
| 1 | 1 |
通過上述分析,我們已經知道了:在兩瓶酒的情況下,只要死囚甲隨便選擇一瓶喝就可以試出哪一瓶是毒酒,
因為二進制只有0和1兩種情況,而死囚對于一瓶酒也只有兩種情況:喝,或者不喝,
而作為創造萬物的程式員,這里我們可以很容易的設定一個規則:當死囚對應的二進制位為1時喝,為0時不喝(當然,你也可以反過來),也就是說,在上表中,編號為0的酒,我們簡稱酒0,不讓死囚甲喝;而酒1則死囚甲喝,
若是,死囚甲毒發身亡,則酒1有毒;反之,則酒0有毒,
同樣的,2名死囚可以表示2的2次方,也就是試出4瓶酒;或者說,4瓶酒的資訊熵為log4(以2為底數)也就是2Bit,2個二進制位,
我們也可以用表格表示出來:
| 十進制 | 二進制位2 | 二進制位1 |
|---|---|---|
| 酒的編號 | 死囚乙 | 死囚甲 |
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 2 | 1 | 0 |
| 3 | 1 | 1 |
同樣的,死囚對應的二進制位為1時喝,為0時不喝,也就是說,死囚甲需要喝酒1和酒3,死囚乙需要喝酒2和酒3,
那么,如果這樣的話,我們該如何根據身亡情況來判定毒酒呢?
其實,很簡單:如果某名死囚身亡,那么毒酒一定在他喝的那些酒中;反之,如果某名死囚沒身亡,那么毒酒一定不在他喝的那些酒中,
而根據我們的規則:某名死囚對應的二進制位為1,則表示喝;為0,則表示不喝,所以,每瓶酒是毒酒所對應的情況分別為:
| 十進制 | 二進制位2 | 二進制位1 |
|---|---|---|
| 酒的編號 | 死囚乙(狀態) | 死囚甲(狀態) |
| 0 | 0 (生) | 0 (生) |
| 1 | 0 (生) | 1 (死) |
| 2 | 1 (死) | 0 (生) |
| 3 | 1 (死) | 1 (死) |
顯然:
- 若是甲乙雙生,則對應的二進制位為
00,也就是酒0為毒酒, - 若是乙生甲死,則對應的二進制位為
01,也就是酒1為毒酒, - 若是乙死甲生,則對應的二進制位為
10,也就是酒2為毒酒, - 若是甲乙雙死,則對應的二進制位為
11,也就是酒3為毒酒,
同樣的,三名死囚可以表示出2的3次方也就是8瓶酒,
| 十進制 | 二進制位3 | 二進制位2 | 二進制位1 |
|---|---|---|---|
| 酒的編號 | 死囚丙 | 死囚乙 | 死囚甲 |
| 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 1 |
| 2 | 0 | 1 | 0 |
| 3 | 0 | 1 | 1 |
| 4 | 1 | 0 | 0 |
| 5 | 1 | 0 | 1 |
| 6 | 1 | 1 | 0 |
| 7 | 1 | 1 | 1 |
同樣的,死囚所對應的二進制位為1,表示喝;為0,則表示不喝,
- 若死囚甲乙丙都死,則他們的二進制位全都表示為1,也就是
111,對應十進制的7,也就是酒7為毒酒, - 若死囚甲乙丙都不死,則他們的二進制位全都表示為0,也就是
000,對應十進制的0,也就是酒0為毒酒,
那么通過以上種種,我們來類推一下
- 試出1瓶酒中的一瓶毒酒,需要0名死囚,也就是log1(以2為底),
- 試出2瓶酒中的一瓶毒酒,需要1名死囚,也就是log2(以2為底),
- 試出3瓶酒中的一瓶毒酒,需要2名死囚,也就是log3(以2為底),向上取整為2,
- 試出4瓶酒中的一瓶毒酒,需要2名死囚,也就是log4(以2為底),
- 試出5瓶酒中的一瓶毒酒,需要3名死囚,也就是log5(以2為底),向上取整為3,
… - 試出8瓶酒中的一瓶毒酒,需要3名死囚,也就是log8(以2為底),
…
所以,試出1000瓶酒中的一瓶毒酒,需要10名死囚,也就是log1000(以2為底),,向上取整為10,

怎么樣?感受到二進制的神奇和美妙了嗎?
回歸數學
當然,神奇歸神奇,那么到底為啥子可以這樣呢?為此,我還特意請教了一位數學專業的小學妹,她從集合的角度分析倒給了我一個全新的思考視角,
一名死囚可以表示的情況為2種:

兩名死囚可以表示的情況為4種:

三名死囚可以表示的情況為8種:

以此類推即可,
其實這個數學問題,被稱為 冪集,也就是求集合中所有的子集(包括全集和空集)構成的集族,因為每個子集都有兩種情況:選,或者不選,所以,n個子集所對應的情況為: 2的n次方,而一種情況恰好對應著唯一的一瓶酒,是不是和之前的分析有著異曲同工之妙呢?
關于圖片:沒辦法,畫圖軟體處理不好圖層之間的關系,我就只好手畫了,哈哈,
最后
不知不覺,一篇博客就肝到了凌晨一點半左右:

作業以后,可以用來寫博客的時間也變少了,各位小伙伴們,如果覺得這篇博客寫的還不錯的話,歡迎各位點贊、評論以及加關注哦,這樣本博主才更有動力給大家帶來更多的優質博客,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/184230.html
標籤:java
上一篇:企業或個人建設一個簡單的網站步驟
