java - 使用预定义的递归创建基本数学运算

标签 java math recursion

我计划在 JavaFX 中开发一个新游戏“数字形状系统”。基本上它是一个小内存游戏,其中图片与数字相关联。所以'2'='天鹅','5'='手(手指)'等等。因此玩家会看到练习“天鹅 + 手指 = ?”。

我想要的是遵循规则的所有可能的数学运算:

/*
 * Generate all possible mathematical operations to the console with the numbers 
 * 0-12, where every result is (>= 0 && <= 12).
 *  - Mathematical operations are '+', '-', '*' and '/'.
 *  - The rule 'dot before line' shouldn't be used, instead the operations will 
 *    be executed from left to right.
 *  - Every among result must be between (>= 0 && <= 12) and a whole number.
 *  - Only different numbers are allowed for the operations (an operation have 
 *    2 numbers). For example 2+3 is allowed, 3*3 not.
 * 
 * A solution with recursive methods would be preferred. I want the output for
 * the length 2-10.
 * 
 * Example output with different length:
 *  - Length 3: 2+3(=5)*2(=10)
 *  - Length 5: 2+3(=5)*2(=10)+2(=12)/4(=3)
 */

我已经准备了一个示例实现,但我不知道如何将其转换为递归功能。

import java.util.ArrayList;
import java.util.List;

public class Generator {

    private static final List<Double> numbers = new ArrayList<>();
    private static final List<String> operations = new ArrayList<>();

    static {
        numbers.add(0.0);
        numbers.add(1.0);
        numbers.add(2.0);
        numbers.add(3.0);
        numbers.add(4.0);
        numbers.add(5.0);
        numbers.add(6.0);
        numbers.add(7.0);
        numbers.add(8.0);
        numbers.add(9.0);
        numbers.add(10.0);
        numbers.add(11.0);
        numbers.add(12.0);

        operations.add("+");
        operations.add("-");
        operations.add("*");
        operations.add("/");
    }

    private int lineCounter = 0;

    public Generator() {
        this.init();
    }

    private void init() {

    }

