Monty Hall Problem - เมื่อความจริงและความรู้สึกไม่ไปด้วยกัน

ผมชอบปัญหา Monty Hall Problem มาก เมื่อครั้งแรกที่ได้ยินก็รู้สึกว่ามีอะไรชวนประหลาดพอสมควร เลยคิดว่าจะเอามาเล่าต่อสักหน่อย เริ่มจากการอธิบายก่อนแล้วกันนะครับว่ามันคืออะไร

เริ่มจากมีรายการเกมโชว์ทีวีรายการหนึ่งชื่อว่า Let's Make a Deal ซึ่งมีพิธีกรชื่อ Monty Hall (ก็เลยกลายเป็น Monty Hall Problem นั่นแหละ) ในรายการจะมีประตูให้ 3 บาน โดยที่ 1 ใน 3 ของประตูจะเปิดแล้วเจอรถ ส่วนอีก 2 บานเปิดแล้วจะเป็นแกะ ซึ่งถ้าเปิดแล้วเจอรถก็คงจะได้รถกลับบ้านล่ะมั้ง (ไม่ชัวร์เพราะไม่เคยดูเหมือนกัน แต่รายการมันคงไม่ขี้งกเกินไปมั้ง) เริ่มต้นคือแขกรับเชิญจะได้เลือกประตู 1 บานก่อน แต่พิธีกรจะยังไม่เฉลย แต่จะไปเปิดประตู 1 ใน 2 บานที่แขกรับเชิญไม่ได้เลือก และเปิดให้ดูว่าเป็นแกะ แล้วถามแขกรับเชิญว่าจะเปลี่ยนใจเลือกประตูอีกบานหรือไม่

ตัวรายการก็มีเพียงเท่านี้แหละครับ แต่จะมีตัวละครอีกคนโผล่ออกมาคือผู้หญิงอีกคนที่ชื่อว่ามาริลิน (Marilyn vos Savant) ซึ่งเป็นผู้ได้รับการบันทึกลงกินเนสบุ๊คว่ามี IQ สูงที่สุดในโลก มาริลินเปิดคอลัมภ์ Ask Marilyn อยู่ในนิตยสาร Parade ให้คนถามคำถามเกี่ยวกับคณิตศาสตร์เข้ามาได้ ทีนี้ก็มีคนที่สงสัยจากการดูรายการ Let's Make a Deal ส่งจดหมายไปถามว่าถ้าเกิดสถานการณ์ขึ้นแบบนี้ ควรจะเปลี่ยนใจเลือกประตูอีกบานหรือไม่

 

มาริลินตอบว่า สมควรเปลี่ยน เพราะถ้าไม่เปลี่ยนโอกาสที่จะเปิดได้รถจะเป็น 1/3 แต่ว่าถ้าเปลี่ยนจะมีโอกาสเปิดเจอรถเป็น 2/3 เลยทีเดียว

 

แน่นอนว่าฟังแล้วก็ออกจะขัดแย้งกับความรู้สึกของหลายๆ คน การไม่เปลี่ยนประตูโอกาสที่จะเจอรถเป็น 1/3 แบบไม่ต้องสงสัย แต่ในเมื่อหลังจากที่ตัดประตูทิ้งไป 1 บานแล้ว การเลือกเปลี่ยนหรือไม่เปลี่ยนน่าจะเหมือนกับเหลือประตู 2 บาน ซึ่งไม่ว่าเลือกที่จะเปลี่ยนหรือไม่เปลี่ยนโอกาสที่จะได้รถก็น่าจะเป็น 1/2 เท่ากันรึเปล่า?

มีหลายคนที่ออกมาเขียนตอบโต้มาริลิน แล้วก็มีการเขียนอธิบายเพิ่มเติมจากมาริลินเพื่อโต้กลับเช่นกัน แต่ตรงนี้ผมขอข้ามรายละเอียดไปนะครับ ใครที่สนใจลองไปหาอ่านต่อดูว่าเขาอธิบายกันต่อว่ายังไง

แต่ไม่ว่าจะฟังยังไงก็ยังเกิดความสงสัยอยู่ว่าทำไมมันถึงเป็น 1/3 ได้ คราวนี้เลยจะมาลองเขียนโปรแกรมเพื่อ simulate ดูว่าความจริงแล้วเป็นยังไงกันแน่ มาลองเริ่มกันเลยครับ

ผมจะเริ่มจากโปรแกรมจำลองในกรณีที่เราไม่เปลี่ยนใจก่อนนะครับ ซึ่งโปรแกรมก็จะง่อยๆ ประมาณนี้ครับ แค่สุ่มหมายเลขประตูที่มีรถ (1-3) และสุ่มหมายเลขประตูที่แขกรับเชิญเลือก (1-3 เหมือนกัน) จากนั้นถ้าตรงกันเราก็เพิ่มค่าให้กับตัวแปรที่เก็บว่าตอบถูกไปกี่ครั้ง

int car_door = rand() % 3 + 1;
int selected_door = rand() % 3 + 1;

if (selected_door == car_door) {
    correct += 1;
}

เขียนให้ครบทั้งโปรแกรมจะเป็นแบบนี้

int t, correct = 0, n = 300000;
for (t = 0; t < n; t++) {
    int car_door = rand() % 3 + 1;
    int selected_door = rand() % 3 + 1;

    if (selected_door == car_door) correct += 1;
}

cout << "Correct " << correct << " from " << n << "\n";
cout << correct * 1.0 / n << " %\n";

