Solution finder algorithm using basic operations

别说谁变了你拦得住时间么 提交于 2019-12-11 03:58:02

问题


I need help with making an algorithm. I am currently designing something for a class I am taking.

Given 4 numbers, I need to find all (or at least the first) combination of the 4 numbers using basic operations (+-*/) to make a certain answer.

For example, if given numbers [1,2,3,4]. and I have to make the answer 12. I can see (without a program) that (2-1)*3*4 = 12

But for more complex numbers, it may be harder to solve just by thinking about it. So I require a program to help me find at least one possible combination to solve the problem.

Note that, in the given 4 numbers, the numbers may repeat, but each number can only used once. For example, the set of 4 can be [2,3,3,4]. But in that set, 2 and 4 cannot be used more than once.

I originally had a plan to brute force find all the possible combinations/orders of each 4 numbers, then iterating through all the operations. I later realized that this won't work as it doesn't take into account an operations like (1-2)*(3+4).

So I was wondering if anyone had an idea of how I can approach solving this problem?

Please keep in mind, that I am still fairly new to programming, so I may not understand some of the more advanced terms and functions. But I can keep up pretty well with things like loops and arrays.


回答1:


There aren't actually that many combinations to check, because the number of precedence cases is limited to 5:
((a:b):c):d
(a:b):(c:d)
(a:(b:c)):d
a:((b:c):d)
a:(b:(c:d))
so with 24 permutations and 3 choices from 4 possible operators, that gives 7680 combinations. And many of these combinations are really identical, because the precedence is unimportant in cases like:
a+b+c+d
a+b+c-d
a*b*c*d
a*b*c/d

Run the code snippet to see a simple loop-based algorithm which checks these 7680 combinations in action. There are a surprising number of solutions for the case 1:2:3:4=12.

function findArithmetic(target, numbers) {

    // PUT THE ARITHMETIC FUNCTIONS IN AN ARRAY, SO WE CAN ITERATE OVER THEM
    function sum(a, b) {return a + b}
    function dif(a, b) {return a - b}
    function prd(a, b) {return a * b}
    function div(a, b) {return a / b}
    var func = [sum, dif, prd, div];

    // DEFINE THE ORDER OF THE CALCULATIONS FOR THE 5 PRECEDENCE CASES
    var prec = [[0, 1, 4, 2, 5, 3],    // 0,1,2,3 are the four numbers
                [0, 1, 2, 3, 4, 5],    // 4 is the result of the 1st calculation
                [1, 2, 0, 4, 5, 3],    // 5 is the result of the 2nd calculation
                [1, 2, 4, 3, 0, 5],    // so here, do 1:2, then result1:3, then 0:result2
                [2, 3, 1, 4, 0, 5]];   // and here, do 2:3, then 1:result1, then 0:result2

    // FIND ALL PERMUTATIONS OF THE NUMBERS AND STORE THEM IN ARRAY "NUMS"
    var nums = [];
    for (var a = 0; a < 4; a++) {
        for (var b = 0; b < 4; b++) {
            if (a == b) continue;
            for (var c = 0; c < 4; c++) {
                if (a == c || b == c) continue;
                for (var d = 0; d < 4; d++) {
                    if (a == d || b == d || c == d) continue;
                    nums.push([numbers[a], numbers[b], numbers[c], numbers[d]]);
                }
            }
        }
    }

    // NOW GET DOWN TO BUSINESS
    var solutions = [];

    // ITERATE OVER ALL 24 PERMUTATIONS
    for (var n = 0; n < nums.length; n++) {

        // ITERATE OVER ALL 5 PRECEDENCE CASES
        for (var p = 0; p < 5; p++) {

            // ITERATE OVER THE 4 OPERATORS FOR THE FIRST CALCULATION
            for (var i = 0; i < 4; i++) {

                // ITERATE OVER THE 4 OPERATORS FOR THE SECOND CALCULATION
                for (var j = 0; j < 4; j++) {

                    // ITERATE OVER THE 4 OPERATORS FOR THE THIRD CALCULATION
                    for (var k = 0; k < 4; k++) {

                        // DO THE CALCULATIONS
                        nums[n][4] = func[i](nums[n][prec[p][0]], nums[n][prec[p][1]]);
                        nums[n][5] = func[j](nums[n][prec[p][2]], nums[n][prec[p][3]]);
                        var result = func[k](nums[n][prec[p][4]], nums[n][prec[p][5]]);

                        // IF THE RESULT IS CORRECT, MAKE A STRING AND ADD TO SOLUTIONS
                        if (result == target) {
                            solutions.push(makeString(n, p, i, j, k));
                        }
                    }
                }
            }
        }
    }
    return solutions;

    // TURN THE RESULT INTO A PRESENTABLE STRING
    // this is a bit fiddly, because in each precedence case, the calculations are done in a different order
    function makeString(n, p, i, j, k) {
        // CHOOSE THE RIGHT STRING TEMPLATE, BASED ON THE PREFERENCE CASE
        var str = ["((aAb)Bc)Cd", "(aAb)B(cCd)", "(aA(bBc))Cd", "aA((bBc)Cd)", "aA(bB(cCd))"][p];
        // REPLACE "a", "b", "c", AND "d" WITH THE NUMBERS
        for (var c = 0; c < 4; c++) str = str.replace(["a","b","c","d"][c], nums[n][c]);
        // REPLACE "A", "B" AND "C" WITH THE OPERATORS, BASED ON EXECUTION ORDER IN PREFERENCE CASE
        var order = [["A","B","C"], ["A","C","B"], ["B","A","C"], ["B","C","A"], ["C","B","A"]];
        for (var c = 0; c < 3; c++) str = str.replace(order[p][c], ["+","-","*","/"][[i,j,k][c]]);
        return str + "=" + target;
    }
}

