一个反直觉数学题的程序验证

很久之前看到的东西了,今天总算抽空把它写完了。

最开始是在 Quora 上看到一个很有意思的答案,虽然是简单的抛硬币问题,但结果确实是相当反直觉,因此,干脆用 Python 写了一系列的小程序,来验证这些结果。

第一个游戏很简单,抛一个硬币,连续抛两次,先后出现正面(heads,后面记为H)和反面(tail,后面记为T)的概率是ht,先后出现HH的概率记为hh。很显然,两者相等。程序验证也很简单,试验十万次,看分别出现多少次。

def game1():  
    # flip twice, the possibility of HH or HT  
    hh = 0; ht = 0  
    for i in xrange(100000):  
        pre = ''; cur = ''  # previous and current  
        pre = random.choice(['H','T'])  
        cur = random.choice(['H','T'])  
        if pre+cur == 'HH':  
            hh += 1  
        if pre+cur == 'HT':  
            ht += 1  
    print hh, ht  
    ## my result: 24943 24776

那么,游戏变一下。假如连续抛一个硬币,直到出现HH或HT(注意HT是有顺序的),问题是,两者需要抛的次数相同吗?基于前面的结论,似乎应该相同,然而,结果是,不同。同样地,代码说话:

def game2():  
    # flip until HH or HT, in average which took more flips?  
    hh = []; ht = []  
    for _ in xrange(100000):  
        pre = ''; cur = ''  
        for i in xrange(99):  
            pre = cur; cur = random.choice(['H','T'])  
            if pre+cur == 'HH':  
                hh += [i+1]  
                break  
    for _ in xrange(100000):  
        pre = ''; cur = ''  
        for i in xrange(99):  
            pre = cur; cur = random.choice(['H','T'])  
            if pre+cur == 'HT':  
                ht += [i+1]  
                break  
    hh = np.array(hh); ht = np.array(ht)  
    print np.mean(hh), np.mean(ht)  
    ## my result: 6.00559 3.99

无视里面用了一小段很丑陋的实现吧。。。对结果是没影响的。

接下来,如果连续抛,出现HH则A赢,出现HT则B赢,问,谁更可能赢?结果是,一样的可能:

def game3():  
    # flip until HH or HT, if HH, A win, if HT, B win. Is the game fair?  
    hh = 0; ht = 0  
    for _ in xrange(100000):  
        pre = ''; cur = ''  
        for i in xrange(99):  
            pre = cur; cur = random.choice(['H','T'])  
            if pre+cur == 'HH':  
                hh += 1  
                break  
            if pre+cur == 'HT':  
                ht += 1  
                break  
    print hh, ht  
    ## my result: 50028 49972

再然后,连续抛,如果出现HHT则A赢,如果出现THH则B赢。注意到HHT和THH是对称的,所以一个直观的猜测是两人赢面相同,但实际上,并非如此:

def game4():  
    # flip until HHT and THH, if HHT, A win, if THH, B win. Is the game fair?  
    hht = 0; thh = 0  
    for _ in xrange(100000):  
        ppr = ''; pre = ''; cur = ''  
        for i in xrange(99):  
            ppr = pre; pre = cur; cur = random.choice(['H','T'])  
            if ppr+pre+cur == 'HHT':  
                hht += 1  
                break  
            if ppr+pre+cur == 'THH':  
                thh += 1  
                break  
    print hht, thh  
    ## my result: 25031 74969

最有意思的来了。上面的游戏里A发现B赢得更多一点,于是A选择了THH,B就换成了TTH,结果A还是输。A换成TTH,B就换成HTT,A又输。A换成HTT,B换成HHT,A还输。上面几局写成不等式更直观一点:thh > hht, tth > thh, htt > tth, hht > htt。WTF?!!!

def game5():  
    print "THH vs HHT"  
    thh = 0; hht = 0  
    for _ in xrange(100000):  
        ppr = ''; pre = ''; cur = ''  
        for i in xrange(99):  
            ppr = pre; pre = cur; cur = random.choice(['H','T'])  
            if ppr+pre+cur == 'THH':  
                thh += 1  
                break  
            if ppr+pre+cur == 'HHT':  
                hht += 1  
                break  
    print 'THH, HHT: ', thh, hht

    print "\nTTH vs. THH"  
    tth = 0; thh = 0  
    for _ in xrange(100000):  
        ppr = ''; pre = ''; cur = ''  
        for i in xrange(99):  
            ppr = pre; pre = cur; cur = random.choice(['H','T'])  
            if ppr+pre+cur == 'TTH':  
                tth += 1  
                break  
            if ppr+pre+cur == 'THH':  
                thh += 1  
                break  
    print 'TTH, THH: ', tth, thh

    print "\nHTT vs. TTH"  
    htt = 0; tth = 0  
    for _ in xrange(100000):  
        ppr = ''; pre = ''; cur = ''  
        for i in xrange(99):  
            ppr = pre; pre = cur; cur = random.choice(['H','T'])  
            if ppr+pre+cur == 'TTH':  
                tth += 1  
                break  
            if ppr+pre+cur == 'HTT':  
                htt += 1  
                break  
    print 'HTT, TTH: ', htt, tth

    print "\nHHT vs. HTT"  
    hht = 0; htt = 0  
    for _ in xrange(100000):  
        ppr = ''; pre = ''; cur = ''  
        for i in xrange(99):  
            ppr = pre; pre = cur; cur = random.choice(['H','T'])  
            if ppr+pre+cur == 'HHT':  
                hht += 1  
                break  
            if ppr+pre+cur == 'HTT':  
                htt += 1  
                break  
    print 'HHT, HTT: ', hht, htt  
    ## Result:  
    ## THH vs HHT  
    ## THH, HHT:  75095 24905  
    ##   
    ## TTH vs. THH  
    ## TTH, THH:  66986 33014  
    ##   
    ## HTT vs. TTH  
    ## HTT, TTH:  74937 25063  
    ##   
    ## HHT vs. HTT  
    ## HHT, HTT:  66880 33120

其实这个游戏叫 Penny’s Game,有兴趣的并且高中数学没丢的可以自己再手算一下上面的概率或者数学期望,再验证一下。

links

social