JavaScript递归:超出最大调用堆栈大小

时间:2012-02-29 10:44:33

标签: javascript recursion

我有一个递归功能,可以在画布上移动一些圆圈。被遮挡的圆圈被放大(放大),所有其他圆圈被推开。 推动的圆圈推动其他圆圈,依此类推,直到缩放完成。

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

  1. 将递归更改为迭代
  2. 使用memoization
  3. 使用SetTimeout
  4. 但我认为我不能使用它们:

    1. 由于需要的操作数量未知,我无法实现迭代
    2. 我不太了解备忘录,但我认为它也不适合(或者说我错了,有人能告诉我不同​​的方式?)
    3. 我不能使用SetTimeout,因为它应该阻止此特定动画的函数调用。
    4. 如何解决此问题?

      // 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);
              }
          }
      };
      

4 个答案:

答案 0 :(得分:8)

  

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

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

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

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

答案 1 :(得分:6)

这种溢出并不令人惊讶,因为算法在迭代时会增加堆栈,但退出条件是不可预测的,动作没有紧密定位(它们对附近的圆圈产生连锁效应),因此处理时间将是混乱的。

我会重新考虑这个算法。考虑找到两个最接近的圆圈,如果它们比分开的给定阈值更远,则中止。否则将它们分开并重复一遍。

答案 2 :(得分:4)

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

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

PS:正如Jim的回答所暗示的那样,你通常可以找到一种不需要这种递归级别的算法。

答案 3 :(得分:0)

尝试确保递归步骤仅针对大于基本情况。例如,在快速排序中:

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.length > 1之类的lk != []代替@Override public Observable<List<Item>> getResults() { return getResultsFromDatabase().toObservable().switchIfEmpty(getResultsFromNetwork()); } @Override public Observable<List<Item>> getResultsFromNetwork() { System.out.println("getting results from network"); return api.getData().doOnNext(new Consumer<List<Item>>() { @Override public void accept(List<Item> items) throws Exception { itemDao.insert(items); } }); } @Override public Maybe<List<Item>> getResultsFromDatabase() { System.out.println("getting coins from database"); return itemDao.getAllItems(); } 而不是检查,则有时可能会使调用堆栈大小超出错误,有时会根据选择的哪个支点工作