程式設計藝術家經典試題解讀:猜生日問題
這是一道很多人知道的經典題目,其中的邏輯推理堪稱短小精悍試題的典範。
題目:
張老師的生日為M月D日,他將M值告訴給了小明,將D值告訴給了小強。然後給出如下這些日期:
3月4日,3月5日,3月8日,6月4日,6月7日,9月1日,9月5日,12月1日,12月2日,12月8日。
張老師:你們知道我的生日是哪天嗎?
小明:如果小強不知道,那我也不知道。
小強:剛才我不知道,現在我知道了。
小明:我也知道了。
分析:
這是一類典型的條件推理問題。通常採用的方式,是通過條件的有序疊加,篩選出最終答案。
標記:
引入如下標記:
M0:張老師生日日期的月份,
D0:張老師生日日期的日子,
M:表示常量月,
D:表示常量日,
m:表示變數月,
d:表示變數日,
{MD}:張老師給出的所有日期的集合,
{d*{MD}}:表示 {d|任意日期md屬於{MD},有md的日為d} 這樣的集合,
{d~M0*{MD}}:表示 {d|任意日期md屬於{MD},有md的月為M0,md的日為d} 這樣的集合,
{d`M0*{MD}}:表示 {d|任意日期md屬於{MD},有md的月為M0,md的日不為d} 這樣的集合,
{m*{MD}}:表示 {m|任意日期md屬於{MD},有md的月為m} 這樣的集合,
{m~D0*{MD}}:表示 {m|任意日期md屬於{MD},有md的日為D0,md的月為m} 這樣的集合,
{m`D0*{MD}}:表示{m|任意日期md屬於{MD},有md的日為D0,md的日不為d} 這樣的集合。
解答:
(1)條件1:如果小強不知道,那小明也不知道。
首先此時,還不知道小強知道不知道。
如果小強不知道,根據條件1,有{d~M0|MD}包含於{d`M0|MD}。則由此得到一個日期集合{MD1a}。
如果小強知道,則條件1不可用,不過這時更容易推理,{m~D0|MD}與{m`D0|MD}無交集,則由此得到一個日期集合{MD1b}。
因此,{MD1a}和{MD1b}共同構成了條件1能推理出的日期集合{MD1}。
(2)條件2:小強在得知條件1前,不知道;在得知條件1後,知道。
首先,小強在得知條件1前不知道,說明{MD1b}可以排除。
然後,由於得知了條件1,小強也知道了目前的答案在{MD1a}中。
接著,考慮條件2,說明{m~D0|{MD1a}}只有一個元素,即單元集或稱模為1,且該元素即為M0,這樣小強就知道了M0和D0,就知道了正確的日期。
(3)條件3:小明在得知條件2後,知道了正確的日期。
首先,小明在聽小強說“剛才不知道”後(即條件2中的“小強在得知條件1前,不知道”),就知道小強在得知條件1前並不知道答案,即小強此時知道了正確的日期在{MDa1}中,排除了{MD1b}。
然後,小明聽小強說“現在知道”後(即條件2中的“小強在得知條件1後,知道”),就知道對於{d*{MD1a}}中個任意元素dx,有{m~dx*{MD1a}}只有一個元素,即是一個單元集或稱模為1。
(4)旁觀者的邏輯推理
在小明和小強的對話中,小強是在小明第一句話之後就知道了正確的日期。而小明是在小強的話之後才知道的,並說出了整個對話中的最後一句話。而旁觀者,是在得知最後一句話後,才唯一確定了正確答案的。具體的邏輯過程,可依照(1)至(3)中的推理。
(5)程式原始碼:
package com.sinosuperman.test;
import java.util.ArrayList;
import java.util.List;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
List<MonDay> dates = new ArrayList<MonDay>();
dates.add(new MonDay(3, 4));
dates.add(new MonDay(3, 5));
dates.add(new MonDay(3, 8));
dates.add(new MonDay(6, 4));
dates.add(new MonDay(6, 7));
dates.add(new MonDay(9, 1));
dates.add(new MonDay(9, 5));
dates.add(new MonDay(12, 1));
dates.add(new MonDay(12, 2));
dates.add(new MonDay(12, 8));
System.out.println("張老師:小明知道月,小強知道日,現在你們能猜出來我的生日嗎?就在這些裡面:");
System.out.println(dates);
System.out.println("\n小明:如果小強不知道,那我也不知道。");
System.out.print("通過這句話可以知道:\n如果小強知道的話,一定在這些日期之中:");
List<MonDay> dates1 = MonDayUtil.getInstance().getDatesWithDuplicateDays(dates);
System.out.println(dates1);
System.out.print("如果小強不知道的話,一定在這些日期之中:");
List<MonDay> dates11 = MonDayUtil.getInstance().getDatesWithDistinctMon(dates);
System.out.println(dates11);
System.out.println("\n小強:剛才我不知道,現在我知道了。");
System.out.print("通過這句話可以知道:\n一定在這些日期之中:");
List<MonDay> dates2 = MonDayUtil.getInstance().getDatesWithDistinctMon(dates1);
System.out.println(dates2);
System.out.println("\n小明:我也知道了。");
System.out.print("通過這句話知道:\n一定在這些日期之中:");
List<MonDay> dates3 = MonDayUtil.getInstance().getDatesWithDistinctDay(dates2);
System.out.println(dates3);
System.out.println("-注意:\n如果只有一個,說明題目可解;\n如果多個日期,說明題目條件不全;\n如果沒有日期,說明題目錯誤。)");
}
}
class MonDayUtil {
private static MonDayUtil instance = new MonDayUtil();
public static MonDayUtil getInstance() { return instance; }
public List<MonDay> getDatesWithDuplicateDays(List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
List<Integer> monList = getMonNum(srcList);
for (Integer m : monList) {
List<MonDay> datesOfMon = getDatesOfMon(m, srcList);
List<Integer> daysOfDates = getDaysOfDates(datesOfMon);
List<MonDay> otherDatesOfMon = getOtherDatesOfMon(m, srcList);
List<Integer> otherDaysOfDates = getDaysOfDates(otherDatesOfMon);
if (otherDaysOfDates.containsAll(daysOfDates)) {
resultList.addAll(datesOfMon);
}
}
return resultList;
}
public List<MonDay> getDatesWithDistinctMon(List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
List<Integer> dayList = getDayNum(srcList);
for (Integer d : dayList) {
List<MonDay> datesOfDay = getDatesOfDay(d, srcList);
if (datesOfDay.size() == 1) {
resultList.addAll(datesOfDay);
}
}
return resultList;
}
public List<MonDay> getDatesWithDistinctDay(List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
List<Integer> monList = getMonNum(srcList);
for (Integer m : monList) {
List<MonDay> datesOfMon = getDatesOfMon(m, srcList);
if (datesOfMon.size() == 1) {
resultList.addAll(datesOfMon);
}
}
return resultList;
}
private List<MonDay> getDatesOfDay(int day, List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
for (MonDay md : srcList) {
if (md.getDay() == day) {
resultList.add(md);
}
}
return resultList;
}
private List<Integer> getDayNum(List<MonDay> srcList) {
List<Integer> resultList = new ArrayList<Integer>();
for (MonDay md : srcList) {
if (!resultList.contains(md.getDay())) {
resultList.add(md.getDay());
}
}
return resultList;
}
private List<Integer> getDaysOfDates(List<MonDay> srcList) {
List<Integer> resultList = new ArrayList<Integer>();
for (MonDay md : srcList) {
if (!resultList.contains(md.getDay())) {
resultList.add(md.getDay());
}
}
return resultList;
}
private List<Integer> getMonNum(List<MonDay> srcList) {
List<Integer> resultList = new ArrayList<Integer>();
for (MonDay md : srcList) {
if (!resultList.contains(md.getMon())) {
resultList.add(md.getMon());
}
}
return resultList;
}
private List<MonDay> getDatesOfMon(int mon, List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
for (MonDay md : srcList) {
if (md.getMon() == mon) {
resultList.add(md);
}
}
return resultList;
}
private List<MonDay> getOtherDatesOfMon(int mon, List<MonDay> srcList) {
List<MonDay> resultList = new ArrayList<MonDay>();
for (MonDay md : srcList) {
if (md.getMon() != mon) {
resultList.add(md);
}
}
return resultList;
}
}
class MonDay {
private int mon;
private int day;
public MonDay(int mon, int day) { this.mon = mon; this.day = day; }
public int getMon() { return mon; }
public int getDay() { return day; }
public String toString() { return mon + "/" + day; }
}
(6)程式解讀
類MonDay用於表示日期。
類MonDayUtil中提供了邏輯推理中可以能用到的推理方式的工具,比如:
getDatesWithDuplicateDays:輸入{MD},輸出{MD}中滿足m是md的日,且{d~m*{MD}}與{d`m*{MD}}有交集的md構成的集合。
getDatesWithDistinctMon:輸入{MD},輸出{MD}中滿足d是md的月,且{m~d*{MD}}與{m`d*{MD}}無交集的md構成的集合。
getDatesWithDistinctDay:輸入{MD},輸出{MD}中滿足m是md的月,且{d~m*{MD}}與{d`m*{MD}}無交集的md構成的集合。
具體的邏輯過程,與邏輯分析一致,只是把數學語言,轉化為計算機語言。
相關文章
- 設計不等於藝術。合理運用“藝術”解決設計問題
- 四個經典的SQL程式設計問題SQL程式設計
- 程式設計師C語言經典筆試題程式設計師C語言筆試
- 程式設計藝術家之路程式設計
- 智力題(程式設計師面試經典)程式設計師面試
- 程式設計師和程式藝術家程式設計師
- 專家審讀——《計算機程式設計藝術,卷1》計算機程式設計
- MySQL 計算生日問題MySql
- java經典程式設計題30道題,強烈推薦Java程式設計
- JavaScript經典筆試題JavaScript筆試
- Linux程式設計:程式同步問題之哲學家就餐問題Linux程式設計
- Java程式設計師面試時應注意的三個經典問題!Java程式設計師面試
- 50道經典的JAVA程式設計題(目錄)Java程式設計
- 一個經典程式設計面試題的“隱退”程式設計面試題
- 優秀的程式設計師就像藝術家?程式設計師
- 關於數字的經典SQL程式設計:連續範圍問題SQL程式設計
- CSS中越界問題經典解決方案CSS
- TCP通訊之經典問題解決TCP
- 《程式設計師程式設計藝術》程式設計師
- Java併發程式設計的藝術,解讀併發程式設計的優缺點Java程式設計
- 八數碼 經典問題
- 筆試題目——程式設計題筆試程式設計
- Go語言經典筆試題Go筆試
- 使用 JavaScript 解決經典爬樓梯問題JavaScript
- 每個程式設計師都應該讀《Unix程式設計藝術》程式設計師
- Java 經典程式設計題 (更新有延遲,但不會缺席)Java程式設計
- 經典n皇后問題java程式碼實現Java
- 面試題:web程式設計技術考試題庫(含答案)面試題Web程式設計
- 軟體測試經典測試題(4)
- 揹包問題的一道經典問題
- JAVA程式設計題-用java解決兔子問題Java程式設計
- 覆盤 PHP 經典面試問題解決過程:上臺階問題PHP面試
- JavaScript經典面試題詳解JavaScript面試題
- 經典程式設計引言程式設計
- 《JavaScript Dom程式設計藝術》讀書筆記(一)JavaScript程式設計筆記
- C++程式設計師必讀的經典著作C++程式設計師
- SQL language裡面的經典問題SQL
- 好程式設計師Java教程分享經典Java main方法面試題程式設計師JavaAI面試題