ตัวโปรแกรมจะวนลูปเพื่อจำลองสถานการณ์นี้เป็นจำนวน 300,000 ครั้ง แล้วดูว่าจำนวนครั้งที่ทายถูกจะเป็นเท่าไหร่ เมื่อรันออกมาก็จะเห็นว่าจาก 300,000 รอบ จะตอบถูกอยู่ที่ประมาณ 100,000 ครั้ง หรือประมาณ 1/3 จริงๆ ซึ่งอันนี้ก็ตามที่คาดการณ์ไว้

Correct 99692 from 300000
0.332307 %

ที่นี้เราลองจำลองเพิ่ม ในกรณีที่ผู้เล่นจะเปลี่ยนใจทุกครั้งหลังจากพิธีกรเปิดประตูแล้ว ด้วยการให้พิธีกรเปิดประตูที่เป็นแกะก่อน จากนั้นผู้เล่นจะเปลี่ยนไปเลือกอีกประตูที่เหลือแทน ในขั้นตอนการจำลองก็จะต้องหาก่อนว่าประตูที่ผู้เล่นไม่ได้เลือกคือประตูใดบ้าง ตามโค้ดด้านล่างนี้

vector<int> remain_doors;
for (int d = 1; d <= 3; d++)
    if (d != selected_door)
        remain_doors.push_back(d);        

ประตูที่ไม่ได้เลือกจะเก็บไว้ที่ remain_doors ซึ่งจะเก็บสองค่าแน่นอน ถ้าผู้เล่นเลือก 2 ก็จะเก็บ [1,3] ถ้าผู้เล่นเลือก 3 ก็จะเก็บ [1,2] หรือถ้าผู้เล่นเลือก 1 ก็จะเก็บ [2,3] หลังจากได้ประตูที่เหลือแล้ว พิธีกรก็จะต้องเปิดประตูทิ้งไป 1 ประตูจาก 2 บานนี้ ถึงตรงนี้โปรแกรมเราจะต้องแบ่งออกเป็น 3 กรณีย่อย คือ

  1. ประตูที่เหลือทั้งสองไม่มีรถอยู่เลย ดังนั้นพิธีกรก็จะสุ่มเปิดขึ้นมา 1 บาน และผู้เล่นก็จะเปลี่ยนไปเปิดบานทีเหลือ
  2. ประตูที่เหลือบานแรกเป็นแกะ และบานที่สองเป็นรถ พิธีกรจะต้องเปิดบานที่เป็นแกะ และผู้เล่นจะเปลี่ยนมาเลือกบานที่เหลือซึ่งเป็นรถทันที
  3. ประตูที่เหลือบานแรกเป็นรถ และบานที่สองเป็นแกะ เช่นเดียวกัน พิธีกรจะเปิดบานที่เป็นแกะ และผู้เล่นก็จะเปลี่ยนมาเปิดบานที่เป็นรถ 

ทั้งหมดนี้ก็จะได้โค้ดออกมาหน้าตาประมาณนี้ครับ

if (remain_doors[0] != car_door && remain_doors[1] != car_door)
    selected_door = remain_doors[rand() % 2];
else if (remain_doors[0] != car_door)
    selected_door = remain_doors[1];
else
    selected_door = remain_doors[0];

เขียนแบบเต็มๆ ทั้งหมดเลยก็จะได้เป็น

int t, correct = 0, n = 300000;
for (t = 0; t < n; t++) {
    int car_door = rand() % 3 + 1;
    int selected_door = rand() % 3 + 1;

    vector<int> remain_doors;
    for (int d = 1; d <= 3; d++)
        if (d != selected_door)
            remain_doors.push_back(d);

    if (remain_doors[0] != car_door && remain_doors[1] != car_door)
        selected_door = remain_doors[rand() % 2];
    else if (remain_doors[0] != car_door)
        selected_door = remain_doors[1];
    else
        selected_door = remain_doors[0];

    if (selected_door == car_door) correct += 1;
}

cout << "Correct " << correct << " from " << n << "\n";
cout << correct * 1.0 / n << " %\n";

และเมื่อรันออกมาผมลัพท์ก็เป็นดังนี้

Correct 199639 from 300000
0.665463 %

2/3 จริงๆ ด้วย!!! แต่จริงๆ ตอนที่เขียนโปรแกรมสำหรับจำลองออกมาก็เริ่มจะไม่แปลกใจแล้วล่ะครับ เพราะเงื่อนไขที่พิธีกรจะเลือกประตูที่จะเปิดมีอยู่ 3 แบบ ซึ่ง 2/3 นั้นถ้าผู้เล่นเปลี่ยนก็จะได้รถกลับบ้านไปแน่ๆ

ปัญหานี้อ่านกี่ครั้งก็ยังรู้สึกประหลาดอยู่เหมือนเดิม แต่ถ้าค่อยๆ ลองไล่สถานการณ์ดูแล้วก็จะพบว่าโอกาสที่จะได้รถมันเพิ่มขึ้นจริงๆ นะ


Reference

https://en.wikipedia.org/wiki/Monty_Hall_problem - ขอขอบคุณ Wikipedia สำหรับข้อมูลเชิงรายละเอียด เช่น ใครชื่ออะไรและออกรายการอะไร ถึงจะบอกว่าใช้อ้างอิงไม่ได้ แต่กรณีนี้ก็น่าเชื่อถือพอมั้งครับ

Taxonomy upgrade extras: