题目描述:在52张扑克牌中(去掉大小王),随机抽取4张牌,找到所有可能的情况和解。

前言

博主曾在网上看到有很多关于24点的算法,但很多都是没有找全所有表达式,要么就是没有去重,而且搜索的时间过长,有些慢的要半个小时才能得到结果。所以经过我的不懈努力,经过几天的研究,目前我的这个24点的算法能够高效的找出所有解的情况。经过测试,平均在0.1几秒左右就可以找到所有情况。

算法分析

本算法采用穷举的思路,对所有数字和操作符进行组合,从而找到所有的情况。
首先想想,如果是人来计算24点,应该怎么计算?比如1,2,3,4.  首先我们可能会思考一下,然后得出结果(1+2+3)*4=24。
对,没错,那么现在对上面思考的过程进行仔细分析一下,24是怎么来的?6*4=24,对吧,那么6是怎么来的?6=1+2+3。
现在有个问题,虽然我们一眼就能看出1+2+3=6,但是这个过程还是经过了2步计算,首先我们计算1+2=3,然后在计算3+3=6,然后再计算6*4=24,对吧。也就是说我们会从这4个数中选择2个数来进行计算,然后得到一个结果,在将这个结果与剩下的2个数中选择一个数来计算,简单来说就是就是每次我们只会计算2个数,当然这2个数可能有好几种不同的运算。
下面我们再来看看13,13,13,12这几个数,咋一看这个好像没有上面的数字好算了,不能一下得出结果,这里给出3种解(当然不止3种):(13+13)/(13/12) 和 13-((13/13)-12) 和 (13+12)-(13/13),我们再来想想,每次只运算2个数,直到得出结果。
OK,明白了上面,我们再来分析下计算机是怎么运算的。其实也是同样的思路,每次只运算2个数,然后将结果拿去进行下一次运算。那么现在我们有4个数,如1,2,3,4. 在这4个数位置不变的情况下, 第一次选择2个数来计算,有哪些情况?如果位置不变,那只有3种情况:(1-2)-3-4, 1-(2-3)-4, 1-2-(3-4)(先假设操作符都是-号)。那第2次计算呢,有下面几种情况,如果第1次是(1-2)-3-4,则第2次可能是((1-2))-3-4和(1-2)-(3-4),如果第1次是1-(2-3)-4,则第2次可能是(1-(2-3))-4和1-(2-(3-4)),如果第1次是1-2-(3-4),则第2次可能是(1-2)-(3-4)和1-(2-(3-4)),
然后第3次运算就只剩2个数了,在计算这2个数的结果是不是24就行了。
上面我们假设的是操作符都是-号,但实际情况可能有很多种,现在我们还是在这4个数位置不变的情况下,再来改变操作符,即每次2个数进行运算的时候,有4种情况,即1-2,  1+2,  1*2,  1/2,(在这2个数位置不变的情况下)。那么下次进行计算的时候呢?同样有4种情况(+-*/),最后一次计算(第3次)同理。这样我们就找到了在这4个数位置不变的情况下的所有解的情况。
那么接下来再考虑这4个数位置变化的情况,即1,2,3,4  可以变成4,3,2,1   和1,4,2,3等。同理,当位置变化时,我们按照上面的方法重新计算。这样就可以找出每一种情况啦。这里用的是排列组合,如1,2,3,4有24种不同的排列。
以下是具体代码:
public class Expression {//用来判断是否有解private static boolean flag = false;//存放操作符private static char[] operator = { '+', '-', '*', '/' };//存放所有结果private static ArrayList<String> results = new ArrayList<String>();public static void main(String[] args) {//所有正确的解的个数int rightCount = 0;//所有情况的个数int allCount = 0;//存放4个数字double[] number = new double[4];long startTime = System.currentTimeMillis();/* 第1次去重,过滤掉可能产生的重复的情况,比如1,2,3,4  和4,3,2,1因为后面是通过排列组合来找出所有情况,1,2,3,4可以组合成4,3,2,1这样就重复了,这里为了过滤掉这些重复的*/for (int i = 1; i <= 13; i++) {for (int j = i; j <= 13; j++) {for (int k = j; k <= 13; k++) {for (int m = k; m <= 13; m++) {number[0] = i;number[1] = j;number[2] = k;number[3] = m;//由于过滤掉重复的,这里重新计算重复的次数(在计算所有情况的个数时需要)//如果你不需要计算所有情况的个数,可以不需要int count = times(i, j , k ,m);allCount += count;duplicateRemoval(number);//判断是否有解if(flag == true){rightCount += count;flag = false;}}}}}long endTime = System.currentTimeMillis();for (int i = 0; i < results.size(); i++) {System.out.println(results.get(i));}System.out.println("共耗费时间:" + (endTime - startTime) + "ms");System.out.println("所有可能的个数:" + allCount);System.out.println("有解的个数:" + rightCount);System.out.println("有解的几率" + (double)rightCount/allCount);}/*** 由于最开始过滤掉一部分重复的情况,但这些重复情况是存在的* 这里是为了计算每种重复情况有多少次数,如当3张牌相同,另一张牌不同时,* 如3,3,3,5  抽牌时有16种不同的情况(根据花色的不同)* 而在计算时为了去重把这些过滤掉了,这里是为了重新计算这些情况* 如果你不需要计算所有情况的个数,可以不需要次方法*/private static int times(int i,int j,int k,int m){//判断有多少种重复Set<Integer> set = new HashSet<Integer>();set.add(i);set.add(j);set.add(k);set.add(m);if(set.size() == 1){//当4个数的数字全部一样时(不同花色),只可能有一种组合return 1;} else if(set.size() == 3){//当4个数中,有两个数相同,其余的数都不相同时return 96;} else if(set.size() == 4){//当4个数全部不同时return 256;} else{if((i == j && k == m)||(i == k && j == m)||(i == m && k == j)){//当4个数中,两两相同时return 36;} else {//当4个数中有三个数相同,另外一个数不同时return 16;}}}/*** 第2次去重,由于排列组合可能导致数字组合的重复* 这里进行第2次过滤,只计算给定4个数的所有不同的排列*/private static void duplicateRemoval(double[] number){Map<Double, Integer> map = new HashMap<Double, Integer>();//存放数字,用来判断输入的4个数字中有几个重复的,和重复的情况for (int i = 0; i < number.length; i++) {if(map.get(number[i]) == null){map.put(number[i], 1);} else {map.put(number[i], map.get(number[i]) + 1);}}if(map.size() == 1){//如果只有一种数字(4个不同花色的),此时只有一种排列组合,如6,6,6,6calculation(number[0], number[1],number[2],number[3]);} else if(map.size() == 2){//如果只有2种数字,有2种情况,如1,1,2,2和1,1,1,2int index = 0;//用于数据处理int state = 0;//判断是那种情况for (Double key : map.keySet()) {if(map.get(key) == 1){//如果是有1个数字和其他3个都不同,将number变为 number[0]=number[1]=number[2],//将不同的那个放到number[3],方便计算number[3] = key;state = 1;} else if(map.get(key) == 2){//两两相同的情况,将number变为number[0]=number[1],number[2]=number[3]的情况,方便计算number[index++] = key;number[index++] = key;} else {number[index++] = key;}}//列出2种情况的所有排列组合,并分别计算if(state == 1){calculation(number[3], number[1], number[1], number[1]);calculation(number[1], number[3], number[1], number[1]);calculation(number[1], number[1], number[3], number[1]);calculation(number[1], number[1], number[1], number[3]);}if(state == 0){calculation(number[1], number[1], number[3], number[3]);calculation(number[1], number[3], number[1], number[3]);calculation(number[1], number[3], number[3], number[1]);calculation(number[3], number[1], number[1], number[3]);calculation(number[3], number[3], number[1], number[1]);calculation(number[3], number[1], number[3], number[1]);}} else if(map.size() == 3){//有3种数字的情况int index = 0;for (Double key : map.keySet()) {if(map.get(key) == 2){//将相同的2个数字放到number[2]=number[3],方便计算number[2] = key;number[3] = key;} else {number[index++] = key;}}//排列组合,所有情况calculation(number[0], number[1], number[3], number[3]);calculation(number[0], number[3], number[1], number[3]);calculation(number[0], number[3], number[3], number[1]);calculation(number[1], number[0], number[3], number[3]);calculation(number[1], number[3], number[0], number[3]);calculation(number[1], number[3], number[3], number[0]);calculation(number[3], number[0], number[1], number[3]);calculation(number[3], number[0], number[3], number[1]);calculation(number[3], number[1], number[0], number[3]);calculation(number[3], number[1], number[3], number[0]);calculation(number[3], number[3], number[0], number[1]);calculation(number[3], number[3], number[1], number[0]);} else if(map.size() == 4){//4个数都不同的情况getNumber(number);}}/*** 排列组合,用来处理4个数都不同的情况* 如1,2,3,4  可以转化为1,3,2,4   2,3,1,4    1,4,2,3等* 并计算每种的结果*/public static void getNumber(double[] number){for (int i = 0; i < 4; i++) {for (int j = 0; j < 4; j++) {if(i == j){continue;}for (int k = 0; k < 4; k++) {if(k == j || k == i){continue;}for (int m = 0; m < 4; m++) {if(m == k || m == j || m == i){continue;}calculation(number[i], number[j], number[k], number[m]);}}}}}/*** 给定4个数,当这4个数位置不变时,只改变操作符号,计算所有的可能性* 如1+2+3+4  ,1*2*3*4 , 1-2+3*4 等* 如果能得到24点,就将表达式添加到结果集*/public static boolean calculation(double num1, double num2, double num3, double num4){for (int i = 0; i < 4; i++) {/*   第一次计算,保存此时的操作符和计算结果此时有3中情况,相当于从4个数中选择2个相邻的数来计算如(1-2)-3-4, 1-(2-3)-4, 1-2-(3-4)则保存此时第一次计算的结果和操作符*/char operator1 = operator[i];//根据操作符,先计算第1,2两个数,如输入数字是1,2,3,4  则计算1+2(1-2,1*2,1/2等),//这里通过循环来改变操作符,下同double firstResult = calcute(num1, num2, operator1);//根据操作符,先计算第2,3两个数,如输入数字是1,2,3,4  则计算2+3double midResult = calcute(num2, num3, operator1);//根据操作符,先计算第3,4两个数,如输入数字是1,2,3,4  则计算3+4double tailResult = calcute(num3, num4, operator1);for (int j = 0; j < 4; j++) {/* 第2次计算,保存此时的操作符和计算结果此时有5中情况,相当于从4个数中选择2个相邻的数来计算如((1-2)-3)-4, (1-(2-3))-4, (1-2)-(3-4),1-((2-3)-4),1-(2-(3-4))则保存此时第2次计算的结果和操作符*/char operator2 = operator[j];//根据操作符和第1次计算的结果,计算第2次的情况,如第一次计算是(1-2)-3-4,//就计算((1-2)-3)-4 ,则第一次计算结果为1-2=-1  -->   即计算-1-3,即firstResult-3//下面的原理类似double firstMidResult = calcute(firstResult, num3, operator2);double firstTailResult = calcute(num3, num4, operator2);double midFirstResult = calcute(num1, midResult, operator2);double midTailResult = calcute(midResult, num4, operator2);double tailMidResult = calcute(num2, tailResult, operator2);for (int k = 0; k < 4; k++) {//最后1次计算,得出结果,如果是24则保存表达式,原理同上char operator3 = operator[k];if(calcute(firstMidResult, num4, operator3) == 24){String expression = "((" + (int)num1 + operator1 + (int)num2 + ")" + operator2 + (int)num3 + ")" + operator3 + (int)num4;results.add(expression);flag = true;}if(calcute(firstResult, firstTailResult, operator3) == 24){String expression = "(" + (int)num1 + operator1 + (int)num2 + ")" + operator3 + "(" + (int)num3 + operator2 + (int)num4 + ")";results.add(expression);flag = true;}if(calcute(midFirstResult, num4, operator3) == 24){String expression = "(" + (int)num1 + operator2 + "(" + (int)num2 + operator1 + (int)num3 + "))" + operator3 + (int)num4;results.add(expression);flag = true;}if(calcute(num1, midTailResult, operator3) == 24){String expression = "" + (int)num1 + operator3 + "((" + (int)num2 + operator1 + (int)num3 + ")" + operator2 + (int)num4 + ")";results.add(expression);flag = true;}if(calcute(num1, tailMidResult, operator3) == 24){String expression = "" + (int)num1 + operator3 + "(" + (int)num2 + operator2 + "(" + (int)num3 + operator1 + (int)num4 + "))";results.add(expression);flag = true;}}}}return flag;}/*** 给定2个数和指定操作符的计算* @date 2017年12月22日 下午2:47:49 */private static double calcute(double number1, double number2, char operator) {if (operator == '+') {return number1 + number2;} else if (operator == '-') {return number1 - number2;} else if (operator == '*') {return number1 * number2;} else if (operator == '/' && number2 != 0) {return number1 / number2;} else {return -1;}}}

这是GitHub:https://github.com/1404510094/24-java-.git

24点算法讲解与实现相关推荐

