今天看到一个帖子说程序员面试考24点算法,想为什么不用程序来实现呢。在网上没有找到非常完美的算法,包括那个24点计算器,给出的结果重复的也较多。所以自己写了一个。在这儿贴出来给大家分享一下。附源码。

问题

给出4个1~10之间的数字,使用加、减、乘、除和括号列出一个计算结果为24的算式。比如: 3、3、8、8 可以写成 8/(3-8/3)。但不是每一个都像这个例子只有一个结果,需要列出所有可能的算式。

思路

  1. 四个数字可以排列。都不同的情况下可能有24中。
  2. 每两个数字之间可以有一个双目运算符。一共可以添加三个运算符,并且只能三个。
  3. 可以通过添加括号的方式改变运算计算顺序,不考虑优先级,一共应该有5种添加括号的方式。

算法

  1. 对给出的4个数字进行排列组合,需要去掉相同数值重复的问题,如果各个数字都不相同有24种排列。参见排列组合算法。
  2. 对每一个排列插入3个运算符,4个运算符3个位置所以有3^4=64个组合。
  3. 逆波兰式用运行符位置来代替括号的组合方式,用枚举法,一共5种。
    • a b + c + d +  => (((a+b)+c)+d)
    • a b + c d + +  => ((a+b)+(c+d))
    • a b c + d + +  => (a+((b+c)+d))
    • a b c + + d +  => ((a+(b+c))+d)
    • a b c d + + +  => (a+(b+(c+d)))
  4. 用分数来计算算式的结果,如果结果是24就获得一个符合要求的算式。
  5. 将符合条件逆波兰式转换成括号方式的四则运算表达式,并输出。不同的逆波兰式转换为四则运算可能重复(如果每一步加括号是不重复的)。

结果

源码

GitHub 地址 https://github.com/codefan/codeForFun

