我有一个关于形成递归关系和计算时间复杂度的问题。
如果我们有一个递归关系T(n)= 2T(n / 2)+ c那么这意味着恒定的工作量c被分成2部分T(n / 2)+ T(n / 2)在绘制递归树时。
现在考虑阶乘的递归关系,即T(n)= n * T(n-1)+ c。如果我们遵循上述方法,那么我们应该将工作c划分为每个T(n-1)的n个实例,然后评估时间复杂度。但是如果以这种方式计算,那么回答将是O(n ^ n),因为我们将有O(n ^ n)个递归调用,这是错误的。
所以我的问题是为什么我们不能像第一种情况那样使用相同的方法将元素划分为子部分。
答案 0 :(得分:1)
让递归关系为T(n) = a * T(n/b) + O(n)
。
这种重复意味着存在一个递归函数:
a
子问题n/b
,则每个子问题的大小为n
当我们说原始问题被分为a
个子问题时,我们的意思是函数体中有a
个递归调用。
因此,例如,如果函数是:
int f(int n)
{
if(n <= 1)
return n;
return f(n-1) + f(n-2);
}
我们说问题(大小为n
)分为2个子问题,大小为n-1
和n-2
。递归关系为T(n) = T(n-1) + T(n-2) + c
。这是因为有2个递归调用,并且有不同的参数。
但是,如果功能如下:
int f(int n)
{
if(n <= 2)
return n;
return n * f(n-1);
}
我们说问题(大小为n
)只分为1个子问题,其大小为n-1
。这是因为只有1次递归调用。
因此,递归关系为T(n) = T(n-1) + c
。
如果我们将T(n-1)
与n
相乘,看起来似乎正常,我们会传达已发出n
个递归调用。
请记住,我们形成递归关系的主要动机是对递归函数进行(渐近)复杂性分析。尽管看起来n
不能从关系中丢弃,因为它取决于输入大小,但它不会像在函数本身中那样起作用。
但是,如果您正在讨论函数返回的值,那么它将是f(n) = n * f(n-1)
。在这里,我们乘以n
,因为它是一个实际值,将用于计算。
现在,来到c
中的T(n) = T(n-1) + c
;它只是表明当我们解决大小为n
的问题时,我们需要解决一个较小的问题,即大小n-1
和其他一些常数(常数时间)的工作就像比较,也可以执行乘法和返回值。
我们永远不能分开&#34;不断的工作量c
&#34;分成T(n/2)
和T(n/2)
两部分,甚至使用递归树方法。事实上,我们所分裂的问题分为两半。同样的&#34; c
&#34;在递归树的每个级别的每个递归调用中都需要工作量。
如果存在像T(n) = 2T(n/2) + O(n)
之类的递归关系,其中要完成的工作量取决于输入大小,那么每个级别要完成的工作量将在下一级别减半,就像你描述的那样。
但是,如果递归关系类似于T(n) = T(n-1) + O(n)
,我们就不会在下一个递归级别将工作量分成两半。我们只会在每个连续级别减少一个工作量(n
- 大小问题在下一级变为n-1
。
要检查工作量将如何随递归而变化,请将替换方法应用于递归关系。
我希望我已经回答了你的问题。