爱因斯坦曾在20世纪初提过一个经典问题,据说世界上有98%的人回答不出来
问题:在一条街上,有5座房子,喷了5中颜色。每个房子住着不同国籍的人。每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物。
问题是:谁养鱼?
提示:1.英国人住红色房子
2.瑞典人养狗
3.丹麦人喝茶
4.绿色房子在白色房子左边
5.绿房子主人喝咖啡
6.抽PallMall香烟的人养鸟
7.黄色房子的主人抽Dunhill香烟
8.住在中间房子的人喝牛奶
9.挪威人住第一间房
10.抽Bleeds香烟的人住在养猫的人隔壁
11.养马的人住抽Dunhill香烟的人隔壁
12.抽BlueMaster的人喝啤酒
13.德国人抽Prince香烟
14.挪威人住蓝色房子隔壁
15.抽Bleeds香烟的人有一个喝水的邻居

作为程序员我并不想用推理的方式解密,而是通过程序来计算出答案,大致思路是,得到所有可能情况的组合,遍历所有的组合,找到满足所有条件的组合。这样养鱼的人就一目了然,难点就在于如何把这些条件转换成计算机可以识别的代码。(注:程序使用语言C#)

假设5个房子从左到右的编号为1,2,3,4,5,每个房子拥有一个颜色,而5个国籍的人分别和5个房子绑定,再加上人本身的3个习惯(抽烟,喝,宠物),所以可以看做每个国籍的人都拥有5个属性,构造一个“人”的类

    class Person{public int index { get; set; }public Color color { get; set; }public Drink drink { get; set; }public Cigarette cigarette { get; set; }public Pet pet { get; set; }}

index表示这个人的位置,也就是他在第几间房子里,index的范围在1-5之间,color表示这个房子的颜色,剩下的4个属性用枚举定义

 enum Color{Red,White,Yellow,Green,Blue}enum Drink{Tea,Milk,Coffee,Bear,Water}enum Cigarette{PallMall,Dunhill,Bleeds,BlueMaster,Prince}enum Pet{Fish,Dog,Bird,Cat,Horse}

这样一来,由这5个属性就可以组合成5^5=3125种不同的情况,接下来定义一个“人”的集合,这个集合包括所有可能的情况,用5个循环嵌套赋值

 static void Foreach(ref List<Person> list){for (int i_index = 0; i_index < 5; i_index++){for (int i_color = 0; i_color < 5; i_color++){for (int i_drink = 0; i_drink < 5; i_drink++){for (int i_cig = 0; i_cig < 5; i_cig++){for (int i_pet = 0; i_pet < 5; i_pet++){list.Add(new Person(){index = i_index + 1,color = (Color)i_color,drink = (Drink)i_drink,cigarette = (Cigarette)i_cig,pet = (Pet)i_pet});}}}}}}

观察条件就会发现其实有些情况根本就不会存在,比如绿房子主人喝咖啡所以如果某人的Color属性等于Green的话那么他的Drink属性一定等于Coffee,所以Color=Green和Drink=Bear或其他Drink这些组合都是不存在的。也就是说绿房主人不可能喝除咖啡以外的饮料,反过来说,喝咖啡的人也不可能不住绿房子,再看看这些类似的条件:5.绿房子主人喝咖啡6.抽PallMall香烟的人养鸟 7.黄色房子的主人抽Dunhill香烟 8.住在中间房子的人喝牛奶 12.抽BlueMaster的人喝啤酒 9.挪威人住第一间房和14.挪威人住蓝色房子隔壁由9和14这两2个条件可以推导出2号房的颜色是蓝色,根据这些约束条件画了个草图

连线表示2个属性是关联的,通过这个可以在开始构造的集合里面,排除掉这些不可能存在的情况,比如2-Blue,则表示不可能存在index=2并且color!=Blue的情况,或者

color=Blue并且index!=2的情况,依次类推,观察草图的连线,将不符合条件的情况全部从集合里排除

            //9.挪威人住第一间房//14.挪威人住蓝色房子隔壁list = list.Except(list.Where(p => (p.index == 2 && p.color != Color.Blue) || (p.index != 2 && p.color == Color.Blue))).ToList();//8.住在中间房子的人喝牛奶list = list.Except(list.Where(p => (p.index == 3 && p.drink != Drink.Milk) || (p.index != 3 && p.drink == Drink.Milk))).ToList();//7.黄色房子的主人抽Dunhill香烟list = list.Except(list.Where(p => (p.color == Color.Yellow && p.cigarette != Cigarette.Dunhill) || (p.color != Color.Yellow && p.cigarette == Cigarette.Dunhill))).ToList();//5.绿房子主人喝咖啡list = list.Except(list.Where(p => (p.color == Color.Green && p.drink != Drink.Coffee) || (p.color != Color.Green && p.drink == Drink.Coffee))).ToList();//12.抽BlueMaster的人喝啤酒list = list.Except(list.Where(p => (p.drink == Drink.Bear && p.cigarette != Cigarette.BlueMaster) || (p.drink != Drink.Bear && p.cigarette == Cigarette.BlueMaster))).ToList();//6.抽PallMall香烟的人养鸟list = list.Except(list.Where(p => (p.cigarette == Cigarette.PallMall && p.pet != Pet.Bird) || (p.cigarette != Cigarette.PallMall && p.pet == Pet.Bird))).ToList();

这个时候list的数量已经由之前的3125骤减至227了,等等,还有个约束条件绿色房子在白色房子左边也就是说index属性等于1并且color属性等于white的情况是不可能出现的,因为index=1&&color=white表示白色房子是第一间房子,这样它的左边就不可能有房子了,进一步过滤

 //4.绿色房子在白色房子左边list = list.Except(list.Where(p => p.index == 1 && p.color == Color.White)).ToList();

暂时就只发现这么多了, 如果你还可以推理出更多的过滤条件就再好不过了,约束越多,list的数量就会越少,接下来的遍历就会越轻松。最终目的是要得到满足所有条件的5个国籍人的组合,每一个组合就是一个解答,再来分析1.英国人住红色房子 2.瑞典人养狗 3.丹麦人喝茶 9.挪威人住第一间房 13.德国人抽Prince香烟
英国人住红色房子,那么我们只要从集合里面过滤出color=red的就可以了,值得注意的是瑞典人养狗,那么英国人就不可能养狗了,同理英国人也不可能喝茶了,而瑞典人的房子也不可能是红色的了……以此类推,

            List<Person> England = list.Where(p => p.index != 1 && p.color == Color.Red && p.pet != Pet.Dog && p.drink != Drink.Tea && p.cigarette != Cigarette.Prince).ToList();List<Person> Sweden = list.Where(p => p.index != 1 && p.color != Color.Red && p.pet == Pet.Dog && p.drink != Drink.Tea && p.cigarette != Cigarette.Prince).ToList();List<Person> Denmark = list.Where(p => p.index != 1 && p.color != Color.Red && p.pet != Pet.Dog && p.drink == Drink.Tea && p.cigarette != Cigarette.Prince).ToList();List<Person> Norway = list.Where(p => p.index == 1 && p.color != Color.Red && p.pet != Pet.Dog && p.drink != Drink.Tea && p.cigarette != Cigarette.Prince).ToList();List<Person> Germany = list.Where(p => p.index != 1 && p.color != Color.Red && p.pet != Pet.Dog && p.drink != Drink.Tea && p.cigarette == Cigarette.Prince).ToList();

到这里得到了满足条件的英国人,瑞典人,丹麦人,挪威人和德国人的集合,他们的数量分别是18,12,18,7,18,共18*12*18*7*18=489888种组合,接下来的工作就是遍历每个组合,在剩下的条件里去验证。遍历5个国籍人的集合,从每个集合里面取一个人出来,把这5个人存到一个List<Person>里面,表示一个组合,如果验证成功则表示这个组合是解答

static List<List<Person>> Work(List<Person> England, List<Person> Sweden, List<Person> Denmark, List<Person> Norway, List<Person> Germany){List<List<Person>> Result = new List<List<Person>>();int count = 1;long total = England.Count * Sweden.Count * Denmark.Count * Norway.Count * Germany.Count;foreach (var e in England){foreach (var s in Sweden){foreach (var d in Denmark){foreach (var n in Norway){foreach (var g in Germany){List<Person> l = new List<Person>();l.Add(e);l.Add(s);l.Add(d);l.Add(n);l.Add(g);if (TestDistinct(l) && Test(l)){Result.Add(l);}Console.WriteLine(string.Format("测试第{0}/{1}个结果,百分比:{2}%", count, total, count * 100 / total));count++;}}}}}           return Result;}

通过剩下的条件验证:

static bool Test(List<Person> list){//4.绿色房子在白色房子左边if (list.Single(p => p.color == Color.Green).index >= list.Single(p => p.color == Color.White).index) return false;//10.抽Bleeds香烟的人住在养猫的人隔壁var p1 = list.Single(p => p.cigarette == Cigarette.Bleeds);var p2 = list.Single(p => p.pet == Pet.Cat);if (p1.index != p2.index - 1 && p1.index != p2.index + 1) return false;//11.养马的人住抽Dunhill香烟的人隔壁var p3 = list.Single(p => p.pet == Pet.Horse);var p4 = list.Single(p => p.cigarette == Cigarette.Dunhill);if (p3.index != p4.index - 1 && p3.index != p4.index + 1) return false;//15.抽Bleeds香烟的人有一个喝水的邻居var p5 = list.Single(p => p.cigarette == Cigarette.Bleeds);var p6 = list.Single(p => p.drink == Drink.Water);if (p5.index != p6.index - 1 && p5.index != p6.index + 1) return false;return true;}

先从集合中找到相应特征的2个人,通过比较2个人的index属性判断是否为邻居关系。当然在验证之前必须要保证每个人拥有的属性是唯一的,因为之前只是判断了每个国籍的人可能出现的所有情况,并没有组合起来判断,假设在这个集合中有2个人的color属性都是Green,这种情况肯定是不存在的,所以在验证前就应该排除掉,像这样

if (list.Count(p => p.index == 1) != 1) return false;if (list.Count(p => p.index == 2) != 1) return false;if (list.Count(p => p.index == 3) != 1) return false;if (list.Count(p => p.index == 4) != 1) return false;if (list.Count(p => p.index == 5) != 1) return false;if (list.Count(p => p.color == Color.Red) != 1) return false;if (list.Count(p => p.color == Color.White) != 1) return false;if (list.Count(p => p.color == Color.Yellow) != 1) return false;
……
return true;

最后,将所有满足条件的List<Person>放到一起,输出结果:

if (Result.Count == 0)Console.WriteLine("未得到任何结果!");else{Console.WriteLine("得到" + Result.Count + "个结果");for (int i = 0; i < Result.Count; i++){Console.WriteLine("******************第" + (i + 1) + "个解答******************");Console.WriteLine(string.Format("英国人住第{0}间房子,颜色:{1},喝{2},抽{3},养{4}", Result[i][0].index, Result[i][0].color, Result[i][0].drink, Result[i][0].cigarette, Result[i][0].pet));Console.WriteLine(string.Format("瑞典人住第{0}间房子,颜色:{1},喝{2},抽{3},养{4}", Result[i][1].index, Result[i][1].color, Result[i][1].drink, Result[i][1].cigarette, Result[i][1].pet));Console.WriteLine(string.Format("丹麦人住第{0}间房子,颜色:{1},喝{2},抽{3},养{4}", Result[i][2].index, Result[i][2].color, Result[i][2].drink, Result[i][2].cigarette, Result[i][2].pet));Console.WriteLine(string.Format("挪威人住第{0}间房子,颜色:{1},喝{2},抽{3},养{4}", Result[i][3].index, Result[i][3].color, Result[i][3].drink, Result[i][3].cigarette, Result[i][3].pet));Console.WriteLine(string.Format("德国人住第{0}间房子,颜色:{1},喝{2},抽{3},养{4}", Result[i][4].index, Result[i][4].color, Result[i][4].drink, Result[i][4].cigarette, Result[i][4].pet));}}

好了,到这里程序已经编写完成了,激动人心的时刻来了,开始运行,遍历所有组合大约用时2分钟,答案就是

共7个解答。

这里得到了7个解答,是因为条件4的判断是绿色房子在白色房子左边,而不一定是非要挨着的,如果绿色房子紧挨在白色房子左边,那么只需把代码改成

if (list.Single(p => p.color == Color.Green).index != list.Single(p => p.color == Color.White).index - 1) return false;

最后得到的结果是只有一个的。

这道题目,我后来用c++重写过,用的更简洁的思路,不需要任何推理。

注:该文章为作者maidou0921原创,csdn系首发,转载请注明出处http://blog.csdn.net/maidou0921/article/details/8192662。

用程序解密爱因斯坦经典难题相关推荐

  1. 用程序解密爱因斯坦经典难题(C++)

    爱因斯坦曾在20世纪初提过一个经典问题,据说世界上有98%的人回答不出来 问题:在一条街上,有5座房子,喷了5中颜色.每个房子住着不同国籍的人.每个人喝不同的饮料,抽不同品牌的香烟,养不同的宠物. 问 ...

  2. linux一句话问答(网络无关篇+网络相关篇+程序开发篇+经典图书)

    一句话问答(网络无关篇+网络相关篇+程序开发篇+经典图书) --------------------------目录-网络无关篇-目录-------------------------- 0001 修 ...

  3. java微信小程序解密AES/CBC/PKCS7Padding

    摘要:微信小程序解密建议使用1.6及以上的环境使用maven下载jar包org.bouncycastlebcprov-jdk15on1.55加密类代码importorg.bouncycastle.jc ...

  4. 微信小程序解密用户信息--java解密

    1.wx.login()方法获取code,通过code换取session_key 2.wx.getUserInfo()方法获取: encryptedData 包括敏感数据在内的完整用户信息的加密数据 ...

  5. 使用Prolog编程语言解决爱因斯坦斑马难题

    使用Prolog编程语言解决爱因斯坦斑马难题 原文链接: http://www.cnblogs.com/lcomplete/p/3192488.html 目前商业上广泛使用的编程语言多是命令式或函数式 ...

  6. 西门子1200-1500博途追款锁机软件程序例程,经典程序编程及到期催款锁机,采用SCL语言编程子程序,内含物料运输顺序控制

    西门子1200-1500博途追款锁机软件程序例程,经典程序编程及到期催款锁机,采用SCL语言编程子程序,内含物料运输顺序控制,运料车自动装卸料控制,展厅人数控制,风机运行监控,卫生间定时冲水,冒泡排序 ...

  7. 程序猿每日经典英语必读

    程序猿每日经典英语必读: %%%%%%%%%%%%%%%%%%%%% 每日一句  20180528 一次只做一件事,但要做到最好. Do one thing at a time, but do wel ...

  8. 夸奖php工程师,关于程序猿的经典笑话

    原标题:关于程序猿的经典笑话 生活不仅只是敲代码,还有--调bug. 这个世界只有十种人:懂二进制的和不懂二进制的. 两程序员向同一个MM求爱,MM说:"去环游世界后再来找我!"码 ...

  9. 程序员必读经典书籍 (转)

    原文地址:http://iteye.blog.163.com/blog/static/186308096201271931628953/ 1.<代码大全> 史蒂夫·迈克康奈尔 " ...

最新文章

  1. Scala安装时的坑
  2. 数字图像处理的三个层次
  3. mysql的引擎讲解
  4. 设置 Nuget 本地源、在线私有源、自动构建打包
  5. 阿里云高级技术专家白常明谈《边缘云的技术挑战和应用创新》
  6. hdu2544 最短路-Floyd算法
  7. opencv 计数后不动了 训练模型时_用OpenCV,深度学习和Python进行年龄识别
  8. sqlserver shiwu
  9. 在Winform开发框架中实现对数据库的加密支持(转)
  10. hadoop 2.7.3 源码编译教程
  11. 中心药库管理系统 v6.85 是什么
  12. 索尼电视总出现Android,索尼电视紧急撤回安卓8.0固件包:N多用户无法连接Wi-Fi...
  13. C语言1加到100的递归方法,递归调用实现1到100的累加
  14. Python——河神小游戏
  15. 新手期货开户的时候需要准备什么呢?
  16. python 3d图形控件 可交互_Python和Matplotlib:在Jupyter Noteb中使3D plot交互
  17. java 开源 cms系统_基于Java的开源CMS系统选择(转)
  18. 使用mp4v2封装mp4
  19. 三参数或七参数计算工具使用帮助
  20. Java PDF转HTML、Word、图片、SVG、XPS、 PDF/A等格式文件

热门文章

  1. 南京邮电大学matlab实验报告,南邮通信原理 实验二 BPSK_BDPSK 传输系统综合实验 (1)...
  2. 李沐《动手学深度学习》第二版 pytorch笔记1 环境搭建
  3. Internet Download Manager(IDM)网页下载浮动条不出现的问题记录
  4. linux中FT4232H设备驱动添加,USB转4串口芯片FT4232HL,FT4232HQ
  5. 130个免费 微信小程序源码分享
  6. python获取a股数据_python获取A股数据列表的例子
  7. 国际中的steam教育发展与启示
  8. WMS系统后端开发-货位管理
  9. Android实战开发-Kotlin教程(布局篇 3.1)
  10. 2019学硕计算机分数,2019计算机考研408分数要求以及复习攻略?