4个数字计算24点java算法(附源码)

今天看到一个帖子说程序员面试考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 + " 个不同方案。");
        }
    }
}

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值