【问题标题】:Calling twice a recursive function调用两次递归函数
【发布时间】:2017-02-03 14:43:20
【问题描述】:

我想做的是计算帕斯卡三角形中的坐标值,我有一个带有 X 和 Y 轴的表格,我的帕斯卡三角形在顶部和左侧对齐,我想做的是创建一个函数来返回特定点的值[例如 (3,2) = 2 ] 这是我尝试过的:

var getCalc = function value( x, y ) {

    if( y == 0 || y == x ){
        return 1;
    }

    return value(x - 1, y - 1) + value(x - 1, y);
}

这是带有帕斯卡三角形的表格:

我们在这里得到的是一个递归函数,在最后一次返回中调用了两次,执行它时出现以下错误:

未捕获的 RangeError:超出最大调用堆栈大小

我也想知道如何使用 ES6 来做到这一点

【问题讨论】:

  • 我刚刚做了,它工作正常......
  • 是什么让你认为 ES6 会有所不同?
  • 请重新评估我更新后的答案。
  • 在你的函数中你有一个错字:如果你在图像中看到 - x,y 值从 1 开始,所以在你的函数中只需将 if( y == 0 更改为 if( y == 1。你得到正确的结果
  • 你用什么参数来调用它? getCalc(3, 2) 对我来说很好。

标签: javascript recursion ecmascript-6


【解决方案1】:

JavaScript 主要是 ECMAScript,你正在做任何违反版本 6 规范的事情,但你可以lint here

  • 您的 pascal triangle 表看起来像是用于行而不是标准对角线(此答案处理对角线,但如果您想要的话,最后会提供一个非对角线示例)。
  • 您的代码应该通过美化器运行。
  • 无需制作 2 个函数(getCalc 和 value)
  • 当 y == x 时,结果不应总是 1
  • 您对错误的框求和 (x - 1, y - 1) 像正方形而不是三角形 (x, y - 1)

修正后的函数:

var value = function(x, y) {
    if (y == 0 || x == 0) {
        return 1;
    }
    return value(x - 1, y) + value(x, y - 1);
};

此外,如果数字非常大或无效,您仍然可能会“超出调用堆栈大小”,安全包装器可能如下所示:

var safeValue = function(x, y) {
    if (x > 20 || y > 20 || y < 0 || x < 0 || Math.floor(x) != x || Math.floor(y) != y ) {
        return null;
    }
    return value(x, y);
};

如果您在任何浏览器中使用 Web 开发人员工具创建断点,您可以跟踪每个递归以及所有变量是什么。 您可以添加一个深度变量来跟踪发生了多少次递归。

输出verification:

(-1,0)=null
(0,0)=1
(2,3)=10
(2.5,3)=null

像大多数递归一样,您也可以将其表示为循环,并且深度限制大大降低。

var value2 = function(x, y) {
    var xp;
    var yp;
    var theArray = {};
    for (xp = 0; xp <= x; xp += 1) {
        for (yp = 0; yp <= y; yp += 1) {
            if (xp === 0 || yp === 0) {
                if (!theArray[xp]) {
                    theArray[xp] = {};
                }
                theArray[xp][yp] = 1;
            } else {
                if (!theArray[xp]) {
                    theArray[xp] = {};
                }
                theArray[xp][yp] = theArray[xp - 1][yp] + theArray[xp][yp - 1];
                //memory saving step
                if (theArray[xp - 1][yp]) {
                    delete theArray[xp - 1][yp];
                }
            }
        }
    }
    return theArray[x][y];
};

正如你从计时测试中看到的那样,它有很大的不同:

var a=new Date().getTime();console.log(value(13,13));console.log('time='+(new Date().getTime()-a));
10400600
time=4526

var a=new Date().getTime();console.log(value2(13,13));console.log('time='+(new Date().getTime()-a));
10400600
time=0

因此,将函数从O(n^n) 内存占用到 O(n),有一个函数将使用 O(n) 时间而不是 O(n*n);

var value3 = function(x, y) {
    var sum = 0;
    var tmp;
    var xp;
    for (xp = 0; xp <= x; xp += 1) {
        if (xp === 0) {
            tmp = 1;
        } else {
            tmp = (y + xp) / xp;
        }
        if (sum === 0) {
            sum = tmp;
        } else {
            sum = sum * tmp;
        }
    }
    return sum;
};

再次,时间差异很大;

var a=new Date().getTime();console.log(value2(500,500));console.log('time='+(new Date().getTime()-a));
2.702882409454366e+299
time=24

var a=new Date().getTime();console.log(value3(500,500));console.log('time='+(new Date().getTime()-a));
2.7028824094543663e+299
time=0

或者如果你想要非对角行:

var value4 = function(x, y) {
    if (y < x) {
        return null;
    }
    var sum = 0;
    var tmp;
    var xp;
    for (xp = 0; xp <= x; xp += 1) {
        if (xp === 0) {
            tmp = 1;
        } else {
            tmp = (y - (xp - 1)) / xp;
        }
        if (sum === 0) {
            sum = tmp;
        } else {
            sum = sum * tmp;
        }
    }
    return sum;
};

【讨论】:

  • 我用我提到的表格的图像更新了帖子,我提到的坐标是表格的行和列
  • 只是部分尾递归。
  • 你的意思是什么?,如果我使用你的代码,例如,我没有得到位于 (6,8) = 21 的值
  • @user5429739 请参阅“或者如果您想要非对角行”部分。
【解决方案2】:

还有getCalc(3,2) = 3 //这个函数是错误的

请看一下打印帕斯卡三角形的this implementation

getCalc(3,2)
-> getCalc(2,1) + getCalc(2,2)
   ->getCalc(1,0) + getCalc(1,1) + 1
      ->1 +1 +1= 3

构建整个三角形的片段:

function pascalRecursive(n, a) {

  if (n < 2) return a; // We already have the top row

  var prevTier = a[a.length-1];
  var curTier = [1];

  for (var i = 1; i < prevTier.length; i++) {
    curTier[i] = prevTier[i] + prevTier[i-1];
  }
  curTier.push(1);
  a.push(curTier);

  return pascalRecursive(n-1, a);
}
var triangle = pascalRecursive(100, [[1]]);
alert(triangle[1]);
///sum
alert(triangle[1].reduce(function(pv, cv) { return pv + cv; }, 0))

【讨论】:

  • 嗯,如果它有效,那么它不会生成正确的数字,我应该怎么做才能得到 (3,2) = 2?这就是帕斯卡三角形的真正价值
  • 我刚刚测试过,如果输出不是预期的,那么你的代码是错误的......请继续学习并阅读答案中提到的参考......
猜你喜欢
  • 1970-01-01
  • 2012-08-29
  • 1970-01-01
  • 1970-01-01
  • 2019-12-14
  • 2018-04-10
  • 2020-04-21
  • 1970-01-01
  • 2021-04-15
相关资源
最近更新 更多