查找数字n
的阶乘的递归程序的复杂性是多少?我的预感是它可能是O(n)
。
答案 0 :(得分:35)
如果将乘法设为O(1)
,则是,O(N)
是正确的。但请注意,在有限硬件上乘以两个任意长度x
的数字不 O(1)
- 因为x
趋于无穷大,乘法所需的时间会增长(例如,如果您使用Karatsuba multiplication,则为O(x ** 1.585)
)。
理论上你可以用Schönhage-Strassen对数量足够大的数字做得更好,但我承认我没有真正的世界经验。 x
,“长度”或“数字位数”(无论以何种为基础,无论如何对N的大O都无关紧要,当然也会随着O(log N)
而增长。
如果您的意思是将问题限制为足够短的数字因子乘以O(1)
,那么N
无法“趋于无穷大”,因此大O符号是不合适的。
答案 1 :(得分:19)
当您表达算法的复杂性时,它始终是输入大小的函数。如果您乘以的数字是固定大小,则假设乘法是O(1)
操作是有效的。例如,如果要确定计算矩阵乘积的算法的复杂性,可以假设矩阵的各个组件具有固定大小。那么假设两个单独的矩阵分量的乘法是O(1)
是有效的,你可以根据每个矩阵中的条目数计算复杂度。
但是,当您想要计算出计算N!
的算法的复杂性时,您必须假设N
可以任意大,因此假设乘法是{ {1}}操作。
如果你想将一个n位数与一个m位数相乘,那么天真算法(你手工做的那种)需要时间O(1)
,但算法更快。
如果您想分析用于计算O(mn)
N!
然后在for循环中的第k个步骤,您将 factorial(N)
f=1
for i = 2 to N
f=f*i
return f
乘以(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]) =
强>
您可以使用更快的乘法算法来改善此性能,例如Schönhage-Strassen,它需要{n = 2个数字的时间O(N^2 (log N)^2)
。
提高性能的另一种方法是使用更好的算法来计算O(n*log(n)*log(log(n)))
。我所知道的最快的一个首先计算N!
的素数因子分解,然后乘以所有素数因子。
答案 2 :(得分:16)
假设你在谈论有史以来最天真的因子算法:
factorial (n):
if (n = 0) then return 1
otherwise return n * factorial(n-1)
是的,该算法是线性的,在O(n)时间内运行。这是因为它每次递减值n
时执行一次,并且它递减值n
直到它达到0
,这意味着函数被递归调用n
倍。当然,这是假设递减和乘法都是常数运算。
当然,如果你以其他方式实现阶乘(例如,使用递归而不是乘法),最终会得到一个更加时间复杂的算法。不过,我不建议使用这样的算法。
答案 3 :(得分:2)
递归阶乘的时间复杂度为:
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)。