【问题标题】:Complexity of recursive factorial program递归阶乘程序的复杂性
【发布时间】:2011-01-20 14:22:32
【问题描述】:

找到数字n 的阶乘的递归程序的复杂性是多少?我的预感是它可能是O(n)

【问题讨论】:

  • 我不知道,伙计。我可以编写一个非常糟糕的递归阶乘程序,它至少需要 O(n!) 时间才能完成。如果你想分析一个算法,你需要实际的算法。

标签: complexity-theory factorial


【解决方案1】:

递归阶乘的时间复杂度为:

factorial (n) {    
    if (n = 0) 
        return 1
    else
        return n * factorial(n-1)
}

所以,

一次递归调用的时间复杂度为:

T(n) = T(n-1) + 3   (3 is for As we have to do three constant operations like 
                 multiplication,subtraction and checking the value of n in each recursive 
                 call)

     = T(n-2) + 6  (Second recursive call)
     = T(n-3) + 9  (Third recursive call)
     .
     .
     .
     .
     = T(n-k) + 3k
     till, k = n

     Then,

     = T(n-n) + 3n
     = T(0) + 3n
     = 1 + 3n

用 Big-Oh 符号表示,

T(N)与n成正比,

因此, 递归阶乘的时间复杂度为 O(n)。 由于在递归调用过程中没有占用额外的空间,空间复杂度为 O(N)。

【讨论】:

  • 这个例子的小注释。如果您要试用代码,请确保使用 == 而不是 =,因为它会分配而不是比较。
  • 是的@AndresZapata 看起来只是错字:D
【解决方案2】:

如果你把乘法当作O(1),那么是的,O(N) 是正确的。但是,请注意,在有限硬件上将任意长度的两个数字相乘 x 不是 O(1) - 因为x 趋于无穷大,所以乘法所需的时间会增加(例如,如果您使用 @ 987654321@,它是O(x ** 1.585))。

理论上,使用Schönhage-Strassen 可以更好地处理足够大的数字,但我承认我没有实际经验。 x,“长度”或“位数”(在任何基础上,对于 N 的 big-O 都无关紧要,当然随着 O(log N) 增长。

如果您的意思是将您的问题限制在足以在 O(1) 中相乘的数字的阶乘上,那么 N 不可能“趋于无穷大”,因此大 O 表示法是不合适的。

【讨论】:

  • 您只能将适合您记忆的数字相乘。所以我不明白乘法如何克服O(1)。能详细解释一下吗?
  • @tur1ng,对于时间 额外空间的要求,您都有 big-O 行为 [[超出输入和输出所需的明显空间,这将是荒谬的]](尽管通常一个表示时间,除非明确提及空间)。乘法在额外空间中是 O(1),在时间上是 O((log N)**1.585)(使用 Karatsuba)。 “物理上可达的宇宙”(因此任何实际可以想象的机器)是有限的这一事实与 CS 无关:分析通常(隐含地)假设“图灵机”根据定义具有无限长的“磁带”(无限存储)。
  • 顺便说一句 @tur1ng,有了这个绰号,您肯定应该更熟悉图灵机及其无限长的磁带(“确实适合您的记忆”-pah!-)-从 en.wikipedia.org/wiki/Turing_machine 开始。
  • 显然,也有 Fürer 的算法(理论上)和改进的算法
【解决方案3】:

当您表达算法的复杂性时,它始终是输入大小的函数。如果您要相乘的数字是固定大小的,则仅假设乘法是 O(1) 操作才有效。例如,如果您想确定计算矩阵乘积的算法的复杂性,您可能会假设矩阵的各个分量是固定大小的。那么假设两个单独的矩阵分量相乘为O(1) 是有效的,并且您将根据每个矩阵中的条目数计算复杂度。

但是,当您想弄清楚计算 N! 的算法的复杂性时,您必须假设 N 可以任意大,因此假设乘法是 O(1) 运算是无效的。

如果您想将一个 n 位数与一个 m 位数相乘,那么简单的算法(您手动执行的那种)需要时间 O(mn),但有更快的算法。

如果要分析计算N!的简易算法的复杂度

    factorial(N)
       f=1
       for i = 2 to N
          f=f*i

       return f

然后在 for 循环的第 k 步,您将 (k-1)! 乘以 k。用于表示(k-1)! 的位数为O(k log k),用于表示k 的位数为O(log k)。所以将(k-1)!k 相乘所需的时间是O(k (log k)^2)(假设您使用朴素的乘法算法)。那么算法所用的总时间就是每一步所用时间的总和:

sum k = 1 to N [k (log k)^2] <= (log N)^2 * (sum k = 1 to N [k]) =
O(N^2 (log N)^2)

您可以通过使用更快的乘法算法来提高此性能,例如 Schönhage-Strassen,它需要时间 O(n*log(n)*log(log(n))) 来处理 2 个 n 位数字。

提高性能的另一种方法是使用更好的算法来计算N!。我所知道的最快的是首先计算N! 的素因数分解,然后将所有素因数相乘。

【讨论】:

    【解决方案4】:

    假设您谈论的是有史以来最天真的阶乘算法:

    factorial (n):
      if (n = 0) then return 1
      otherwise return n * factorial(n-1)
    

    是的,算法是线性的,在 O(n) 时间内运行。之所以如此,是因为它每次递减值n 时都会执行一次,并且递减值n 直到达到0,这意味着该函数被递归调用n 次。当然,这是假设减法和乘法都是常数运算。

    当然,如果您以其他方式实现阶乘(例如,递归地使用加法而不是乘法),您最终会得到一个时间复杂得多的算法。不过,我不建议使用这样的算法。

    【讨论】:

      猜你喜欢
      • 2013-04-28
      • 1970-01-01
      • 2019-10-22
      • 1970-01-01
      • 1970-01-01
      • 2016-01-28
      • 1970-01-01
      • 1970-01-01
      • 2015-04-19
      相关资源
      最近更新 更多