`
RednaxelaFX
  • 浏览: 3047679 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

那啥……穷举实现的24点(rev1)

    博客分类:
  • Java
阅读更多
相关链接:
那啥……穷举实现的24点

昨天那帖里提到,那个实现并不完整,会漏掉一些情况。漏掉的是什么呢?
假如我们用n代表一个数字,用o代表一个运算符,那么昨天实现的运算是这样的形式的后序表达式:
n n o n o n o

实际上因为减法与除法不满足交换律,所以另外几种情况也必须考虑到。
一共是5种情况:
后序表达式 => 中序表达式
n n n n o o o => n o ( n o ( n o n ) )
n n n o n o o => n o ( ( n o n ) o n ) )
n n n o o n o => ( n o ( n o n ) ) o n
n n o n n o o => ( n o n ) o ( n o n )
n n o n o n o => ( ( n o n ) o n ) o n

思路是:以后序表达式考虑,一共是7个空位,其中头两个必须是数字,最后一个必须是运算符;从左向右看,每个位置之前的数字的个数至少要比运算符的个数多一个。于是得到上面的5种情况。

把昨天漏掉的情况加进来,我做了下面的实现,暂且叫rev1。
不过这个实现并没有尝试去消除所有本质上相同的表达式(在后缀表达式中本质上相同但写法不同的表达式会由满足交换律的运算而产生),只是把三个运算都是加法或者乘法的情况作为特殊情况单独计算了而已。
还是把生成全排列的部分换成了递归的方法。昨天那个三层for循环嵌套的方法太ad-hoc了,不爽。

说起来,我并没有用栈去计算表达式,实际上也没有用后序表达式来做计算。只是用后序表达式来输出,想绕开括号问题而已。在rev1的实现里,只要在Evaluator里把其中的几个toString()方法改掉就能改回到以中序方式输出;不想在括号上多麻烦的话,可以只给加法和减法的两边加括号——因为这里运算优先级最高的除括号外就是乘法和除法了,它们两边加不加括号都可以。有个小陷阱就是了:a * b / c与a * ( b / c )不一定一样,因为b不一定是c的倍数,但a * b可能是c的倍数。

在Operator和两个Tuple类里都添加了些暂时还没用上的方法(例如说运算符优先级、Iteratable<E>的实现、equals()的覆盖等),不过如果以后还想改进的话说不定会用到,就留着了。
Anyway,这代码里一个switch也没有,因为没有这个必要。依靠面向对象的多态性,没必要让代码被switch所污染。在多个地方对某种整型代码做switch的话,很可能改一处就改一堆地方,太麻烦……

要是Java支持类似C#的indexer就好了……那样就不用写一堆get(0)、get(1)之类的 T T

Calc24.java:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashSet;

public class Calc24 {
    private HashSet<OperandTuple> operandTupleSet;
    private HashSet<OperatorTuple> operatorTupleSet;
    private int[] inputOperand;

    public Calc24() {
        this.operandTupleSet  = new HashSet<OperandTuple>();
        this.operatorTupleSet = new HashSet<OperatorTuple>();
        this.inputOperand = promptInput();
        this.permutateOperand();
        for (Operator op1 : Operator.values())
            for (Operator op2 : Operator.values())
                for (Operator op3 : Operator.values())
                    this.operatorTupleSet.add(new OperatorTuple(op1, op2, op3));
    }

    public static void main(String[] args) {
        Calc24 program = new Calc24();
        HashSet<String> resultSet = new HashSet<String>();
        OperandTuple numTuple = new OperandTuple(program.inputOperand[0], program.inputOperand[1], program.inputOperand[2], program.inputOperand[3]);
        OperatorTuple opAddTuple = new OperatorTuple(Operator.Add, Operator.Add, Operator.Add);
        OperatorTuple opMulTuple = new OperatorTuple(Operator.Multiply, Operator.Multiply, Operator.Multiply);
        program.operatorTupleSet.remove(opAddTuple);
        program.operatorTupleSet.remove(opMulTuple);
        
        // special case handling
        if (Evaluator.TYPE1.eval(numTuple, opAddTuple) == 24)
            resultSet.add(Evaluator.TYPE1.toString(numTuple, opAddTuple));
        if (Evaluator.TYPE1.eval(numTuple, opMulTuple) == 24)
            resultSet.add(Evaluator.TYPE1.toString(numTuple, opMulTuple));
        // evaluate all other possible expressions
        for (OperandTuple operandTuple : program.operandTupleSet)
            for (OperatorTuple operatorTuple : program.operatorTupleSet)
                for (Evaluator ev : Evaluator.values())
                    try {
                        if (ev.eval(operandTuple, operatorTuple) == 24)
                            resultSet.add(ev.toString(operandTuple, operatorTuple));
                    } catch (Exception e) { } // skip when modulo is not zero in a division
        for (String expression : resultSet)
            System.out.println(expression);
    }

    private static int[] promptInput() {
        int[] input = new int[4];
        BufferedReader reader = null;
        String line = null;
        try {
            reader = new BufferedReader(new InputStreamReader(System.in));
            for (int i = 0; i < 4; ++i) {
                System.out.print("Enter a number (1 to 13): ");
                line = reader.readLine();
                try {
                    input[i] = Integer.parseInt(line);
                    if (input[i] < 1 || input[i] > 13) throw new IllegalArgumentException();
                } catch (IllegalArgumentException e) {
                    System.out.println("The input is invalid. Please try again.");
                    --i;
                }
            }
        } catch (Exception e) { // ignore other exceptions
        }
        return input;
    }

    // print N! permutation of the elements of array a (not in order)
    private void permutateOperand() {
        int[] indices = new int[] { 0, 1, 2, 3 };
        permutateOperand(indices, indices.length);
    }

    private void permutateOperand(int[] array, int length) {
        if (length == 1) {
            this.operandTupleSet.add(new OperandTuple(
                    this.inputOperand[array[0]], this.inputOperand[array[1]],
                    this.inputOperand[array[2]], this.inputOperand[array[3]]));
            return;
        }
        for (int i = 0; i < length; i++) {
            swap(array, i, length - 1);
            permutateOperand(array, length - 1);
            swap(array, i, length - 1);
        }
    }

    // swap the numbers at indices i and j
    private static void swap(int[] array, int i, int j) {
        int temp;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
}


OperandTuple.java:
import java.util.Arrays;
import java.util.Iterator;

public class OperandTuple implements Iterable<Integer> {
    private Integer[] numbers;

    public OperandTuple(int first, int second, int third, int fourth) {
        this.numbers = new Integer[4];
        this.numbers[0] = first;
        this.numbers[1] = second;
        this.numbers[2] = third;
        this.numbers[3] = fourth;
    }

    public int get(int index) {
        return this.numbers[index];
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof OperandTuple) {
            OperandTuple rhs = (OperandTuple) obj;
            return Arrays.equals(this.numbers, rhs.numbers);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        return Arrays.hashCode(this.numbers);
    }

    public Iterator<Integer> iterator() {
        return Arrays.asList(this.numbers).iterator();
    }
}


OperatorTuple.java:
import java.util.Arrays;
import java.util.Iterator;

public class OperatorTuple implements Iterable<Operator> {
    private Operator[] operators;

    public OperatorTuple(Operator first, Operator second, Operator third) {
        this.operators = new Operator[3];
        this.operators[0] = first;
        this.operators[1] = second;
        this.operators[2] = third;
    }
    
    public Operator get(int index) {
        return this.operators[index];
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof OperatorTuple) {
            OperatorTuple rhs = (OperatorTuple) obj;
            return Arrays.equals(this.operators, rhs.operators);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        return Arrays.hashCode(this.operators);
    }

    public Iterator<Operator> iterator() {
        return Arrays.asList(this.operators).iterator();
    }
}


Operator.java:
public enum Operator {
    Add("+", 1) {
        int invoke(int first, int second) {
            return first + second;
        }
    },
    Minus("-", 1) {
        int invoke(int first, int second) {
            return first - second;
        }
    },
    Multiply("*", 2) {
        int invoke(int first, int second) {
            return first * second;
        }
    },
    Divide("/", 2) {
        int invoke(int first, int second) {
            if (!canDivide(first, second)) throw new CannotDivideException(first + ", " + second);
            return first / second;
        }
    };
    
    private String opChar;
    private int precedence;
    
    Operator(String opChar, int precedence) {
        this.opChar = opChar;
        this.precedence = precedence;
    }
    
    abstract int invoke(int first, int second);
    
    @Override
    public String toString() {
        return this.opChar;
    }
    
    public int getPrecedence() {
        return this.precedence;
    }
    
    public static boolean canDivide(int first, int second) {
        return (first % second) == 0;
    }
}


Evaluator.java:
public enum Evaluator {
    TYPE1 { // num1 num2 num3 num4 op1 op2 op3
        public int eval(OperandTuple nums, OperatorTuple ops) {
            return ops.get(2).invoke(nums.get(0), ops.get(1).invoke(nums.get(1), ops.get(0).invoke(nums.get(2), nums.get(3))));
        }
        public String toString(OperandTuple nums, OperatorTuple ops) {
            return (nums.get(0) + " " + nums.get(1) + " " + nums.get(2) + " " + nums.get(3) + " " + ops.get(0) + " " + ops.get(1) + " " + ops.get(2));
        }
    },
    TYPE2{ // num1 num2 num3 op1 num4 op2 op3
        public int eval(OperandTuple nums, OperatorTuple ops) {
            return ops.get(2).invoke(nums.get(0), ops.get(1).invoke(ops.get(0).invoke(nums.get(1), nums.get(2)), nums.get(3)));
        }
        public String toString(OperandTuple nums, OperatorTuple ops) {
            return (nums.get(0) + " " + nums.get(1) + " " + nums.get(2) + " " + ops.get(0) + " " + nums.get(3) + " " + ops.get(1) + " " + ops.get(2));
        }
    },
    TYPE3{ // num1 num2 num3 op1 op2 num4 op3
        public int eval(OperandTuple nums, OperatorTuple ops) {
            return ops.get(2).invoke(ops.get(1).invoke(nums.get(0), ops.get(0).invoke(nums.get(1), nums.get(2))), nums.get(3));
        }
        public String toString(OperandTuple nums, OperatorTuple ops) {
            return (nums.get(0) + " " + nums.get(1) + " " + nums.get(2) + " " + ops.get(0) + " " + ops.get(1) + " " + nums.get(3) + " " + ops.get(2));
        }
    },
    TYPE4{ // num1 num2 op1 num3 num4 op2 op3
        public int eval(OperandTuple nums, OperatorTuple ops) {
            return ops.get(2).invoke(ops.get(0).invoke(nums.get(0), nums.get(1)), ops.get(1).invoke(nums.get(2), nums.get(3)));
        }
        public String toString(OperandTuple nums, OperatorTuple ops) {
            return (nums.get(0) + " " + nums.get(1) + " " + ops.get(0) + " " + nums.get(2) + " " + nums.get(3) + " " + ops.get(1) + " " + ops.get(2));
        }
    },
    TYPE5{ // num1 num2 op1 num3 op2 num4 op3
        public int eval(OperandTuple nums, OperatorTuple ops) {
            return ops.get(2).invoke(ops.get(1).invoke(ops.get(0).invoke(nums.get(0), nums.get(1)), nums.get(2)), nums.get(3));
        }
        public String toString(OperandTuple nums, OperatorTuple ops) {
            return (nums.get(0) + " " + nums.get(1) + " " + ops.get(0) + " " + nums.get(2) + " " + ops.get(1) + " " + nums.get(3) + " " + ops.get(2));
        }
    };
    
    abstract public int eval(OperandTuple nums, OperatorTuple ops);
    abstract public String toString(OperandTuple nums, OperatorTuple ops);
}


CannotDivideException.java:
public class CannotDivideException extends RuntimeException {
    private static final long serialVersionUID = -9174448473262875939L;

    public CannotDivideException() {
        super();
    }
    
    public CannotDivideException(String message) {
        super(message);
    }
}
分享到:
评论
3 楼 RednaxelaFX 2008-08-11  
在CodeProject上看到另一种更受限制的24点问题,和对应的一种解:The 24 Puzzle
2 楼 RednaxelaFX 2008-04-09  
我这也是傻瓜型的……总之算法什么的完全跟“优化”沾不上边。只是做了:
1、充分使用Java的语法
2、使用面向对象的“壳”……骨子里嘛,嘛rev1的代码比昨天的稍微好点吧。
1 楼 jhpx 2008-04-09  
偶以前用C写过一个递归的傻瓜型24点……

相关推荐

    C++算24点代码穷举实现

    在编程领域,"C++算24点代码穷举实现"是一个典型的数学和算法问题,它涉及到使用C++编程语言来解决24点游戏。24点游戏是一种流行的心算游戏,玩家需要从四张1到13的扑克牌中通过加、减、乘、除运算得出24这个结果。...

    穷举组合法计算24点

    在编程领域,"穷举组合法计算24点"是一个有趣的算法问题,它涉及到数学、逻辑和编程技巧。24点游戏是大家都熟知的一种智力游戏,玩家需要利用四张牌上的数字,通过加减乘除四则运算以及括号来得到结果24。在这个问题...

    C语言24点游戏穷举法

    本文将深入探讨如何使用C语言实现24点游戏的穷举法。24点游戏是一种数学智力游戏,玩家需要从四张1到13之间的扑克牌中,通过加、减、乘、除和括号的操作,使得结果等于24。 首先,我们需要理解游戏规则。四张牌可以...

    还是24点……前两个都有问题……

    标题中的“还是24点……前两个都有问题……”暗示了这是一个关于解决24点游戏的问题,其中前两种尝试可能存在不足或错误。24点游戏是一个流行的心算游戏,玩家需要使用加减乘除四种基本运算,以及括号来重新组合给定...

    ACM程序设计培训教程

     15.6 应用…………………………………………………………………………………………………………227  〖案例l〗果园篱笆………………………………………………………………227  〖案例2〗巨人和鬼………………...

    24点游戏穷举源码

    穷举破解24点游戏

    24点算法(穷举法)

    24点算法是一种基于四则运算的...总的来说,24点算法的C++实现涉及了数据结构(堆栈)、算法(穷举搜索、后缀表达式求值)以及基本的逻辑判断。这种编程挑战有助于提升对数值计算、逻辑控制和数据结构的理解与运用。

    C++八皇后穷举法实现

    自己用粗暴的穷举法实现的八皇后,代码浅显易懂,对初学者应该有帮助。

    24点算法实现

    给定4个整数,其中每个数字只能使用一次;任意使用 + - * / ( ) ,构造出一个表达式,使得最终结果为24,这就是常见的算24点的游戏。这方面的程序很多,一般都是穷举求解。本程序通过递归算法实现实现24点计算,

    穷举法求解0-1整数规划的matlab程序.zip_TSP问题穷举法_穷举_穷举法求解0-1_穷举法;整数规划_背包问题MATL

    0-1整数规划有很广泛的应用背景,比如指派问题,背包问题等等,实际上TSP问题也是一个0-1问题,当然这些问题都是NP问题,对于规模较大的问题用穷举法是没有办法在可接受的时间内求得最优解的,本程序只不过是一个...

    穷举法C/C++程序

    在编程领域,穷举法是一种常见的解决问题的策略,尤其在C和C++这两种语言中,开发者经常使用这种方法来实现特定的算法。穷举法,也称为全搜索或枚举,是指在解决一个问题时,尝试所有可能的情况,直到找到正确答案。...

    穷举算法经典案例及其C语言实现.

    ### 知识点解析 #### 一、穷举算法简介 穷举算法是一种通过尝试所有可能情况来寻找问题最优解的算法。它适用于问题规模较小的情况,在这些情况下,计算机能够快速处理并找到最佳解决方案。虽然穷举算法在解决大...

    SADirRead 穷举目录 穷举文件

    标签"穷举目录 穷举文件"再次强调了这个程序的主要任务,即遍历目录结构并列举出所有文件。在实际的编程实践中,这通常涉及到递归算法,因为要处理无限深度的子目录结构。 压缩包内的文件名列表提供了更多关于程序...

    穷举法求解0-1整数规划的matlab程序

    根据提供的文件信息,本文将详细解析“穷举法求解0-1整数规划的MATLAB程序”的核心知识点,包括穷举法的基本概念、在0-1整数规划中的应用以及MATLAB实现方法。 ### 一、穷举法概述 穷举法(Exhaustive Search ...

    穷举法实现n皇后问题

    用穷举法求解n皇后问题

    穷举法代码解析带注释(学习穷举法代码好资料)

    标题与描述中的关键词“穷举法代码解析带注释”明确指出本文将深入探讨穷举法的基本原理、代码实现以及具体应用案例。穷举法,作为一种基础而有效的算法策略,被广泛应用于解决特定类型的问题,尤其是在面对有限解...

    穷举的应用 希望大家喜欢

    四、穷举的实现技巧 1. 递归:适合于结构清晰、状态转移明确的问题,如回溯法。 2. 循环:对于有序或无序集合,可以用循环结构进行枚举。 3. 广度优先搜索(BFS)和深度优先搜索(DFS):在图和树结构中应用广泛,...

    matlab程序(穷举法).rar_matlab_枚举法_穷举法 matlab_穷举法MATLAB_穷举算法 tsp

    MATLAB优化算法案例分析与应用(进阶篇)1-10章程序下载

    算法与程序设计:第2章 穷举法与迭代法.ppt

    通常,穷举法需要用多重循环来实现。在程序实现时,需要考虑穷举范围和约束条件。这两点是穷举法的关键步骤。 本章节还提供了几个实例来说明穷举法的应用。例如,换钱问题、水仙花数问题和数独游戏问题。这些实例...

    如何实现24点计算_论文

    ### 如何实现24点计算 #### 知识点概览 本文主要介绍了一种实现24点游戏计算的方法,并提供了详细的算法分析及C++语言的实现思路。24点是一种数学益智游戏,通常玩家需要利用加减乘除四则运算以及括号,将随机给出...

Global site tag (gtag.js) - Google Analytics