哪种方式更有效地执行功能(在时间复杂度方面),使用递归,使用while循环,或两者兼而有之?

时间:2010-11-14 18:13:32

标签: algorithm

我已经看过“分而治之”算法的代码示例(或者,至少我认为是“分而治之”) - 一组通用示例倾向于使用递归,而另一组使用递归而循环。

这是递归示例:

...
if (exponent%2==0) 
{ 
return Power(base*base, exponent/2); 
} 
else if (exponent%2==1)
{ 
    return base*Power(base*base, exponent/2); 
} 
...

而且,这是while循环示例:

...
while (exponent>1)
{
    if (exponent%2 == 1)
        result *= base;
    exponent/=2;
    base *= base;
}
...

在这两种情况下,看起来它们的执行次数相同。两种方法似乎采取的操作数量受T(exponent) = Θ(log_2(exponent))的上限函数的约束。 除非我的分析是错误的,否则我看不出一种方法比另一种更快。我认为递归方法在空间复杂度方面效率低于while循环方法,因为递归方法的空间复杂度为2*(log_2(exponent))(如果分析正确的话)。

while-loop方法的唯一优势是它具有较低的空间要求吗?

5 个答案:

答案 0 :(得分:5)

假设您使用的编译器是理智的,那么是......较低的空间要求是唯一的优势。

请注意,大多数编译器都允许有效处理tail recursion,这在函数仅将自身称为执行的最后一步时发生(请参阅文章中的示例)。如上所述,你的递归算法不是尾递归的,因为返回之前的最后一步是乘法(base * Power),但是这可以通过添加一个累加器变量作为参数来改变,该参数在每次调用时相乘然后返回你达到的最后一个累加器。

示例代码:

...
int Power(int base, int exponent, int accumulator)
{
    if (exponent%2==0) 
    { 
        return Power(base*base, exponent/2, accumulator); 
    } 
    else if (exponent%2==1)
    { 
        return Power(base*base, exponent/2, accumulator * base); 
    } 
}
...

其中Power总是最初用累加器1调用(例如,你可以产生一个替代功能(a,b),如果需要的话只调用Power(a,b,1)。

答案 1 :(得分:1)

带递归的版本适用于dydactical目的,但loop版本更快。

这不仅是因为调用开销,还因为循环可以由编译器展开,并且数据依赖性可以更容易传播。

答案 2 :(得分:0)

对于幂问题---是的,由于编译器优化的范围,循环可能更好。但是你必须意识到这并不适用于所有分而治之的算法(如果这是你在第一段所暗示的情况)。

另外,不要忘记,一些分而治之的范例在分布式系统中运行得非常好。

答案 3 :(得分:0)

在你的问题中,时间复杂性的概念似乎有点过时了。

如果您正在考虑“这个函数运行多长时间”的问题,那么时间复杂性的概念就没有意义了。您似乎了解基础知识,因此您知道1000N + 100000的复杂性与N / 10000相同。也就是说,我还要注意1000N + 100000的时间成本 10000

复杂性的概念与增长有关。它表示函数的时间消耗随N的大小(输入数据的大小)而增长的速度。

在时间成本的世界中,循环方法在大多数情况下会推翻递归方法(我认为我还没有看到递归解决方案花费更少时间的情况,即使在编译器优化的情况下也是如此) )。此外,正如您(以及我之前的大多数海报)所提到的,所需的空间可能更大(如果不使用尾调用优化,则必须显着更大)。

如果你想知道什么是递归有利于(校外),那么答案就是代码简单性(如果使用得当,递归可以制作非常简单的代码)。

答案 4 :(得分:0)

这个用于计算基数幂的循环示例是 错误 它将计算base ^ log(指数)而不是base ^ exponent

*使用递归的分而治之方法会给出时间复杂度:O(logn) *使用循环n-1时间乘法运算将发生TC:O(n)

如果你有脑,你可以决定哪一个在时间上更好。