【问题标题】:c++ recursion with big numbers to find modc++ 大数递归以查找 mod
【发布时间】:2019-03-10 02:50:17
【问题描述】:

您好,在练习递归时,我发现了一个不使用 % 运算符即可找到模数的练习。 所以我写了我的函数,一切正常。 除非我击中 5 位或更多位的数字,否则此功能会失败。 而且我不确定我是做错了什么还是因为电话太多而失败了。 如果电话太多,正常吗?真的可以有太多的函数调用吗?如果将来我有一个真正有用的递归函数,我该如何防止它发生? 因为这对我来说真的没有意义。我做了河内递归塔,无论我要移动多少个磁盘,它都没有这个问题。

这是我的功能,前提是两个数字始终为正:

#include <iostream>

using namespace std;
int modulo(int n, int m) {
    if (n < m) return n;
    else return modulo(n - m, m);
}

int main()
{
    int n{ 0 }, m{ 0 };
    char check{ 'a' };
    do {
        cout << "Enter 2 positive integers to calculate their modulo: ";
        cin >> n >> m;
        if (cin.fail()) {
            cerr << "Numbers must be a positive integers. Please try again.\n";
            cin.clear(); //clear input stream
            cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); //discard any unprocessed characters
        }
        else if (n < 0 || m <= 0) {
            cerr << "Numbers must be positive and second number cannot be zero.\nPlease try again.\n";
        }
        else {
            cout << "n%m = " << n << "%" << m << " = " << modulo(n, m) << endl;
            cout << " Try again? (enter 'n' to quit): ";
            cin >> check;
        }
    } while (check != 'n');
}

错误是:

GuessNumber.exe 中 0x00007FF77D5C2793 处的未处理异常: 0xC00000FD:堆栈溢出(参数:0x0000000000000001, 0x0000006F322F3F30)。

对于我尝试的数字 40001 % 10,它可以工作,但 44001 % 10 失败 44001 之后的一切对我来说也都失败了。其他号码我没试过

【问题讨论】:

  • 能否请您添加错误打印输出(如果有)?
  • 感觉就像由于递归调用堆栈而导致堆栈溢出。但是一个错误会有助于理解。
  • 导致错误的最小数字是多少?
  • 错误确实是堆栈溢出。我只是不明白我为什么要买一个。 GuessNumber.exe 中 0x00007FF77D5C2793 处的未处理异常:0xC00000FD:堆栈溢出(参数:0x0000000000000001、0x0000006F322F3F30)。对于我尝试的数字 40001 % 10,它可以工作,但 44001 % 10 失败
  • 您是否尝试过使用 40000 个磁盘的 Tower of Hanoi 程序?

标签: c++ recursion modulo


【解决方案1】:

如果递归太深,则程序会耗尽堆栈内存。它被称为堆栈溢出。

int modulo(int n, int m) 
{ 
    if (n < m) return n; 
    else return modulo(n - m, m); 
}

例如,modulo(1000000, 2) 调用modulo(999998, 2),后者调用modulo(999996, 2),依此类推,直到modulo(0, 2) 最后堆栈上有500001 个活动的modulo 函数。在任何合理的系统上,每个函数调用都应占用两个整数、返回地址和一个帧指针,至少占用 16 个字节。总共 8 MB 的堆栈空间,通常高于最大堆栈大小。

每个函数调用都必须等待下一个函数的结果,直到它可以完成计算并返回。在它返回之前,堆栈必须保存所有变量、参数和返回地址。返回地址是程序在return 语句之后应该恢复的位置。

这些调用会填满堆栈,直到达到最大限制并且程序崩溃。

在某些情况下,编译器可以将递归转换为循环。在这种情况下,由于递归在return 语句处,它可以简单地goto 到函数的开头,根本不执行调用。这称为尾调用优化

int modulo(int n, int m) 
{ 
    start:
    if (n < m) return n; 
    else {
       n -= m;
       goto start;
    }
}

如果您启用了优化(clang 或 G++ 中的 -O2,或 Visual C++ 上的发布模式),则不会发生崩溃,因为编译器会创建循环而不是递归。为避免崩溃,只需启用优化即可。

请注意,编译器不需要对此进行优化,也并非总是可以。这就是为什么不建议进行如此深度的递归的原因。

【讨论】:

    【解决方案2】:

    您正在递归到 4400 的深度,这是自找麻烦。这里也没有必要,因为您可以使用循环实现相同的算法:

    while (n >= m) n -= m ;
    return n ;
    

    【讨论】:

      猜你喜欢
      • 2021-12-08
      • 2018-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多