用程序解密爱因斯坦经典难题_weixin_30718391的博客-程序员秘密

技术标签: c#  

爱因斯坦曾在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个解答。

 

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

 

转载于:https://www.cnblogs.com/crazy-dave/archive/2012/11/17/Puzzle.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30718391/article/details/96377788

智能推荐

配置Eclipse for Java 9_ArvinWoo的博客-程序员秘密

原文:Configure Eclipse for Java 9 今天 安装 JDK9 之后, 配置到 Eclipse 总是 提示: Target is not a JDK root. System library was not found.配置Eclipse for Java 9本wiki页面解释了如何使用Java 9配置Eclipse来启动。这也解释了如何为Eclipse 4.7安装

spring aop处理系统操作日志和异常日志,保存到数据库_shanger_1216的博客-程序员秘密

package com.env.web.zj;import java.text.SimpleDateFormat;import java.util.Arrays;import java.util.Date;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import org.apa...

遇到一个奇怪的问题——关于VS2013、VS2015中字符集(多字节字符集和Unicode字符集)的选择_vs2015 wchar_t_阿喵不是猫的博客-程序员秘密

喵哥最近在写一个控制程序和被控制程序,脑子进水般的同时用了VS2013和VS2015,一个程序对应一个,最开始,两者都采用Unicode字符集,但是控制程序发出的指令不能被被控制程序接收,绞尽脑汁的思考,才想到可能是由于字符集的原因——因为在被控制程序接收指令的地方有这样一段代码:/*****************接收ShellExecute的消息*********************...

jQuery入门,jq第一天学习_jquery是写好的代码吗_XiaLuoCXY的博客-程序员秘密

一.jQuery介绍其本质就是第三方的框架:别人写好的js代码文件,好处就是效率高,坏处就是处理bug比较麻烦。、官网:jQueryjQuery文件下载:https://code.jquery.com/jquery-1.12.4.min.js官方文档传送门:jQuery API 中文文档 | jQuery API 中文在线手册 | jquery api 下载 | jquery api chm二.不同版本的区别1.jQuery版本有很多,分为1.x 2.x 3 1.x版...

jenkins重启 linux_jenkins在Linux 下安装部署_weixin_39788572的博客-程序员秘密

这里介绍两种方法,一种方法将最新版jenkins加入到yum源,另外一种是下载指定版本的rpm包系统centos6自带jdk1.7一 安装jenkinswget -O :下载并以不同的文件名保存yum的repo中默认没有Jenkins,需要先将Jenkins存储库添加到yum repos,执行下面的命令:wget -O /etc/yum.repos.d/jenkins.repo https://p...

Sql养成一个好习惯是一笔财富_192517什么意思_厦门德仔的博客-程序员秘密

我们做软件开发的,大部分人都离不开跟数据库打交道,特别是erp开发的,跟数据库打交道更是频繁,存储过程动不动就是上千行,如果数据量大,人员流动大,那么我么还能保证下一段时间系统还能流畅的运行吗?那么还能保证下一个人能看懂我么的存储过程吗?那么我结合公司平时的培训和平时个人工作经验和大家分享一下,希望对大家有帮助。要知道sql语句,我想我们有必要知道sqlserver查询分析器怎么执行我么sql

随便推点

ubuntu下运行python提示: no module named pip_moonuke的博客-程序员秘密

我之前装了pip啊。后来又装了几遍网上各种方法都不行。我按知乎的说法检查 cd /usr/local/lib/python3.5/dist-packages/ 文件夹下发现没有pip文件夹也就是没装python3.5的pip??所以运行apt-get install python3-pip(try)(其他的一些配置)pip3 install numpypip3 install scipypip3 ...

java半数集问题_半数集问题与动态规划_刘一帝的博客-程序员秘密

首先给出如下的半数集问题给定一个自然数n,由n 开始可以依次产生半数集set(n)中的数如下。(1) n∈set(n);(2) 在n 的左边加上一个自然数,但该自然数不能超过最近添加的数的一半;(3) 按此规则进行处理,直到不能再添加自然数为止。例如,set(6)={6,16,26,126,36,136}。半数集set(6)中有6 个元素。注意半数集是多重集。首先分析题目意思:已知一个数n,要整数...

抢茅台拼的不是速度,是策略!_王 炸的博客-程序员秘密

抢茅台拼的不是速度,是策略!最近翻朋友圈发现一个抢茅台脚本,正好也有朋友过来问我能否写个程序,帮他抢茅台。朋一瓶茅台的利润在400-1100,惊呆了,竟然这么赚钱!!!不取1100最高利...

webx问题集_alvinice的博客-程序员秘密

 1.方法修饰符不对出现的错误com.alibaba.citrus.service.pipeline.PipelineException Failed to invoke Valve[#2/3, level 3]: com.alibaba.citrus.turbine.pipeline.valve.PerformTemplateScreenValve#a28815:PerformTempl...

1127 ZigZagging on a Tree (将层序遍历按z型输出, 2种方法,1.下标法构建满足题意的层序条件 2.构建树 BFS遍历层序)_月下独喝的博客-程序员秘密

1127 ZigZagging on a Tree (30 分)Suppose that all the keys in a binary tree are distinct positive integers. A unique binary tree can be determined by a given pair of postorder and inorder traversal s...