    public void generate() {
        // Length 2 ###########################################################
        boolean okay = false;
        int lineCounter = 0;
        StringBuilder sbDouble = new StringBuilder();
        for (Double first : numbers) {
            for (Double second : numbers) {
                for (String operation : operations) {
                    if (first == second) {
                        continue;
                    }

                    if (operation.equals("/") && (first == 0.0 || second == 0.0)) {
                        continue;
                    }

                    double result = perform(first, operation, second);
                    okay = this.check(result, operation);
                    if (okay) {
                        ++lineCounter;

                        sbDouble = new StringBuilder();
                        this.computeResultAsString(sbDouble, first, operation, second, result);
                        System.out.println(sbDouble.toString());
                    }
                }
            }
        }

        System.out.println("Compute with length 2: " + lineCounter + " lines");
        // Length 2 ###########################################################

        // Length 3 ###########################################################
    okay = false;
        lineCounter = 0;
        sbDouble = new StringBuilder();
        for (Double first : numbers) {
            for (Double second : numbers) {
                for (String operation1 : operations) {
                    if (first == second) {
                        continue;
                    }
                    if (operation1.equals("/") && (first == 0.0 || second == 0.0)) {
                        continue;
                    }

                    double result1 = perform(first, operation1, second);
                    okay = this.check(result1, operation1);
                    if (okay) {
                        for (Double third : numbers) {
                            for (String operation2 : operations) {
                                if (second == third) {
                                    continue;
                                }
                                if (operation2.equals("/") && third == 0.0) {
                                    continue;
                                }

                                double result2 = perform(result1, operation2, third);
                                okay = this.check(result2, operation2);
                                if (okay) {
                                    ++lineCounter;

                                    sbDouble = new StringBuilder();
                                    this.computeResultAsString(sbDouble, first, operation1, second, result1);
                                    this.computeResultAsString(sbDouble, operation2, third, result2);
                                    System.out.println(sbDouble.toString());
                                }
                            }
                        }
                    }
                }
            }
        }

        System.out.println("Compute with length 3: " + lineCounter + " lines");
        // Length 3 ###########################################################

        // Length 4 ###########################################################
        okay = false;
        lineCounter = 0;
        sbDouble = new StringBuilder();
        for (Double first : numbers) {
            for (Double second : numbers) {
                for (String operation1 : operations) {
                    if (first == second) {
                        continue;
                    }
                    if (operation1.equals("/") && (first == 0.0 || second == 0.0)) {
                        continue;
                    }

                    double result1 = perform(first, operation1, second);
                    okay = this.check(result1, operation1);
                    if (okay) {
                        for (Double third : numbers) {
                            for (String operation2 : operations) {
                                if (second == third) {
                                    continue;
                                }
                                if (operation2.equals("/") && third == 0.0) {
                                    continue;
                                }

                                double result2 = perform(result1, operation2, third);
                                okay = this.check(result2, operation2);
                                if (okay) {
                                    for (Double forth : numbers) {
                                        for (String operation3 : operations) {
                                            if (third == forth) {
                                                continue;
                                            }
                                            if (operation3.equals("/") && forth == 0.0) {
                                                continue;
                                            }

                                            double result3 = perform(result2, operation3, forth);
                                            okay = this.check(result3, operation3);
                                            if (okay) {
                                                ++lineCounter;

                                                sbDouble = new StringBuilder();
                                                this.computeResultAsString(sbDouble, first, operation1, second, result1);
                                                this.computeResultAsString(sbDouble, operation2, third, result2);
                                                this.computeResultAsString(sbDouble, operation3, forth, result3);
                                                System.out.println(sbDouble.toString());
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        System.out.println("Compute with length 4: " + lineCounter + " lines");
        // Length 4 ###########################################################
    }

    private boolean check(double result, String operation) {
        switch (operation) {
            case "+":
            case "-":
            case "*": {
                if (result > 0 && result <= 12) {
                    return true;
                }
                break;
            }
            case "/": {
                if (
                        (Math.floor(result) == result)
                        && (result >= 0 && result <= 12)
                ) {
                    return true;
                }
                break;
            }
        }

        return false;
    }

    private double perform(double first, String operation, double second) {
        double result = 0.0;
        switch (operation) {
            case "+": { result = first + second; break; }
            case "-": { result = first - second; break; }
            case "*": { result = first * second; break; }
            case "/": { result = first / second; break; }
        }

        return result;
    }

    private void computeResultAsString(StringBuilder sbDouble, String operation, double second, double result) {
        sbDouble.append(operation);
        sbDouble.append(second);
        sbDouble.append("(=");
        sbDouble.append(result);
        sbDouble.append(")");
    }

    private void computeResultAsString(StringBuilder sbDouble, double first, String operation, double second, double result) {
        sbDouble.append(first);
        sbDouble.append(operation);
        sbDouble.append(second);
        sbDouble.append("(=");
        sbDouble.append(result);
        sbDouble.append(")");
    }

    public static void main(String[] args) {
        final Generator generator = new Generator();
        generator.generate();
    }
}

最佳答案

正如您在自己的代码中看到的,对于“长度”的每次增加,您都必须嵌套相同代码的另一个 block 。对于动态长度值,您无法做到这一点。

因此,您将代码块移动到一个方法中,并传入一个参数来表示它还需要“嵌套”多少次,即 remainingLength。然后该方法可以使用 remainingLength 的递减值调用自身,直到达到 0。

这是一个示例,使用enum作为运算符。

public static void generate(int length) {
    if (length <= 0)
        throw new IllegalArgumentException();
    StringBuilder expr = new StringBuilder();
    for (int number = 0; number <= 12; number++) {
        expr.append(number);
        generate(expr, number, length - 1);
        expr.setLength(0);
    }
}
private static void generate(StringBuilder expr, int exprTotal, int remainingLength) {
    if (remainingLength == 0) {
        System.out.println(expr);
        return;
    }
    final int exprLength = expr.length();
    for (int number = 0; number <= 12; number++) {
        if (number != exprTotal) {
            for (Operator oper : Operator.values()) {
                int total = oper.method.applyAsInt(exprTotal, number);
                if (total >= 0 && total <= 12) {
                    expr.append(oper.symbol).append(number)
                        .append("(=").append(total).append(")");
                    generate(expr, total, remainingLength - 1);
                    expr.setLength(exprLength);
                }
            }
        }
    }
}
private enum Operator {
    PLUS    ('+', Math::addExact),
    MINUS   ('-', Math::subtractExact),
    MULTIPLY('*', Math::multiplyExact),
    DIVIDE  ('/', Operator::divide);

    final char symbol;
    final IntBinaryOperator method;
    private Operator(char symbol, IntBinaryOperator method) {
        this.symbol = symbol;
        this.method = method;
    }
    private static int divide(int left, int right) {
        if (right == 0 || left % right != 0)
            return -1/*No exact integer value*/;
        return left / right;
    }
}

请注意,排列的数量增长很快:

1:            13
2:           253
3:         5,206
4:       113,298
5:     2,583,682
6:    61,064,003
7: 1,480,508,933

关于java - 使用预定义的递归创建基本数学运算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39753988/

相关文章:

Java比较数组元素索引与索引

file - 批处理文件基础知识 : Adding a hardcoded integer to a variable in a for loop

java - 多树和优先排序算法

algorithm - 大 O 符号和递归

python - 应用于共享数据类型的递归在 python 中出错

java - 字符串不匹配? java

java - Android onResume 有时会显示黑屏,有时它不会检测到用户交互

java - 如何使用 ASCII 值生成字符

asp.net - 作为动态变量的数学运算符

scala - 为复数定义一个类是否有意义,其中实部/虚部使用 Numeric[T] 而不是具体类型?