  1. BF、KMP、BM、Sunday算法讲解

    原文地址: https://www.cnblogs.com/Syhawk/p/4077295.html BF.KMP.BM.Sunday算法讲解 字串的定位操作通常称作串的模式匹配,是各种串处理系统中 ...

  2. [转]全网最!详!细!tarjan算法讲解

    转发地址:https://blog.csdn.net/qq_34374664/article/details/77488976 原版的地址好像挂了..... 看到别人总结的很好,自己就偷个懒吧..以下 ...

  3. 【OCR】EAST算法讲解及实现

    [OCR]EAST算法讲解及实现 一.概念介绍 OCR(Optical Character Recognition):光学字符识别: 基本流程: 输入图片 -- 预处理 -- 文字定位(采用EAST算 ...

  4. 边界填充算法讲解_边界填充算法

    边界填充算法讲解 Boundary fill is the algorithm used frequently in computer graphics to fill a desired color ...

  5. 干货回顾丨机器学习笔记-----AP(affinity propagat)算法讲解及matlab实现

    在统计和数据挖掘中,亲和传播(AP)是基于数据点之间"消息传递"概念的聚类算法.与诸如k-means或k-medoids的聚类算法不同,亲和传播不需要在运行算法之前确定或估计聚类的 ...

  6. Learning to Rank 中Listwise关于ListNet算法讲解及实现

     [学习排序] Learning to Rank 中Listwise关于ListNet算法讲解及实现             版权声明:本文为博主原创文章,转载请注明CSDN博客源地址!共同学习, ...

  7. TF-IDF算法讲解

    什么是 TF-IDF 算法? TF(全称TermFrequency),中文含义词频,简单理解就是关键词出现在网页当中的频次. IDF(全称InverseDocumentFrequency),中文含义逆 ...

  8. Java的算法讲解以及案例!

    算法是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,java算法就是采用Java语言来实现解决某一问题的清晰指令. 算法的特征: 输入性:有零个或多个外部量作为算法的输入 输出性:算法产生 ...

  9. 【趣味分享】C#实现回味童年的24点算法游戏

    一.24点游戏玩法规则效果展示 1.初始化界面 2.开始游戏界面 3.游戏超时界面 4.查看答案界面 5.答对界面 6.答错界面 7.计算表达式的验证界面 8.一副牌算完开始新一副牌界面 到这里24点 ...

  10. C语言给出任意4个数算24点,讨论24点算法。

    讨论24点算法. 24点是扑克牌游戏 玩法是:从一副扑克的A到10里随意抽出4张牌 用'加''减''乘''除'四个符号算出4个数是否等于24,是 的话成功,否的话失败: 我用的是穷举法!(源码有点长) ...

最新文章

  1. CentOS 7安装fail2ban+Firewalld防止SSH爆破
  2. python评语生成_如何评价生成模型框架 ZhuSuan?
  3. muduo之EPollPoller
  4. 在系统出现未处理的错误时,在Global的Application_Error记录下错误
  5. 最优化学习笔记(四)共轭梯度法
  6. ubuntu之apache正向代理及反向代理(ProxyPass\ProxyPassReverse)
  7. java jar包与配置文件的写法
  8. python界面开发工具手机版_Python界面编程工具包WxPython
  9. [Spark]-结构化数据查询之自定义UDAF
  10. elasticsearch基础查询
  11. Windows电脑上最好的3个txt阅读器
  12. 树莓派4B-Python-控制WS2812
  13. Linux苹果工具,Projectsandcastle:一款针对iPhone的AndroidLinux支持工具
  14. linux下用impdp导入dmp文件
  15. 草稿草稿草稿22.10.9 “yuyu“ IO进程线程
  16. java做2048_java版实现2048游戏功能
  17. 虚拟机3-11-14:53,工作记录
  18. docker 离线安装字体库
  19. 网页打开慢,响应时间慢,如何定位这个问题?
  20. 企业级存储发展趋势谈:开源存储的冷思考

热门文章

  1. 扫盲篇:数字签名、数字证书、域名劫持、域名欺骗
  2. 音乐源码重新上传了,请童鞋们自行下载
  3. 三维场景 WGS84 和街景(百度街景,腾讯街景,google街景,orbitgt街景)联动
  4. 当你是个35岁的硬件工程师,该如何选择未来的职业道路
  5. 给大学生的劝告——你们为何应该开始接触UNIX/Linux
  6. 串口服务器芯片方案,串口转以太网单芯片
  7. 服务器做中转站,搭建 git 本地中转站
  8. 托福考试前你需要知道哪些事情
  9. oAuth2.0店铺订单接口,获取单笔交易的详细信息api接口
  10. matlab 电力电子仿真电路,基于Matlab的电力电子电路仿真方法