为什么这个递归函数会跳过数字?

时间:2016-09-13 20:11:10

标签: javascript recursion

我正试图找到与数字1-9相等的各种可能性。这个功能可以产生预期的效果,但也有其他我没想过的结果。其他结果加起来为100,但没有这些数字,比如省略3或6.为什么包含这些其他结果?

    var nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
    var signs = ["+", "-", "N"];
    var results = [];
    find100("1");
    function find100(expr) {
       if (eval(expr.replace(/N/g, "")) === 100) {
          results.push(expr);
       } else {
          for (var i = eval(expr.substring(expr.length - 1, expr.length)) + 1; i <=    
          nums.length; i++) {
             signs.forEach(function(sign) {
                var expr2 = expr;
                find100(expr2 += sign + i);
             });
          }
       }
    }

期望的输出:

1+2+3-4+5+6+78+9, 
1+2+34-5+67-8+9, 
1+23-4+5+6+78-9, 
1+23-4+56+7+8+9, 
12+3+4+5-6-7+89, 
12+3-4+5+67+8+9, 
12-3-4+5-6+7+89, 
123+4-5+67-89, 
123+45-67+8-9, 
123-4-5-6-7+8-9, 
123-45-67+89

2 个答案:

答案 0 :(得分:2)

它会添加不需要的结果,因为您的第一个循环遍历每个剩余数字并添加任意结果,评估为100,即使它已跳过一个数字也是如此。如果方法找到一个数字的解决方案,它会将解决方案添加到results - 这是正确的,但是如果它找不到解决方案,它仍会移动到下一个数字。这是跳过的数字的来源。如果没有数字的解决方案,它应该没有继续下一个数字。

至于如何修复它,这是一个不同的问题(但为什么不......)

这里的区别在于,如果任何数字存在使用所有剩余数字的表达式,则只能获得结果。

var results = [];
var operations = [ "+", "-", "" ];
var expected = 100;
var limit = 10;

function findExpression(expr, next) {
    if (next === limit) {
        eval(expr) === expected && results.push(expr);
    } else {
        operations.forEach(function(operation) {
            findExpression(expr + operation + next, next + 1);
        });
    }
}

$(document).ready(function() {
    findExpression("1", 2);
    for(var i=0; i<results.length; i++) {
        $("#foo").append(results[i]+"<br />");
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<body>
<div id="foo"></div>
</body>

答案 1 :(得分:1)

跳过某些数字的原因是在这个循环中:

for (var i = eval(expr.substring(expr.length - 1, expr.length)) + 1; i <=    
      nums.length; i++) {

在第二次迭代中,它将递增表达式中的最后一位数,这将在连续递归中产生间隙。简而言之,那个循环不应该存在。

我建议不使用eval的解决方案,不是因为它会有某种危险,而是因为它会导致重大的性能损失。

相反,您可以将数值变量更新为表达式所代表的内容。事实上,我建议使用两个这样的变量,一个用于前一个术语的总和,另一个用于最后一个术语,因为那个变量可能仍然需要用更多的数字进行扩展。

为了促进符号影响表达式的不同方式,我已经为每个符号定义了一个函数:它采用上面提到的数值,也是最后一个数字,并返回更新的值。

这是一个使用该想法的工作片段(ES6语法),您会注意到性能的显着改善:

function find100(digits, signs) {
    const loop = (expr, i, [sum, value]) =>
        // Not yet all digits used?
        i < digits.length ?
            // Apply each of the signs in turn:
            Object.keys(signs).reduce( (results, sign) =>
                // Recurse, passing on the modified expression, the sum of the 
                // preceding terms, and the value of the last term. As '+' is
                // not any different than '' before the first digit, skip '+':
                sign != '+' || i ?
                    results.concat(loop(expr+sign+digits[i], i+1, 
                                        signs[sign](sum, value, digits[i]))) :
                    results,
                [] ) :
        // All digits were used. Did it match?
        sum+value == 100 ? [expr] : [];
    // Start recursion
    return loop('', 0, [0, 0]); 
}

var nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// define how each sign should modify the expression value:
var signs = {
    '+': (sum, value, digit) => [sum+value, digit],
    '-': (sum, value, digit) => [sum+value, -digit],
    '' : (sum, value, digit) => [sum, value*10 + (value<0 ? -digit : digit)]
};
var results = find100(nums, signs);

console.log(results);

请注意,这也会输出以下表达式:

-1+2-3+4+5+6+78+9

这是因为代码也尝试在第一个数字之前的符号。我认为这也包含在输出中是相关的。