【问题标题】:Do both these factorial functions run in O(n)?这两个阶乘函数都运行在 O(n) 中吗?
【发布时间】:2025-12-25 21:50:11
【问题描述】:

递归函数定义如下:

function factrec($x) {
    if($x <= 1) {
        return $x;
    } else {
        return $x * factrec($x - 1);
    }
}

在这里迭代:

function factiter($x) {
    $y = $x;
    while($y > 1) {
        $x *= ($y - 1);
        $y--;
    }
    return $x;
}

我在递归函数中读到,主体是 O(1),递归调用 O(n-1) 使其成为 O(n),但对于迭代,它也是 O(n) 吗?

【问题讨论】:

  • function factorial($x) { for ($y = $x; $y-- &gt; 1; $x *= $y); return $x; }:PHP 太棒了。 (希望我没有犯错:D)

标签: php algorithm big-o


【解决方案1】:

是的,两个版本都在 O(n) 时间内运行。迭代版本的推理与递归版本基本相同:循环体运行时间为O(1),执行n次。

但需要注意的是,迭代版本在 O(1) 空间中运行,而递归版本使用 O(n) 堆栈空间(因为递归深度为 n)。

【讨论】:

  • 这实际上非常适合我的答案,我只需要知道为什么在我的基准测试中递归比另一个慢得多。我会把它记在笔记里。
  • 可以重构递归方法以允许尾调用,因此在空间中也是 O(1)。但它确实使它不太清楚。
  • gcc -O2 将递归阶乘转换为迭代(参见*.com/questions/405770/why-are-compilers-so-stupid/…)。如果 GHC 在简单的情况下做了类似的事情,我不会太惊讶。再一次,程序员低估了编译器编写者;)但是,是的,PHP 不会优化它。
【解决方案2】:

是的,它是 O(n)。试着想象一下当x 的值很大时处理器将运行多少次操作。值大时,它不会变得更复杂,并且总是以线性方式进行相同的操作。

【讨论】:

  • 实际上对于处理器来说这不是O(n),因为乘法不是O(1) 操作。但是,如果您将乘法视为O(1),它就是O(n)
  • 我很高兴我的想法是正确的。我对这两者进行基准测试感到无聊,在计算更高阶乘时,递归函数飙升至迭代之上。只是想知道无论结果如何,它们是否都是 O(n)。然后我又一次愚蠢并使用PHP来做到这一点..也许这就是它更高的原因。
  • 原始数据类型的乘法是 O(1)。只要它小于 64 位整数,它就会是 O(1)。但是,是的,如果它是任意精度整数类型,那么您是对的。但两种算法都成倍增加。这不会让他们分开。在递归版本中为每个连续调用创建堆栈帧肯定会减慢它的速度。
  • 抱歉,我假设 $x 和 $y 具有 int 数据类型。阶乘函数通常在整数上计算。否则你需要 Gamma 函数。