// RUN THE FUNCTION AND DISPLAY THE RESULTS IN THE CONSOLE
var sol = findArithmetic(12, [1,2,3,4]);
document.write(sol.length + " solutions found:<BR><PRE>");
for (var s in sol) document.write(sol[s] + "<BR>");

This is a simpler solution, without the precedence array. It has the calculations for the five precedence cases written out seperately. Usually programmers would consider this an unelegant solution, because it breaks the "don't repeat yourself" rule; however, in this case it makes the code much easier to understand, and it greatly simplifies the displaying of the results, so for once I think it makes sense to do it this way.

This version only returns one solution per permutation of the numbers and combination of operators, because solutions with different bracket placement, like (a*b)+(c-d) and ((a*b)+c)-d, are really just duplicates. (That's what the continue statement after each calculation is for.)

function findArithmetic(target, numbers) {

    // PUT THE ARITHMETIC FUNCTIONS IN AN ARRAY, SO WE CAN ITERATE OVER THEM
    function sum(a, b) {return a + b}
    function dif(a, b) {return a - b}
    function prd(a, b) {return a * b}
    function div(a, b) {return a / b}
    var func = [sum, dif, prd, div];

    // FIND ALL PERMUTATIONS OF THE NUMBERS AND STORE THEM IN ARRAY "NUMS"
    var nums = [];
    for (var a = 0; a < 4; a++) {
        for (var b = 0; b < 4; b++) {
            if (a == b) continue;
            for (var c = 0; c < 4; c++) {
                if (a == c || b == c) continue;
                for (var d = 0; d < 4; d++) {
                    if (a == d || b == d || c == d) continue;
                    nums.push([numbers[a], numbers[b], numbers[c], numbers[d]]);
                }
            }
        }
    }

    // NOW GET DOWN TO BUSINESS
    var solutions = [];
    var op = ["+","-","*","/"];

    // ITERATE OVER ALL 24 PERMUTATIONS
    for (var n = 0; n < nums.length; n++) {
        var a = nums[n][0], b = nums[n][1], c = nums[n][2], d = nums[n][3];

        // ITERATE OVER THE 4 OPERATORS FOR THE FIRST CALCULATION
        for (var i = 0; i < 4; i++) {

            // ITERATE OVER THE 4 OPERATORS FOR THE SECOND CALCULATION
            for (var j = 0; j < 4; j++) {

                // ITERATE OVER THE 4 OPERATORS FOR THE THIRD CALCULATION
                for (var k = 0; k < 4; k++) {

                    // CHECK PRECEDENCE CASE 1:  ((a:b):c):d
                    if (target == func[k](func[j](func[i](a, b), c), d)) {
                        solutions.push("((" + a + op[i] + b + ")" + op[j] + c + ")" + op[k] + d + "=" + target);
                        continue;
                    }
                    // CHECK PRECEDENCE CASE 2:  (a:b):(c:d)
                    if (target == func[j](func[i](a, b), func[k](c, d))) {
                        solutions.push("(" + a + op[i] + b + ")" + op[j] + "(" + c + op[k] + d + ")=" + target);
                        continue;
                    }
                    // CHECK PRECEDENCE CASE 3:  (a:(b:c)):d
                    if (target == func[k](func[i](a, func[j](b, c)), d)) {
                        solutions.push("(" + a + op[i] + "(" + b + op[j] + c + "))" + op[k] + d + "=" + target);
                        continue;
                    }
                    // CHECK PRECEDENCE CASE 4:  a:((b:c):d)
                    if (target == func[i](a, func[k](func[j](b, c), d))) {
                        solutions.push(a + op[i] + "((" + b + op[j] + c + ")" + op[k] + d + ")=" + target);
                        continue;
                    }
                    // CHECK PRECEDENCE CASE 5:  a:(b:(c:d))
                    if (target == func[i](a, func[j](b, func[k](c, d)))) {
                        solutions.push(a + op[i] + "(" + b + op[j] + "(" + c + op[k] + d + "))=" + target);
                    }
                }
            }
        }
    }
    return solutions;
}

// RUN THE FUNCTION AND DISPLAY THE RESULTS IN THE CONSOLE
var sol = findArithmetic(2, [4,5,6,12]);
document.write(sol.length + " solutions found:<BR><PRE>");
for (var s in sol) document.write(sol[s] + "<BR>");



回答2:


You are looking for binary expression trees with 4 leafs. There is always a top-level node (+,*,-,/ in your case). For a given top level node, organize your search by the number of leafs to the left of the top-level node, which must be a number in the range 1 to 3. There is a natural recursive solution since, for example, if the left side has three leafs then it itself is a binary expression tree with three leafs. You really don't need to use tree data structures -- you can use fully parenthesized strings (e.g. like "(((1 + 2) * 3) / 4)" for the tree whose top node is "/" and whose left side is the tree "((1 + 2)*3)" and whose right side is the leaf 4).



来源:https://stackoverflow.com/questions/32229242/solution-finder-algorithm-using-basic-operations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!