神秘的堆栈溢出

时间:2016-01-31 02:00:13

标签: c++ algorithm recursion overflow

所以我写了一个程序,利用euclid算法找到2个整数的GCD。

用户输入一个int(n),然后程序获取8和n之间的每个可能的整数组合,找到它们各自的GCD(递归地),并打印哪些GCD计算需要最多模数运算。

我让程序正常工作,但是我在n = 50左右得到堆栈溢出,并且需要工作至少3000.

我已经检查了一段时间的代码,但找不到问题。

#include<iostream>
#include <math.h>
using namespace std;

int cost, gcd, greatestCost, n, beginningA, beginningB, finalA, finalB, finalGCD, iteration;

void findGCD(int num1, int num2, int startingCost) {
    //findGCD
    //finds GCD of every combination (a,b) from i to n
    //prints those with the greatest number of modulus operations
    int a = num1;
    int b = num2;
    cost = startingCost;

    cost++;
    if (b%a > 0) {
        //cout << "gcd(" << b << "," << a << ") = ";
        findGCD(b%a, a, cost);
    }       
    else {
            gcd = a;

            if (cost > greatestCost) {
                greatestCost = cost;
                finalA = beginningA;
                finalB = beginningB;
                finalGCD = gcd;
            }
            //cout << "gcd(" << b << "," << a << ") = " << gcd << " With a  cost of: " << cost << endl;

            //do next iteration (2,8), (3,8) etc...
            if (++beginningA <= beginningB) {           //beginning A goes from 1-i first
               findGCD(beginningA, beginningB, 0);

            }
            else {
                    if (beginningA <= n) {      //begin next cycle with new b value (1,9), (2,9) while b <= n
                        beginningA = 1;                     //reset to 1 so it will increment from 1-i again
                        cout << "At i=" << iteration++ << "; gcd(" << finalA << "," << finalB << ") = " << finalGCD << 
                                " took " << greatestCost << " modulus operations" << endl;
                        findGCD(beginningA, ++beginningB, 0);           
                    }
                    else    //When it tries to continue iterating with a number > n
                            //print the last, most intensive, iteration and stop
                        cout << "At i=" << iteration++ << "; gcd(" << finalA << "," << finalB << ") = " << finalGCD << 
                        " took " << greatestCost << " modulus operations" << endl;
                  }
        }
}

int main() {

    greatestCost = 0;       //cost of the iteration with the most modulus operations
    beginningA = 1;         
    beginningB = 8;
    iteration = 8;

    cout << "Enter an integer greater than 8 " << endl; //receive n from user
    cin >> n;

    if (n <= beginningB)                                //begin GCD search, granted user input > 8
        cout << "Error!!! integer must be greater than 8";
    else
        findGCD(beginningA, beginningB, 0);     //algorithm begins at (1,8) 

    return 0;
}

此时我唯一可以想到的问题就是我在C ++中所做的事情,我不应该这样做(我是C ++新手并从java转移过来)

Sample Output

我尝试的事情:

  • 将gcd功能分成2个
  • 仅通过函数传递引用

2 个答案:

答案 0 :(得分:1)

首先,您的解释尚不清楚,从您的代码中我了解到,对于每个8<=i<=n,您可以x, y y<=ix<=y,并计算哪个gcd需要大多数步骤。

我已经重写了你的代码,以便findGCD只能找到2个数字的gcd,同时增加一些全局代价变量。

#include<iostream>
#include <math.h>
using namespace std;

int cost, gcd, greatestCost, n, beginningA, beginningB, finalA, finalB, finalGCD, iteration;

int findGCD(int a, int b) {
    cost++;
    if (b%a > 0)
        return findGCD(b%a, a);
    else
        return a;
}

int main() {

    greatestCost = 0;       //cost of the iteration with the most modulus operations
    beginningA = 1;         
    beginningB = 8;
    iteration = 8;

    cout << "Enter an integer greater than 8 " << endl; //receive n from user
    cin >> n;

    if (n <= beginningB)                                //begin GCD search, granted user input > 8
        cout << "Error!!! integer must be greater than 8";
    else {
        for ( int i = beginningB; i <= n; i++ ) {
            int greatestCost = 0, gcd0 = 1, i0 = 0, j0 = 0;
            for ( int t = beginningB; t <= i; t++ )
                for ( int j = 1; j <= t; j++ ) {
                    cost = 0;
                    int gcd = findGCD(j, t);
                    if ( cost > greatestCost ) {
                        greatestCost = cost;
                        gcd0 = gcd;
                        i0 = t;
                        j0 = j;
                    }
                }

            cout << "At i=" << i << "; gcd(" << j0 << "," << i0 << ") = " << gcd0 <<
                                     " took " << greatestCost << " modulus operations" << endl;

        }
    }
    return 0;
}

答案 1 :(得分:0)

使用过于递归的调用会导致您获得的堆栈溢出:每次调用函数时,都会在(调用)堆栈中创建一个新的堆栈帧(保存局部变量,参数和可能的其他东西) 。仅当从函数返回(通常或通过异常)时才释放该帧。但是对于递归调用,你不会在从第二个函数调用返回之前从第一个函数调用返回,而第二个函数调用仅在第三个之后返回,依此类推。因此堆栈帧堆积在堆栈上,通常大约为8 kB左右,直到堆栈的所有可用内存都被使用:堆栈溢出(你把它放得太多,因此溢出) )。

这可以通过使用迭代(使用循环)来解决:

从8增加到用户提供的最大值的外部,以及从0递增到外部循环的当前迭代变量的值的内部。这为您提供了您想要操作的所有值对。

计算最大公约数及其成本应计入函数中。

唯一剩下的就是从循环中调用该函数,以及如何跟踪最大值。

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

unsigned gcd(unsigned a, unsigned b, unsigned * const cost) {
  if (cost) {
    *cost = 0;
  }
  while (b != 0) {
    auto const rest = a % b;
    if (cost) {
      ++(*cost);
    }
    a = b;
    b = rest;
  }
  return a;
}

int main() {
  unsigned const n = 3500;
  unsigned greatestCost = 0;
  vector<pair<unsigned, unsigned>> pairs;
  for (unsigned b = 8; b <= n; ++b) {
    for (unsigned a = 0; a <= b; ++a) {
      unsigned cost;
      gcd(a, b, &cost);
      if (cost == greatestCost) {
        pairs.emplace_back(a, b);
      } else if (cost > greatestCost) {
        pairs.clear();
        pairs.emplace_back(a, b);
        greatestCost = cost;
      }
    }
  }
  cout << "Greatest cost is " << greatestCost << " when calculating the GCD of " << endl;
  for (auto const & p : pairs) {
    cout << "(" << p.first << ", " << p.second  << ")" << endl;
  }
  return 0;
}

(Live)

特别注意我没有使用任何全局变量。

以上可能会让你觉得递归是一种无法使用的无用结构。不是这种情况。许多算法使用递归最清晰地表达。当将递归调用作为最后一个语句时,可以使用称为尾调用优化的优化:然后被调用函数重用调用函数的堆栈帧,因此不再使用任何内存。

不幸的是,由于各种原因,这种优化在像C ++这样的语言中实现起来非常棘手。

其他语言,主要是功能语言,使用它,因此也是递归而不是循环。这种语言的一个例子是Scheme,它甚至要求实现能够进行上述优化。

作为最后一点:你可以在这里使用递归调用来实现GCD计算,因为你看到最大深度将是17 + 1,它应该足够小以适应任何(嵌入式系统之外)调用堆栈。我仍然使用迭代版本:它具有更好的性能,更好地适应语言的习语,并且是更安全的&#34;要走的路。