Cheryl 的生日是哪天?

In [3]:
%reload_ext varwatch
<IPython.kernel.zmq.zmqshell.ZMQInteractiveShell object at 0x7f8f83482710>
  1. Albert 和 Bernard 最近和 Cheryl 成為朋友,他們想知道她的生日。 Cheryl 給他們十個可能的日期:

       May 15     May 16     May 19
      June 17    June 18
      July 14    July 16
    August 14  August 15  August 17
    
  2. 她接著分別告訴 Albert 和 Bernard 自己生日的月份和日期

  3. Albert: 我不知道 Cheryl 的生日,但 Bernard 也不知道.
  4. Bernard: 我一開始不知道,但現在我知到了
  5. Albert: 我也知道 Cheryl 的生日了
  6. 所以 Cheryl 什麼時候生日?</i>

1. Cheryl 給十個可能的日期:

In [4]:
十個日期 = ['May 15',    'May 16',    'May 19',
        'June 17',   'June 18',
        'July 14',   'July 16',
      'August 14', 'August 15', 'August 17']

輔助函數:

In [5]:
def (date): return date.split()[0]

def (date): return date.split()[1]
In [6]:
('May 15')
Out[6]:
'May'
In [7]:
('May 15')
Out[7]:
'15'

2. 她接著分別告訴 Albert 和 Bernard 自己生日的月份和日期

我們可以模擬出 告訴, 和 知道 這兩個概念:

In [8]:
def 告訴(部份資訊, 可能日期=十個日期):
    "傳回符合部份資訊的可能日期"
    return [日期 for 日期 in 可能日期 if (部份資訊 in 日期)]

def 知道(可能日期):
    "一個人知道的可能日期只有一個時,那就知道了"
    return len(可能日期) == 1

比方: 如果 Cheryl 告訴 Albert 她生日在五月,那 Albert 就知道:

In [9]:
告訴('May')
Out[9]:
['May 15', 'May 16', 'May 19']

如果她告訴 Bernard 她生日是 15 號, 那他知道的可能日期是:

In [10]:
告訴('15')
Out[10]:
['May 15', 'August 15']

所以 Bernard 不知道確切日期:

In [11]:
知道(告訴('15'))
Out[11]:
False

3. Albert: 我不知道 Cheryl 的生日,但 Bernard 也不知道.

In [12]:
def 陳述3(日期):
    "Albert: 我不知道 Cheryl 的生日,但 Bernard 也不知道."
    可能的日期 = 告訴((日期))
    return (not 知道(可能的日期) 
            and all(not 知道(告訴((d))) for d in 可能的日期))

試試看:

In [13]:
陳述3('July 14')
Out[13]:
True

看看有哪些日期符合陳述3:

In [14]:
過濾 = lambda f, l: [x for x in l if f(x)]
過濾(陳述3, 十個日期)
Out[14]:
['July 14', 'July 16', 'August 14', 'August 15', 'August 17']

4. Bernard: 我一開始不知道,但現在我知到了

換句話說

Bernard: 從一開始 Cheryl 告訴我的資訊,我不知道確切日期。考慮符合陳述3的結果,現在我知到了。

In [15]:
def 陳述4(日期):
    "Bernard: At first I don't know when Cheryl's birthday is, but I know now."
    一開始 = 告訴((日期))
    return (not 知道(一開始)
            and 知道(過濾(陳述3, 一開始)))
In [16]:
過濾(陳述4, 十個日期)
Out[16]:
['May 15', 'May 16', 'June 17', 'July 16', 'August 15', 'August 17']
In [17]:
過濾(陳述4, 過濾(陳述3, 十個日期))
Out[17]:
['July 16', 'August 15', 'August 17']

5. Albert: 我也知道 Cheryl 的生日了

Albert 得到兩個資訊,一個是一開始 Cheryl 告訴他的月份,一個是陳述4的結果。從這兩件事情,推論出 Cheryl 的生日

In [18]:
def 陳述5(日期):
    "Albert: 我也知道  Cheryl 的生日了"
    一開始 = 告訴((日期))
    return 知道(過濾(陳述4, 一開始))

6. 所以 Cheryl 什麼時候生日?

In [19]:
可能2 = 十個日期
可能3 = 過濾(陳述3, 可能2)
可能4 = 過濾(陳述4, 可能3)
過濾(陳述5, 可能4)
Out[19]:
['July 16']

Credit:

改自 Peter Norvig: http://nbviewer.ipython.org/url/norvig.com/ipython/Cheryl.ipynb
投影片化做了修剪,也增加一些我覺得關鍵的點(資訊的流向)。