当前位置: 首页 > 知识库问答 >
问题:

在阶乘中,return是如何工作的?

卢普松
2023-03-14

我正从最基本的方面着手研究递归,因为我在过去很挣扎。我在看这个阶乘解:

int Factorial(int n)
{
    if (n == 0)
    {
        return 1;
    }
    return n * Factorial(n - 1);
}

因此函数会一直调用自己,直到<code>n==0</code>为止。然后在每次调用时返回值。这就是我不明白的:当它从基本条件返回时,它返回一个值,并不断添加每个调用的值。

这些值存储在哪里,以便最终能够返回总和?

共有3个答案

辛龙野
2023-03-14

所有内容都存储在堆栈中相应的激活记录1中。

您的函数int Factorial(int n);正在使用递归。第一个返回称为基本情况,它充当终止条件,第二个返回是递归步骤,这会导致调用同一个函数,其中一个参数(在基本情况if条件语句中使用)被修改。结果在达到基本条件后累积并返回。

这里有一个很好的递归解释,使用完全阶乘作为示例

1.在计算机体系结构级别,结果存储在特定的寄存器中,累积最终结果。

南门宇
2023-03-14

为了在给定的输入上递归计算其结果,递归函数使用不同的(以某种方式“较小”)输入调用(副本)自身,并使用此调用的结果来构造其结果。递归调用执行相同的操作,除非已达到基本情况。因此,调用堆栈在此过程中发展。例如,为了计算阶乘(3),这依次递归调用Factorial(2),Factorial(1),Factorial(0)(“清盘”堆栈),此时递归终止为Factorial(0) = 1,然后堆栈以相反的顺序展开,结果在沿着调用堆栈返回初始调用帧Factorial(3)的途中计算, 其中,最终结果计算为 3*2 =: 6,最后返回。在此示例中,函数返回单个值。

湛联
2023-03-14

正如您正确解释的那样,函数Factorial调用自己,除非参数为0。由于它使用n-1调用自己,最终递归调用序列将在n步骤后停止。每个调用都是函数的新实例或调用,计算将暂停,直到新实例返回。给定实例的状态保存在内存中称为堆栈的区域(在大多数当前环境中)。

当使用值<code>0</code>调用的最后一个实例返回时,最后一个调用实例可以完成计算并将<code>1*1</code>返回给调用方,依此类推,直到初始实例可以完成乘法并返回其参数的阶乘。

-------停止阅读此处,除非您对实现细节感兴趣--------

上面代码的第一个问题是,如果< code>n是负数或非常大,程序可能会调用未定义的行为,因为太多的递归调用,每个调用都会消耗一些堆栈空间,这是一种有限的资源。

第二个问题:< code>int类型的可能值范围有限。在大多数当前系统中,< code>int以32位存储。阶乘增长非常快,因此< code>Factorial(12)得出< code>479001600,当乘以< code>13时会导致算术溢出,因为结果< code>6227020800无法容纳在32位中。算术溢出调用未定义的行为。

您可以在此网站上尝试编译您的阶乘函数:http://gcc.godbolt.org/#它有一个交互式编译器并显示汇编代码。尝试不同的优化器选项:

>

  • -m32-O1将给出一个递归版本,不太难理解。
  • -m32-O2表明,编译器足够精明,可以将递归C实现转换为迭代汇编版本,比-O1-版本更短更快
  • -m32-O3生成的代码复杂得多,涉及MMX寄存器上的SIMD指令……可能比-O2“版本更快,但完全过度设计,因为一个简单的小查找表不需要单独测试就足够了:

    int Factorial(int n) {
        static int f32[16] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320,
                               362880, 3628800, 39916800, 479001600,
                               0, 0, 0 };
        return f32[n & 15];
    }
    

    请注意如何利用未定义的行为语义学(任何事情)来简化实现并删除测试。编译器被允许这样做,其中一些确实这样做了。

  •  类似资料:
    • 我在Python中定义了一个阶乘函数,如下所示: python程序返回一个非常大的数值,计算值为100(如预期的那样)。朱莉娅返回0。对于较小的数字(如10),它们都起作用。 我有两个问题: 为什么Python可以处理这个问题,而Julia不能。 为什么Julia不抛出错误而只打印0?

    • 计算一个数字的阶乘。 使用递归。如果 n 小于或等于 1 ,则返回 1 。否则返回 n 和 n - 1 的阶乘。如果 n 是负数,则会引发异常。 const factorial = n => n < 0 ? (() => { throw new TypeError('Negative numbers are not allowed!'); })()

    • 在Spark中是如何工作的? 如果我们注册一个对象作为一个表,会将所有数据保存在内存中吗?

    • 我从网上和论坛上看到了关于BatchSize的相关主题,但我仍然不明白一些部分。所以让我们描述一下我理解的和不理解的。 批量取数:选择取数的优化策略。Hibernate通过指定主键或外键列表,在一次选择中检索一批实体实例或集合。 让我们有JPA 2.0,带有Hibernate实现。这些实体: } 因此,我懒得去了解产品中的制造商。因此,当我执行select fetching时,就完成了。所以我有很

    • 我的项目中的三个模型对象(本文末尾的模型和存储库片段)之间确实存在关系。 当我调用时,它会触发三个select查询: (“sql”) (对我来说)那是相当不寻常的行为。在阅读Hibernate文档后,我认为它应该始终使用连接查询。当类中的更改为时,查询没有区别(使用附加选择进行查询),当更改为时,城市类的查询也一样(使用JOIN进行查询)。 当我使用抑制火灾时,有两种选择: 我的目标是在所有情况下

    • 我经常把文本输出到文件中。我想知道:是如何工作的? 当我调用时,它是否在文件上写入文本?如果它不写文本,我需要使用flush函数来写数据吗? 例如: 如果while循环中发生错误,文件将在不写入数据的情况下关闭。如果我在while循环中使用函数,那么为什么要使用?如果我错了,请纠正我。