F(6) * <-- only once
F(5) * <-- only once too
F(4) **
F(3) ****
F(2) ********
F(1) **************** <-- 16
F(0) ******************************** <-- 32
static int fib(int n)
{
/* memory */
int f[] = new int[n+1];
int i;
/* Init */
f[0] = 0;
f[1] = 1;
/* Fill */
for (i = 2; i <= n; i++)
{
f[i] = f[i-1] + f[i-2];
}
return f[n];
}
这是优化的,只做 n 步骤,但也是指数 .
成本函数是从输入大小到解决问题的步骤数定义的 . 当你看到Fibonacci的动态版本(计算表的 n 步骤)或最简单的算法来知道数字是否为素数( sqrt(n) 来分析数字的有效除数) . 您可能认为这些算法是 O(n) 或 O(sqrt(n)) 但是由于以下原因,这根本不是这样的:算法的输入是一个数字: n ,使用二进制表示法,整数 n 的输入大小是 log2(n) 然后执行变量的
m = log2(n) // your real input size
让我们找出作为输入大小函数的步数
m = log2(n)
2^m = 2^log2(n) = n
那么算法的成本是输入大小的函数是:
T(m) = n steps = 2^m steps
这就是成本是指数的原因 .
110
这样做更好:
unsigned int Fib(unsigned int n)
{
// first Fibonaci number is Fib(0)
// second one is Fib(1) and so on
// unsigned int m; // m + current_n = original_n
unsigned int a = 1; // Fib(m)
unsigned int b = 0; // Fib(m-1)
unsigned int c = 0; // Fib(m-2)
while (n--)
{
c = b;
b = a;
a = b+c;
}
return a;
}
12 回答
您可以将时间函数建模为
Fib(n)
,作为计算Fib(n-1)
的时间加上计算Fib(n-2)
的时间加上将它们加在一起的时间(O(1)
) .T(n<=1) = O(1)
T(n) = T(n-1) + T(n-2) + O(1)
您可以解决此递归关系(例如,使用生成函数),您将得到答案 .
或者,您可以绘制递归树,它将具有深度
n
并且直观地发现该函数是渐近的O(2
n)
. 然后,您可以通过归纳证明您的猜想 .基地:
n = 1
很明显因此,假设
T(n-1) = O(2
n-1)
T(n) = T(n-1) + T(n-2) + O(1)
等于T(n) = O(2
n-1) + O(2
n-2) + O(1) = O(2
n)
但是,正如评论中所指出的,这不是紧张局限 . 关于这个函数的一个有趣的事实是T(n)渐近 the same 作为
Fib(n)
的值,因为两者都被定义为f(n) = f(n-1) + f(n-2)
.递归树的叶子将始终返回1.
Fib(n)
的值是递归树中叶子返回的所有值的总和,它等于叶子的数量 . 由于每个叶子将采用O(1)来计算,T(n)
等于Fib(n) x O(1)
. 因此,该函数的紧束缚是Fibonacci序列本身(~θ(1.6
n)
) . 你可以通过使用我上面提到的生成函数来找出这个紧密的界限 .只要问问自己需要执行多少语句才能完成
F(n)
.对于
F(1)
,答案是1
(条件的第一部分) .对于
F(n)
,答案是F(n-1) + F(n-2)
.那么什么功能满足这些规则呢?试试(a> 1):
an == a(n-1)a(n-2)
除以(n-2):
a2 == a 1
解决
a
并得到(1+sqrt(5))/2 = 1.6180339887
,也称为golden ratio .所以它需要指数时间 .
对这个specific problem over at MIT进行了非常好的讨论 . 在第5页,他们指出,如果你假设一个加法需要一个计算单位,那么计算Fib(N)所需的时间与Fib(N)的结果密切相关 .
因此,您可以直接跳到Fibonacci系列的非常接近的近似值:
并且因此说,天真算法的最坏情况表现是
PS:如果您想了解更多信息,请在维基百科上讨论closed form expression of the Nth Fibonacci number .
我同意pgaur和rickerbh,recursive-fibonacci的复杂性是O(2 ^ n) .
我以相当简单的方式得出了相同的结论,但我认为仍然是有效的推理 .
首先,它是关于计算在计算第N个斐波纳契数时调用递归的斐波纳契函数(从现在开始的F())的次数 . 如果在序列0到n中每个数字调用一次,那么我们有O(n),如果每个数字被调用n次,那么我们得到O(n * n)或O(n ^ 2),等等 .
因此,当为数字n调用F()时,对于0和n-1之间的给定数字,调用F()的次数随着接近0而增长 .
作为第一印象,在我看来,如果我们以可视方式放置它,每次为一个给定数字调用F()时绘制一个单位,wet得到一种金字塔形状(也就是说,如果我们水平居中单位) ) . 像这样的东西:
现在,问题是,随着n的增长,这个金字塔的基数有多快?
我们来看一个真实案例,例如F(6)
我们看到F(0)被调用32次,这是2 ^ 5,对于这个样本情况是2 ^(n-1) .
现在,我们想知道F(x)被调用多少次,我们可以看到调用F(0)的次数只是其中的一部分 .
如果我们在精神上将所有*从F(6)移动到F(2)线到F(1)线,我们看到F(1)和F(0)线的长度现在相等 . 这意味着,当n = 6时,调用F()的总次数是2x32 = 64 = 2 ^ 6 .
现在,就复杂性而言:
您可以扩展它并进行可视化
它的下端由
2^(n/2)
界定,在上端由2 ^ n界定(如其他注释中所述) . 递归实现的一个有趣的事实是它具有Fib(n)本身的紧密渐近界 . 这些事实可以概括为:如果你愿意,可以使用closed form进一步减少紧密限制 .
证明答案是好的,但我总是需要手工做几次迭代才能真正说服自己 . 所以我在我的白板上画了一个小的调用树,并开始计算节点 . 我将计数分为总节点,叶节点和内部节点 . 这是我得到的:
立即跳出的是叶子节点的数量是
fib(n)
. 又花了几次迭代要注意的是内部节点的数量是fib(n) - 1
. 因此节点总数为2 * fib(n) - 1
.由于在对计算复杂性进行分类时丢弃系数,因此最终答案是
θ(fib(n))
.好吧,据我说它是
O(2^n)
因为在这个函数中只有递归需要相当长的时间(分而治之) . 我们看到,上面的函数将在树中继续,直到叶子接近到达F(n-(n-1))
的水平,即F(1)
. 所以,在这里我们记下在树的每个深度遇到的时间复杂度时,求和系列是:这是
2^n [ O(2^n) ]
的顺序 .http://www.ics.uci.edu/~eppstein/161/960109.html
time(n) = 3F(n) - 2
通过绘制递归树可以更好地估计递归算法的时间复杂度 . 在这种情况下,绘制递归树的递归关系将是T(n)= T(n-1)T(n-2)O(1)注意每一步取O(1)意味着恒定的时间,因为它在 if block中只检查n的值,而且只有一个比较.Recursion树看起来像
这里可以说上面树的每个级别由i表示,因此,
让我们说在i的特定值,树结束,那种情况将是当n-i = 1时,因此i = n-1,这意味着树的高度是n-1 . 现在让我们看看树中每个n层的工作量是多少 . 注意每个步骤需要O(1)时间,如递归关系中所述 .
因为i = n-1是在每个级别完成的树木工作的高度
因此,完成的总工作将是在每个级别完成的工作的总和,因此它将是2 ^ 0 2 ^ 1 2 ^ 2 2 ^ 3 ... 2 ^(n-1),因为i = n-1 . 通过几何级数,这个和是2 ^ n,因此这里的总时间复杂度是 O(2^n)
由于计算中的重复,Fibonacci的朴素递归版本是指数式的:
你在计算根源:
F(n)取决于F(n-1)和F(n-2)
F(n-1)再次取决于F(n-2)和F(n-3)
F(n-2)再次取决于F(n-3)和F(n-4)
然后你在每个级别进行2次递归调用,这些调用在计算中浪费了大量数据,时间函数将如下所示:
T(n)= T(n-1)T(n-2)C,C恒定
然后,T(n-1)= T(n-2)T(n-3)> T(n-2)
T(n)> 2 * T(n-2)
...
T(n)> 2 ^(n / 2)* T(1)= O(2 ^(n / 2))
这只是一个下限,为了你的分析目的应该足够,但实时函数是一个常数的因子乘以相同的斐波纳契公式,并且已知closed form是黄金比率的指数 .
此外,您可以使用动态编程找到Fibonacci的优化版本,如下所示:
这是优化的,只做 n 步骤,但也是指数 .
成本函数是从输入大小到解决问题的步骤数定义的 . 当你看到Fibonacci的动态版本(计算表的 n 步骤)或最简单的算法来知道数字是否为素数( sqrt(n) 来分析数字的有效除数) . 您可能认为这些算法是 O(n) 或 O(sqrt(n)) 但是由于以下原因,这根本不是这样的:算法的输入是一个数字: n ,使用二进制表示法,整数 n 的输入大小是 log2(n) 然后执行变量的
让我们找出作为输入大小函数的步数
那么算法的成本是输入大小的函数是:
这就是成本是指数的原因 .
这样做更好: