爱因斯坦思考题(多维穷举)

来源:互联网 发布:文艺知乎 编辑:程序博客网 时间:2024/05/21 07:47

爱因斯坦有一道思考题,据说世界上只有2%的人可以解答。

题目是这样的,据说有五个不同颜色的房间排成一排,每个房间里分别住着一个不同国籍的人,每个人都喝一种特定品牌的饮料,抽一种特定品牌的烟,养一种宠物,没有任意两个人抽相同品牌的香烟,或喝相同品牌的饮料,或养相同的宠物,问题是谁在养鱼作为宠物?为了寻找答案,爱因斯坦给出了十五条线索:

(1)、英国人住在红色的房子里;

(2)、瑞典人养狗作为宠物;

(3)、丹麦人喝茶;

(4)、绿房子紧挨着白房子,在白房子的左边;

(5)、绿房子的主人喝咖啡;

(6)、抽Pall Mall牌香烟的人养鸟;

(7)、黄色房子里的人抽Dunhill牌香烟;

(8)、住在中间那个房子里的人喝牛奶;

(9)、挪威人住在第一个房子里面;

(10)、抽Blends牌香烟的人和养猫的人相邻;

(11)、养马的人和抽Dunhill牌香烟的人相邻;

(12)、抽BlueMaster牌香烟的人和啤酒;

(13)、德国人抽Prince牌香烟;

(14)、挪威人和住在蓝房子的人相邻;

(15)、抽Blends牌香烟的人和喝矿泉水的人相邻。



     这道题是一道逻辑推理题,按照我们常人的思维就是拿出一张纸和一支笔,然后用假设法慢慢去推倒,但是也肯定要想很久。

于是我们想到了把推理的过程交给计算机去做,但是怎么做呢?计算机不会思考?

计算机不会思考,但是会计算,我们可以让计算机算出最终答案。

解题思路:1.穷举所有的组合结果 2.对结果进行判断

多维穷举算法:

整个算法的过程就是这两个核心的整合。

先建立数学模型:把问题抽象成一个二维数组,data[i][j],其中下标i代表大类(房子颜色、宠物等),j代表的是小类(绿色、蓝色、猫和狗等)。

然后通过多个递归排列组合算法进行穷举。

结果判断:

结果判断分成三大类,一类是组内关系绑定,第二类是组间关系绑定,第三类是过滤线索。

组内关系绑定是一个属性类型和一个值绑定,如:绿房子的主人喝咖啡,丹麦人喝茶。

组间关系绑定是一个组和相邻的组之间产生关系,如:挪威人住在蓝房子的人相邻,抽Blends牌香烟的人和喝矿泉水的人相邻。

过滤类线索是很难归为一二类绑定关系的判断条件,我们只能通过在穷举算法中过滤掉。



这个爱因斯坦思考题也是《算法的乐趣》上的,但是作者没有给代码,所以我只能够自己写,本来以为判断是难点,后来发现排列组合也是难点之一,就在百度上搜了一下排列组合的算法,然后自己先用算法穷举所有可能的组合,然后再用java里面的枚举类去处理组间组内的判断关系,整个过程没有任何的代码参考,完完全全是思考的结果,不像倒水问题和妖怪和尚过河有作者的C++代码。


所以这对我来说也是一个巨大的挑战,但是我最终战胜了困难,解出了这道题。

下面一张图是代码运行出现结果的图,我对照了书上的最终结果,完全正确:



将近两亿个枚举,我的算法不够好,用了接近15秒的时间才解出这道题。

下面是我的实现代码:

Test类是主类,用于启动程序

package 二维穷举爱因斯坦的思考题_待完成;public class Test {    public static int[] text = {0,1,2,3,4};    public static int count;    public static int judge1;    public static int judge2;    public static void main(String[] args) {        Test ts = new Test();        ts.initMain();    }    public void initMain(){    long time1 = System.currentTimeMillis();    Think tk = new Think();    tk.initThink(tk.data, 0, text.length);    long time2 = System.currentTimeMillis();    System.out.println("花费的时间:"+(time2 - time1));    System.out.println("多维穷举次数是:"+count);    }}

Think类是多维穷举算法实现类,用于穷举排列组合:

package 二维穷举爱因斯坦的思考题_待完成;public class Think{/** * 一维的分别是:房子颜色、国籍、饮料、香烟、宠物 * 二维排列随机排列组合检测 * red green white yellow blue  * 1.红 2.绿 3. 白 4.黄     5.蓝 * britain swedend norway germany denmark  * 1.英国2. 瑞典 3.挪威    4.德国      5.丹麦 * tea coffee milk    beer water  *  1.茶  2. 咖啡 3.牛奶   4.啤酒    5.水 * dog    cat   fish    bird     horse  *  1.狗    2. 猫    3.鱼      4.鸟          5.马 * pallmall bunhill blends bluemaster prince *     1        2      3       4         5 */public int[][] data = new int[5][5];//定义一个二维数组存放分组数据public static int[] norway = {2,1,4,5};public int j;public Think(){{for(int i=0;i<data.length;i++){for(int j=0;j<data[i].length;j++){data[i][j] = j+1;}}}}public static void initThink(int[] data[],int m,int n){//遍历1次int j = 0;if(m < n - 1){initThink(data, m + 1, n);for (int i = m + 1; i < n; i++) {             int t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;             initThink(data, m + 1, n);             t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;//             if( !(data[j][m] == 2 && data[j][m+1] == 3) ){//如果绿房子右边不是白房子就返回//return ;//}         }}else {for(int i=0;i<5;i++){try{if( data[j][i] == 2 && data[j][i+1] == 3 ){secondThink(norway, 0, norway.length,data);}}catch(Exception e){}}}}public static void secondThink(int[] norway,int m,int n,int[] data[]){//遍历第二次if(m < n - 1){secondThink(norway, m + 1, n,data);for (int i = m + 1; i < n; i++) {             int t = norway[m];             norway[m] = norway[i];             norway[i] = t;             secondThink(norway, m + 1, n,data);             t = norway[m];             norway[m] = norway[i];             norway[i] = t;         }}else{for(int k=0;k<5;k++){if( k == 0 ){data[1][k] = 3;}else{data[1][k] = norway[k-1];}}ThreeThink(data, 0, data.length);}}public static void ThreeThink(int[] data[],int m,int n){//遍历第三次int j = 2;if(m < n - 1){ThreeThink(data, m + 1, n);for (int i = m + 1; i < n; i++) {             int t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;             ThreeThink(data, m + 1, n);             t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;         }}else{if( data[j][2] != 3 ){//中间房子的主人喝牛奶,如果中间房主不喝牛奶就返回return ;            }FourThink(data, 0, data.length);}}public static void FourThink(int[] data[],int m,int n){//遍历第四次int j = 3;if(m < n - 1){FourThink(data, m + 1, n);for (int i = m + 1; i < n; i++) {             int t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;             FourThink(data, m + 1, n);             t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;         }}else{FiveThink(data, 0, data.length);}}public static void FiveThink(int[] data[],int m,int n){//遍历第五次int j = 4;if(m < n - 1){FiveThink(data, m + 1, n);for (int i = m + 1; i < n; i++) {             int t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;             FiveThink(data, m + 1, n);             t = data[j][m];             data[j][m] = data[j][i];             data[j][i] = t;         }}else{Test.count++;if( Tool.judge1(data) && Tool.judge2(data) ){System.out.println("算法推理成功!结果是:");Tool.print(data);System.out.println();Tool.printCorrect();}}}public static void printResult(int[] data) {        for (int i = 0; i < data.length; i++) {            System.out.print(data[i]+" ");        }        System.out.println();    }public static void print(int[][] data){for(int i=0;i<data.length;i++){for(int j=0;j<data[0].length;j++){System.out.print(data[i][j] +" ");}System.out.println();}}}

Tool类是判断实现类,实现组间组内绑定关系的条件判断:

package 二维穷举爱因斯坦的思考题_待完成;public class Tool {public static int[][] ans = {{4,5,1,2,3},{3,5,1,4,2},{5,1,3,2,4},{2,5,4,3,1},{2,3,1,5,4}};public static int count;public static boolean judge1(int[] data[]){for(int i=0;i<InGroupRelation.values().length;i++){int ft = InGroupRelation.values()[i].firstType;int st = InGroupRelation.values()[i].secondType;int fv = InGroupRelation.values()[i].firstvalue;int sv = InGroupRelation.values()[i].secondvalue;for(int j=0;j<5;j++){if( data[ft][j] == fv && data[st][j] == sv ){//判断排列组合,成立就跳出,不成立就返回falsebreak;}if(j == 4){return false;}}}return true;}public static boolean judge2(int[] data[]){for(int i=0;i<OutOfFroupRelation.values().length;i++){int ft = OutOfFroupRelation.values()[i].firstType;int st = OutOfFroupRelation.values()[i].secondType;int fv = OutOfFroupRelation.values()[i].firstValue;int sv = OutOfFroupRelation.values()[i].secondValue;for(int j=0;j<5;j++){try{if( data[ft][j] == fv && (data[st][j+1] == sv || data[st][j-1] == sv) ){break;}}catch(Exception e){}if(j == 4){return false;}}}return true;}public static void print(int[] data[]){for(int i=0;i<data.length;i++){for(int j=0;j<data[i].length;j++){System.out.print(data[i][j]+" ");}System.out.println();}}public static boolean check(int[] data[]){for(int i=0;i<data.length;i++){for(int j=0;j<data[i].length;j++){if(data[i][j] != ans[i][j]){return false;}}}count++;return true;}public static void printCorrect(){System.out.println("4:黄色5:蓝色1:红色2:绿色3:白色");System.out.println("3:挪威5:丹麦1:英国4:德国2:瑞典");System.out.println("5:水 1:茶3:牛奶2:咖啡4:啤酒");System.out.println("2:猫5:马4:鸟3:鱼1:狗   ");System.out.println("2:Bunhill3:Blends1:PallMall5:Prince4:BlueMaster");}}

InGroupRelation类给定组内关系:

package 二维穷举爱因斯坦的思考题_待完成;public enum InGroupRelation {red_england(0,1,1,1),swedend_dog(1,3,2,1),danmark_tea(1,2,5,1),green_coffee(0,2,2,2),pallmall_bird(4,3,1,4),yellow_dunhill(0,4,4,2),bluemaster_beer(4,2,4,4),germany_prince(1,4,4,5);public int[][] data = new int[5][5];public int firstType,secondType,firstvalue,secondvalue;InGroupRelation(int firstType,int secondType,int firstvalue,int secondvalue){this.firstType = firstType;this.secondType = secondType;this.firstvalue = firstvalue;this.secondvalue = secondvalue;}}

OutOfFroupRelation类给定组间关系:

package 二维穷举爱因斯坦的思考题_待完成;public enum OutOfFroupRelation {blends_cat(4,3,3,2),horse_dunhill(3,4,5,2),norway_blue(1,0,3,5),blends_drink_water(4,2,3,5);public int firstType,secondType,firstValue,secondValue;OutOfFroupRelation(int firstType,int secondType,int firstValue,int secondValue){this.firstType = firstType;this.secondType = secondType;this.firstValue = firstValue;this.secondValue = secondValue;}}
这就是我这几天做的多维穷举算法。




我觉得学习算法一定要思路清晰,头昏脑涨的时候最好不要学习算法,因为那时候效率很低,往往学不到东西,还浪费时间。



作者博客:

http://blog.csdn.net/orbit/article/details/6994575


2 0
原创粉丝点击