【问题标题】:JavaScript recursion: Maximum call stack size exceededJavaScript 递归:超出最大调用堆栈大小
【发布时间】:2012-03-18 20:24:55
【问题描述】:

我有一个递归函数可以在画布上移动一些圆圈。被覆盖的圆圈被放大(放大)并且所有其他圆圈被推开。 推动的圆圈推动其他圆圈,依此类推,直到缩放完成。

我收到一个错误“超出最大调用堆栈大小”,我理解这个问题,但我只是不知道如何解决它...... 我找到了解决递归问题的三种可能的解决方案:

  1. 将递归改为迭代
  2. 使用memoization
  3. 使用设置超时

但我认为我不能使用它们:

  1. 由于需要的操作数未知,我无法实现迭代
  2. 我不太了解 memoization,但我认为它也不适合(或者我错了,有人可能会以不同的方式告诉我?)
  3. 我不能使用 SetTimeout,因为它应该在这个特定的动画中阻塞函数调用。

我该如何解决这个问题?

// Pushes circles aside when some other circle leans on these circles (on zoom in)
var moveCirclesAside = function(circle1, circleToSkip, groupOfMoves) {
    var count = circles.length;
    for (var i = 0; i < count; i++) {

        // Skip the same circle
        if (i == circle1.i) {
            continue;
        }

        // Also skip the circle which was intended not to move any further
        if (circleToSkip != null && i == circleToSkip.i) {
            continue;
        }

        // Get second circle
        var circle2 = circles[i];

        // Calculate a distance between two circles
        var dx = circle2.x - circle1.x;
        var dy = circle2.y - circle1.y;
        var distance = Math.sqrt((dx * dx) + (dy * dy));

        // If circles already collided need to do some moving...
        if (distance <= circle1.r + circle2.r + OD.config.circleSpacing) {

            // Get collision angles
            var angle = Math.atan2(dy, dx);
            var sine = Math.sin(angle);
            var cosine = Math.cos(angle);

            // Some circle position calculation
            var x = OD.config.circleSpacing;
            var xb = x + (circle1.r + circle2.r);
            var yb = dy * cosine - dx * sine;

            // Save each state (move) of any circle to the stack for later rollback of the movement
            groupOfMoves.push(copyCircleByVal(circle2));

            // Move the circle
            circle2.x = circle1.x + (xb * cosine - yb * sine);
            circle2.y = circle1.y + (yb * cosine + xb * sine);

            // Make sure that circle won't go anywhere out of the canvas
            adjustCircleByBoundary(circle2);

            // If moved circle leans against some other circles make sure that they are moved accordingly
            // And such related moves must be grouped for correct rolback of moves later - so we pass 'groupOfMoves' var
            moveCirclesAside(circle2, circle1, groupOfMoves);
        }
    }
};

【问题讨论】:

    标签: javascript recursion


    【解决方案1】:

    1) 由于需要的操作数未知,我无法实现迭代;

    好吧,我没有看过你的代码,但是线性递归的一般避免(你这里有一个线性递归)看起来像这样:

    while (1 == 1) {
        if (breakcondition)
            break;
        doSomeCode()
    }
    

    因此您不必像for- 循环案例那样知道确切的迭代次数。

    【讨论】:

      【解决方案2】:

      这种溢出并不奇怪,因为算法在迭代时会增加堆栈,但退出条件是不可预测的,动作没有紧密定位(它们对附近的圆圈有连锁反应),所以处理时间会很混乱。

      我会重新考虑算法。考虑找到两个最近的圆,如果它们之间的距离超过给定的阈值,则中止。否则将它们分开一点并重复。

      【讨论】:

        【解决方案3】:

        您无需知道制定迭代解决方案所需的数量或操作。关键是用您自己的一个替换 JavaScript 堆栈。检查此答案以查看如何实现它的示例:Link

        您可以在 JavaScript 中将 Array 对象用作堆栈,因为它支持 push()pop()

        PS:正如 Jim 的回答所建议的,您通常可以找到不需要这么多递归级别的算法。

        【讨论】:

        • 您的回答也很有帮助,但遗憾的是我只能接受一个答案...谢谢!
        【解决方案4】:

        尽量确保递归步骤仅针对大于基本情况的情况进行。例如这里的快速排序:

        function qsort(k){
        
        if(k == []){
            return k;
        }
        if(k.length == 1){
            return k;
        }
        
        //pick random pivot
        
        var p = Math.floor(Math.random()*k.length);
        console.log("pivot is" +  p + "\n");
        
        //set left and right iter
        
        var l = 0; var r = k.length - 1;
        
        while( l < r){
        console.log('hi\n');
        //move l until its element is bigger than pivot
        
        while(k[l] < k[p] && l < k.length) {
            l++;
            console.log('h1i\n');
        
        }
        
        //move r until its element is smaller than pivot
        
        while(k[r] > k[p] && r >= 0) {r--;
        
            console.log('h2i\n');
        }
        
        //swap k[l] with k[r]
        
        var temp = k[l]; k[l] = k[r]; k[r] = temp;
        }
        
        if(l == r){
        //swap k[l] with k[p]
        
        temp = k[l]; k[l] = k[p]; k[p] = temp;
        
        }
        
        
        
        var lk = k.slice(0,p); var rk = k.slice(p,k.length);
        
        console.log(lk);
        console.log(rk);
        
        if(lk.length > 1){
         lk = qsort(lk);
        }
        if(rk.length > 1){
         rk = qsort(rk);
        }
        
        result = lk.concat(rk);
        
        console.log(result);
        
        return result;
        
        }
        
        var k = [23,245,67,34,24];
        var result = qsort(k);
        console.log(result);
        //document.write(result);
        

        如果您使用 lk != [] 而不是 lk.length &gt; 1 或没有检查,它有时可能会导致调用堆栈大小超出错误,并且有时会根据选择的枢轴来工作。。 p>

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-07-24
          • 2019-09-09
          • 2019-05-21
          • 1970-01-01
          • 2014-01-18
          • 2020-07-10
          • 2014-08-03
          相关资源
          最近更新 更多