public class Calculation24 {private static Map<String, List<String>> foundReslutions = new HashMap<>(50);/*** 非递归的排列组合* @param listSouce 可 排序的 列表* @param comparable 比较函数* @param consumer 消费排序结果* @param <T> 泛型*/public static <T> void permutation(List<T> listSouce ,Comparator<? super T> comparable,Consumer<List<T>> consumer){if(listSouce.size()<2){consumer.accept(listSouce);return;}listSouce.sort(comparable);int len = listSouce.size();List<Integer> comPos = new ArrayList<>(len);List<Boolean> usedItem = new ArrayList<>(len);List<T> comRes = new ArrayList<>(len);for(int i=0;i<len;i++){comPos.add(-1);usedItem.add(false);comRes.add(null);}comPos.set(0,0);usedItem.set(0, true);int sortIndex = 0;while(sortIndex >=0 ){comRes.set(sortIndex, listSouce.get( comPos.get(sortIndex)));if( sortIndex == len - 2){ // 如果获得一个排序for(int i=0; i< len; i++){if(!usedItem.get(i)){// 将最后一个未使用的添加到排列的最后comRes.set( sortIndex +1, listSouce.get(i));break;}}consumer.accept(comRes);while(sortIndex >=0 ) {//下一个int prePos = comPos.get(sortIndex);usedItem.set(prePos, false);//当前pos ++ (步进)while (comPos.get(sortIndex) + 1  < len &&( usedItem.get(comPos.get(sortIndex) + 1) ||comparable.compare( listSouce.get(prePos),listSouce.get(comPos.get(sortIndex) + 1))== 0 )) {comPos.set(sortIndex, comPos.get(sortIndex) + 1);}comPos.set(sortIndex, comPos.get(sortIndex) + 1);// 如果已经到上线,继续回退if (comPos.get(sortIndex)  < len ) {//重新计算下个列表usedItem.set(comPos.get(sortIndex), true);comRes.set( sortIndex, listSouce.get(comPos.get(sortIndex)));break;}else{ // 回退sortIndex--;//comPos.set(sortIndex, comPos.get(sortIndex) + 1);}}} else { // 下一个for(int i=0; i< len; i++){if(!usedItem.get(i)){comPos.set(sortIndex + 1,i);usedItem.set(i,true);break;}}sortIndex++;}}}//逆波兰式private static Fraction calcReversePolishRepresentation(Object[] reversePolish) {Fraction[] stack = new Fraction[4];int j = 0;for (int i = 0; i < 7; i++) {if (reversePolish[i] instanceof Integer) {stack[j] =  new Fraction((Integer) reversePolish[i]);j++;} else {switch ((String) reversePolish[i]) {case "+":stack[j - 2] = stack[j - 2].add(stack[j - 1]);break;case "-":stack[j - 2] = stack[j - 2].subtract(stack[j - 1]);break;case "*":stack[j - 2] = stack[j - 2].multiply(stack[j - 1]);break;case "/":if (stack[j - 1].equals(Fraction.ZERO)) {return Fraction.MINUS_ONE;}stack[j - 2] = stack[j - 2].divide(stack[j - 1]);break;}j--;}}return stack[0];}// 算24点 并将结果的逆波兰式转换为 四则运算表达式@SuppressWarnings("unchecked")private static void checkResult(Object[] reversePolish){if( calcReversePolishRepresentation(reversePolish).equals(new Fraction(24))  ){Pair<String, String>[] stack = new Pair[4];int j = 0;for (int i = 0; i < 7; i++) {if (reversePolish[i] instanceof Integer) {stack[j] = new ImmutablePair<>("O",String.valueOf(reversePolish[i]));j++;} else {switch ((String) reversePolish[i]) {case "+":stack[j - 2] =new ImmutablePair<>("+",stack[j - 2].getRight()+"+"+stack[j - 1].getRight());break;case "-":stack[j - 2] =new ImmutablePair<>("-",stack[j - 2].getRight()+( StringUtils.equalsAny( stack[j - 1].getLeft(), "-","+")? "-("+stack[j - 1].getRight()+")": "-"+stack[j - 1].getRight()));break;case "*":stack[j - 2] =stack[j - 2] =new ImmutablePair<>("*",( StringUtils.equalsAny( stack[j - 2].getLeft(), "-","+")? "("+stack[j - 2].getRight()+")": stack[j - 2].getRight())+( StringUtils.equalsAny( stack[j - 1].getLeft(), "-","+")? "*("+stack[j - 1].getRight()+")": "*"+stack[j - 1].getRight()));break;case "/":stack[j - 2] =new ImmutablePair<>("/",( StringUtils.equalsAny( stack[j - 2].getLeft(), "-","+")? "("+stack[j - 2].getRight()+")": stack[j - 2].getRight())+( StringUtils.equalsAny( stack[j - 1].getLeft(), "-","+","*","/")? "/("+stack[j - 1].getRight()+")": "/"+stack[j - 1].getRight()));break;}j--;}}String rb = StringUtils.join(reversePolish, " ");List<String> rbs = foundReslutions.get(stack[0].getRight());if(rbs==null){rbs = new ArrayList<>();rbs.add(rb);foundReslutions.put(stack[0].getRight(), rbs);}else{rbs.add(rb);}}}//将 数字和操作排序//这部分代码写的比较笨拙,应该可以更优美一点private static void calc24Point(List<Integer> rList){String[] opts = {"+","-","*","/"};Object [] stack = new Object[7];for(int i=0;i<4;i++){for(int j=0;j<4;j++){for(int k=0;k<4;k++){// a b + c + d +  (((a+b)+c)+d)stack[0] = rList.get(0);stack[1] = rList.get(1);stack[2] = opts[i];stack[3] = rList.get(2);stack[4] = opts[j];stack[5] = rList.get(3);stack[6] = opts[k];checkResult(stack);// a b + c d + +  ((a+b)+(c+d))stack[3] = rList.get(2);stack[4] = rList.get(3);stack[5] = opts[j];checkResult(stack);// a b c + d + +  (a+((b+c)+d))stack[2] = rList.get(2);stack[3] = opts[i];stack[4] = rList.get(3);checkResult(stack);// a b c + + d +  ((a+(b+c))+d)stack[3] = opts[i];stack[4] = opts[j];stack[5] = rList.get(3);checkResult(stack);// a b c d + + +  (a+(b+(c+d)))stack[3] = rList.get(3);stack[4] = opts[i];stack[5] = opts[j];checkResult(stack);}}}}//判断输入的是否为数值private static boolean isNumber(String strNum) {if(StringUtils.isBlank(strNum)){return false;}for(int i=0; i<strNum.length(); i++){if(strNum.charAt(i)<'0' || strNum.charAt(i)>'9'){return false;}}return true;}public static void main(String arg[]) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));while (true) {System.out.println("请在一行中输入用空格隔开的4在1和10之间的整数,退出请输入exit:");String s = br.readLine().trim();if(StringUtils.isBlank(s)){continue;}if(StringUtils.equalsIgnoreCase("exit",s)){break;}foundReslutions.clear();String[] nums = s.split(" ");List<Integer> alist = new ArrayList<>(4);for (String num : nums) {if (Calculation24.isNumber(num)) {//这边没有判断范围alist.add(Integer.valueOf(num));if (alist.size() == 4) {break;}}}if( alist.size() < 4){continue;}Calculation24.permutation(alist, Integer::compare, Calculation24::calc24Point);//展示结果int sc=0;for(Map.Entry<String,List<String>> ent : foundReslutions.entrySet()){sc ++;System.out.print((sc<10?" "+sc+ ": ": sc + ": ")+ ent.getKey());for(int i= ent.getKey().length(); i<16; i++ ){System.out.print(" ");}System.out.println(ent.getValue().get(0));for(int i=1; i<ent.getValue().size();i++){System.out.print("                    ");System.out.println(ent.getValue().get(i));}}System.out.println("一共中找到 " + sc + " 个不同方案。");}}
}

4个数字计算24点java算法(附源码)相关推荐

