用程序消除一道機率題的二義性

  無心在維基看到了一個關於機率悖論的討論Boy or Girl paradox。有爭議的的題目以下:
  史密斯先生有兩個孩子,至少其中之一是男孩,請問兩個孩子都是男孩的可能性有多大?
  原文以下:
  Mr. Smith has two children. At least one of them is a boy. What is the probability that both children are boys?
  一部分人的答案是1/3,一部分人的答案是1/2.爲何會產生這樣的結果呢,文中說得很清楚是由於問題自己存在歧義,對「至少一個孩子是男孩」的信息作不一樣的假設,致使了不一樣的結果。問題歸根結底是因爲天然語言的二義性,將問題轉化成了兩個不一樣的數學模型,從而產生了兩個不一樣的結果。咱們知道設計一種編程語言的任務之一就是消除其二義性,所以通常而言編程語言是一種沒有歧義的語言(因此程序員都喜歡寫代碼不喜歡說話麼),若是用編程語言來描述這一問題,會不會好理解點呢?以java爲例,咱們來看看。
  如下是對史密斯先生有兩個孩子的可能狀況進行描述,其中random.nextBoolean()函數隨機返回true或false的機率均爲1/2,用於模擬現實中生男孩女孩的機率各一半。可見程序不只描述了全部的組合,還明確了題目中暗含的條件。java

class TwoChildren
    {
        Child child1;
        Child child2;
        public TwoChildren()//一個孩子是男孩或女孩的機率是50%
        {
            child1 = random.nextBoolean()?Child.BOY:Child.GIRL;
            child2 = random.nextBoolean()?Child.BOY:Child.GIRL;
        }
    }

  接着描述「至少一個孩子是男孩」,由於這裏是存在歧義的地方,因此轉化成代碼描述時,咱們會根據兩個不一樣的假定,獲得不一樣的代碼。1/3結果的假定條件:觀察了史密斯的兩個孩子,其中一個是男孩:程序員

boolean isObserved(TwoChildren draw) 
{
    return draw.child1 == Child.BOY || draw.child2 == Child.BOY;
}

熟悉java的程序員會注意到,若是child1爲BOY的話,child2不會被觀察(||運算符後面的代碼不會被執行),是否和天然語言描述不同?咱們從邏輯或運算定義可知兩者是等價的,即便||運算符後面的代碼被執行,也不影響程序結果,換成天然語言就是咱們觀察史密斯的兩個孩子時,一個一個的觀察,若是發現其中一個是男孩,就已經保證了「至少一個是男孩」,就不必接着觀察了。
  接着描述1/2的結果假定條件:隨機觀察了史密斯的一個孩子,其中一個是男孩:編程

boolean isObserved(TwoChildren draw) 
{
    return random.nextBoolean()?draw.child1 == Child.BOY:draw.child2 == Child.BOY;
}

其中random.nextBoolean()函數隨機返回true或false的機率均爲1/2,就是兩個孩子被選中觀察的機率是1/2.dom

最後咱們描述問題的提出編程語言

if(isObserved(draw))
{
    observedCount ++;
    if(draw.child1 == Child.BOY && draw.child2 == Child.BOY)
    {//兩個孩子都是男孩
        matchedCount++;
      //通過不少次運算後,((double)matchedCount/observedCount)最可能的值是多少?
    }
}

  至此咱們將一個以天然語言描述的問題,轉化成了一個以程序語言描述的問題。這個轉化是否正確呢,咱們作10萬次測試,看結果是否知足數學推導的預期。如下是一個完整的測試代碼:ide

package hermitdl.test2;

import java.util.Random;

/**
 * Test for <<Boy or Girl paradox>>
 */
public class App 
{
    static enum Child
    {
        BOY,
        GIRL
    };
    
    static Random random = new Random();
    
    static class TwoChildren
    {
        Child child1;
        Child child2;
        public TwoChildren()//一個孩子是男孩或女孩的機率是50%
        {
            child1 = random.nextBoolean()?Child.BOY:Child.GIRL;
            child2 = random.nextBoolean()?Child.BOY:Child.GIRL;
        }
    }
    //隨機檢查一個孩子的性別是不是男孩
    static class PeekOneTest extends ProbabilityTest
    {
        @Override
        boolean isObserved(TwoChildren draw) 
        {
            return random.nextBoolean()?draw.child1 == Child.BOY:draw.child2 == Child.BOY;
        }
    }
    //檢查兩個孩子的性別,是否其中之一是男孩
    static class PeekTwoTest extends ProbabilityTest
    {
        @Override
        boolean isObserved(TwoChildren draw) 
        {
            return draw.child1 == Child.BOY || draw.child2 == Child.BOY;
        }
    }
    
    
    static abstract class ProbabilityTest
    {
        int observedCount = 0;
        int matchedCount = 0;
        //在isObserved爲真的狀況下計數,以及兩個孩子均是女孩的狀況計數。
        void test(TwoChildren draw)
        {
            if(isObserved(draw))
            {
                observedCount ++;
                if(draw.child1 == Child.BOY && draw.child2 == Child.BOY)
                {
                    ////兩個孩子都是男孩
                    matchedCount++;
                }
            }
        }
        abstract boolean isObserved(TwoChildren draw);
        
        void printResult()
        {
            System.out.printf(this.getClass().getSimpleName() +"=%d/%d=%f\n"
            ,matchedCount
            ,observedCount
            ,((double)matchedCount/observedCount));
        }
    }
    
    public static void main( String[] args )
    {
        PeekOneTest peekOneTest = new PeekOneTest();
        PeekTwoTest peekTwoTest = new PeekTwoTest();
        TwoChildren draw;
        for(int i = 0;i < 1000000; i++)
        {
            draw = new TwoChildren();
            peekOneTest.test(draw);
            peekTwoTest.test(draw);
        }
        peekOneTest.printResult();
        peekTwoTest.printResult();
    }
}

如下爲程序的幾回運行結果:
PeekOneTest=249436/499301=0.499570
PeekTwoTest=249436/750234=0.332478函數

PeekOneTest=250209/500229=0.500189
PeekTwoTest=250209/749846=0.333681測試

PeekOneTest=249963/500234=0.499692
PeekTwoTest=249963/749712=0.333412this

  可見模擬結果始終分別在1/2與1/3附近波動,是符合數學預期的。設計

相關文章
相關標籤/搜索