是什么让这些递归片段的速度如此不同?

时间:2016-12-21 15:33:50

标签: c performance recursion difference

我必须设计一个递归函数来应用一个赋值(我不允许使用关于河内塔的标准数学库。我偶然发现了以下代码,我认为这对于分配工作,但是不可能为(n> 30)运行它,因为它太慢了:

#include <stdio.h>
#include <stdlib.h>

int TOH(int,char,char,char);

int main()
{
    int n;
    printf("\nEnter number of disks:");
    scanf("%d",&n);
    int c = TOH(n,'A','C','B');
    printf("\nTotal number of moves = %d \n ", c);
    return 0;
}

int TOH(int n,char x,char y,char z)
{
    int count = 0;

    if(n>0){
    count = TOH(n-1, x, z, y);
    count++;
    count += TOH(n-1, z, y, x);
    }
    return count;
}

在寻找速度的解决方案时,我偶然发现了这个代码,它在使用递归时立即运行。我迷失在这种速度差异来自的地方:

#include <stdio.h>
#include <stdlib.h>

float count_moves(int);
float power(int);

int main()
{
    int STACKS;
    printf("\nEnter numbers of disks: ");
    scanf("%d", &STACKS);
    float total = count_moves(STACKS);
    printf("\nTotal number of moves: %.0f\n", total);
    return 0;
}

float power(int multi)
{
    if(!multi)
    {
        return 1;
    }

    else
    {
        return 2 * power(multi - 1);
    } 
}

float count_moves(int layers)
{
    if(!layers)
    {
        return 0;
    }

    else
    {
        return power(layers - 1) + count_moves(layers - 1);
    }
}

第二个如何能够立即在控制台中打印某些东西,而第二个需要更长的时间,我做n / STACKS的数字越大?

3 个答案:

答案 0 :(得分:1)

首先我建议你绘制递归树。看看pegs = 30有多大。请参阅Complexity for towers of Hanoi? 它的复杂度为O(2 ^ n)。 http://www.iitk.ac.in/esc101/08Jan/lecnotes/lecture32.pdf

第二种解决方案不是以传统方式计算它。它正在打一个电话。 T(n-1)+ c = O(n ^ 2)

所以,2 ^ 30 vs 30 ^ 2。猜猜哪一个更快!

亲自看看。

为像这样的函数添加一个计数器 (制作&#39; c&#39;&#39; d&#39;全球)

float power(int multi)
{
    printf("d = %d\n",d);
    d++;
    if(!multi)
    {
        return 1;
    }

    else
    {
        return 2 * power(multi - 1);
    } 
}

float count_moves(int layers)
{
    printf("c = %d\n",c);
    c++;
    if(!layers)
    {
        return 0;
    }

    else
    {
        return power(layers - 1) + count_moves(layers - 1);
    }
}

并查看他们被召唤的次数。

答案 1 :(得分:0)

忽略无意义的参数,第一个算法是count = (n > 0) ? TOH(n-1)+TOH(n-1)+1 : 0。每次拨打TOH都会导致另外两次拨打TOH。其复杂度为O(2 ^ n)。每当n加1时,费用就会翻倍。

第二个是(layers > 0) ? [something linear] + count_moves(layers - 1) : 0。它是两个尾递归方法,但每次调用都会产生至多一个进一步的调用。但它已经这样做了n次,而且每次都做了n次&#34;所以这就是n * n。它是O(n ^ 2)。

O(2 ^ n)比O(n ^ 2)快得多。

答案 2 :(得分:0)

您的第一个版本遵循实际计算移动环的步骤顺序的方案。但是你没有明确地计算这些步骤;实际上,你的TOH版本忽略了它的论点! (除了将它们传递给递归调用,除了将它们传递给递归调用之外,它们也会忽略它们。)

这意味着TOH的返回值仅取决于其第一个参数n。这也意味着使用n-1进行的两次递归调用将返回相同的值,因此所有TOH一次就足够了,并使用返回值两次。将ifTOH的正文更改为:

int tmp = TOH(n-1, x, y, z);
count = tmp + 1 + tmp;

使您的代码立即以与之前相同的答案终止。请注意,您可能会在初始n值为31的情况下获得算术溢出。

顺便说一句,GCC级别-O3似乎足够智能,可以在您的原始代码上自动执行此优化,而无需进行任何更改。