  1. 人民币数字大写转换 java实现 [附源码]

    动机 业务系统开发的报表中经常需要将人民币数值转换为大写,所以江苏南大先腾java研发框架中写了一个通用的大写转换函数.由于报表的数值一般比较大所以在java开发中不建议使用float或者int类型, ...

  2. 通用的非递归排列和组合算法[附源码]

    动机 Apache Math包中有很多关分布的算法,但是没有找到排列组合相关的算法.索性自己写一个.排列组合可以分两个算法: 组合算法,就是在一个数组中取出m(小于等于数组的长度 n)个对象,有多少中 ...

  3. 从零接入微信公众号(Java实现 附源码)

    目录 从零接入微信公众号 一.微信测试号的申请与关注 1.申请 2.访问域名配置 3.关注 二.测试号快速生成底部导航栏 1.获取 access_token 2.自定义菜单 三.微信公众号网页授权 1 ...

  4. 利用百度语音识别技术实现文字转语音的应用(Java版附源码)

    @throws IOException @throws DemoException */ public static String getResponseString(HttpURLConnectio ...

  5. 数字基带传输系统--matlab实现,附源码

    目录 一. 数字基带传输系统原理 1. 带限信道的基带系统模型(连续域分析) 2. 升余弦滚降滤波器 3. 最佳基带系统 二.发送滤波器设计 1.滤波器的设计 2. 频率抽样法设计非匹配形式的基带系统 ...

  6. C语言常用13种算法附源码

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105147802 常 ...

  7. C语言使用1到9求出所有k个数字的所有组合的算法(附完整源码)

    C语言使用1到9求出所有k个数字的所有组合的算法 C语言使用1到9求出所有k个数字的所有组合的算法完整源码(定义,实现,main函数测试) C语言使用1到9求出所有k个数字的所有组合的算法完整源码(定 ...

  8. c++Numerical string sort数字字符串排序的实现算法(附完整源码)

    C++Numerical string sort数字字符串排序的实现算法 C++Numerical string sort数字字符串排序的实现算法完整源码(定义,实现,main函数测试) C++Num ...

  9. 【毕设|Java项目开发合集】(附源码)

    [毕设|Java项目开发合集] 14个Java项目(附源码)助你轻松搞定毕业设计! 1.新冠疫情统计系统 2.家教系统 3.进销存管理系统 4.饮食分享平台 5.宠物领养平台 6.销售评价系统 7.酒 ...

最新文章

  1. 游戏角色坐标的保存间隔_游戏岗位看这里鸭——
  2. CLR线程池的作用与原理浅析
  3. C# 如何处理抛出的异常,或者已知的错误
  4. ICPC程序设计题解书籍系列之五:吴永辉:《数据结构编程实验》(第2版)
  5. 计算机对电器的影响论文,突然断电到底伤不伤电器:这是答案
  6. outlook安装包下载
  7. python操作oracle数据库知识梳理
  8. 【PS | 学习】毛坯乡间图打造复古美式电影海报图
  9. 国际电话区号 mysql数据表
  10. 武汉大学计算机学院 田海院长,玄跻峰 - 教师简历 CV- 武汉大学计算机学院
  11. OpenSSL 常用函数——证书操作
  12. BTC txid与vote的关系
  13. D3D管线以及着色器工作原理-画一个三角形
  14. 求求你别再写上千行的类了,试试 IDEA 这些牛逼的重构技巧吧!
  15. 含源码 | C语言做可写入文件的账号密码登录系统
  16. kms工具(2021.7.24更新)
  17. 程序员必须知道的9大数据挖掘工具
  18. python2.x 默认编码问题
  19. Attempt to reset the display driver and recover from timeout failed
  20. javascript匿名函数的各种执行形式

热门文章

  1. HDFS命令批量创建文件夹和文件
  2. OCR应用:表格票据识别
  3. 微信sougo中 风景关键字搜索 文章爬取
  4. 解决perl模块无法安装的问题
  5. ASP.NET大型外卖订餐系统源码 (PC版+手机版+商户版)
  6. 企业内部激励机制设计·上
  7. 智能家居市场展现蓬勃发展态势,未来智能家居发展趋势如何?
  8. 罗马仕php30和plp30,别盲目选择!一篇文章让你区分华为P30和华为P30Pro的差别
  9. 在线将pdf转换成jpg格式的方法
  10. linux socks5 全局代理软件 tsocks 简介