神秘的堆栈溢出
Mysterious Stack Overflow
所以我写了一个程序,利用欧几里得算法来查找 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 转移过来的(
示例输出
我尝试过的事情:
- 将 GCD 函数拆分为 2
- 仅通过函数传递引用
首先,您的解释不清楚,从您的代码中,我了解到,对于每个8<=i<=n
,您都会采取所有可能x, y
y<=i
和x<=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;
}
你得到的堆栈溢出是由使用太深的递归调用引起的:每次调用函数时,都会在(调用(堆栈中创建一个新的堆栈帧(保存局部变量、参数和可能的其他东西(。仅当从函数返回(通常或通过异常(时,才会释放此帧。但是对于递归调用,您不会在从第二个函数调用返回之前从第一个函数调用返回,而第二个函数调用又只在第三个函数调用之后返回,依此类推。因此,堆栈帧堆积在堆栈上,通常大小约为 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;
}
(直播(
特别要注意的是,我没有使用任何全局变量。
以上可能会让你觉得递归是一种不可用、无用的构造。事实并非如此。许多算法使用递归最清晰地表达。当将递归调用作为最后一个语句时,可以使用称为尾部调用优化的优化: 然后被调用的函数重用调用函数的堆栈帧,因此不再使用任何内存。
不幸的是,由于各种原因,这种优化在像C++这样的语言中实现非常棘手。
其他语言,主要是函数式语言,使用它,因此也使用递归而不是循环。这种语言的一个例子是 Scheme,它甚至需要实现才能进行上述优化。
最后一点:您可以在此处使用递归调用实现 GCD 计算,因为如您所见,最大深度将17 + 1
,该深度应该足够小以适合任何(嵌入式系统之外(调用堆栈。不过,我仍然会使用迭代版本:它具有更好的性能,更适合该语言的习语,并且是"更安全"的方式。
- 在 leetcode 上提交解决方案时出现堆栈缓冲区溢出错误
- 我的 int main() 中出现堆栈溢出错误
- C++ 对象数组堆栈溢出
- 有没有一种方法可以捕获进程中的堆栈溢出?C++Linux
- 对象接收堆栈溢出异常 c++ 的排序向量
- 将公共递归转换为尾递归,因为大型输入的堆栈溢出
- C++ 中递归期间的堆栈溢出
- 启动 dll 时 C# 环境堆栈溢出
- 在C++中使用数组时如何防止堆栈溢出?
- 如何修复递归函数导致的堆栈溢出错误?C++
- 当我尝试为结构分配新指针时出现堆栈溢出错误
- 为什么析构函数无休止地调用自己(导致堆栈溢出)?
- 为什么堆栈溢出?如有建议,不胜感激
- 主函数执行时C++堆栈溢出异常
- 如何在不导致堆栈溢出的情况下计算非常大的数字和很小的 HCF.我正在使用欧几里得算法
- 递归克隆图时的堆栈溢出
- 防止 CRTP 特征码在"pure virtual"调用中堆栈溢出
- 静态数组溢出堆栈(seg 错误 11)仅在类定义内部时,否则不会..?
- 使用递归的溢出堆栈
- c++中栈溢出和分段错误的危险