C 为什么递归要花这么长时间?

C 为什么递归要花这么长时间?,c,C,在使用递归计算斐波那契序列的第n个数时,我编写了以下简单程序: #include <stdio.h> #include <stdlib.h> #include <limits.h> unsigned int long long fibonacci(unsigned int number); //game of craps int main(int argc, char** argv) { for(int n = 1; n <= 100; n

在使用递归计算斐波那契序列的第n个数时,我编写了以下简单程序:

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

unsigned int long long fibonacci(unsigned int number);

//game of craps
int main(int argc, char** argv)
{

    for(int n = 1; n <= 100; n++)
    {
        printf("%llu\n", fibonacci(n));
    }

    return (EXIT_SUCCESS);
}


unsigned long long int fibonacci(unsigned int number)
{
    if (number == 0 || number == 1)
    {
        return number;
    }
    else
    {
        return fibonacci(number - 2) + fibonacci(number - 1);      
    }
}
#包括
#包括
#包括
无符号整数长fibonacci(无符号整数);
//掷骰子游戏
int main(int argc,字符**argv)
{

对于(int n=1;n如果您有单线程程序,则不会利用四核系统

它将只在一个内核上运行,因此21/25%的CPU使用率是现实的

使用它的一种方法是,首先不要使用递归,因为它会让人讨厌。当你有一个for/while循环时,将它拆分为4个while循环,并将每个while循环放在一个新线程中。然后你必须管理同步以正确打印消息,但这甚至没有那么难。你可以将所有结果存储在一个线程中完成所有线程后,请重新排列并打印它

对递归函数的调用次数大约为2^n,即指数复杂度。明白

我不确定你是否完全理解这一点,因为你似乎对它在n=40和n=47左右变慢感到惊讶

复杂度为2^n,n为40,即240,或109951162776,或约1万亿次运算。如果您的计算机每纳秒能运行其中一次运算,即每秒10亿次运算,则需要1000秒才能完成

考虑一下,如果n仅为30,则230为1073741824,在同一台计算机上只需约1秒

如前所述,您只使用了一个内核。您可以并行化,但这不会有多大帮助。使用四个内核而不是一个,我的n=40示例仍需要250秒。上升到n=42,您将返回到1000秒,因为并行化最多只能使您的性能成倍增长,但这样的算法会成倍增长

  • 发布的代码包含一些极端的过度复杂性
  • 即使是
    long无符号int
    也不能包含斐波那契值 数字100(甚至接近它)
  • 建议使用一个非常简单的程序开始,一个计算斐波那契序列的程序。然后使用该程序确定如何显示结果

    下面的程序计算数字非常快,但仍然存在
    long unsigned int的溢出问题

    #include <stdio.h>  // printf()
    
    
    int main( void )
    {
        long long unsigned currentNum = 1;
        long long unsigned priorNum = 1;
    
        printf( "1\n1\n" );
    
        for (size_t i = 2;  i < 100; i++ )
        {
            long long unsigned newNum = currentNum+priorNum;
            printf( "%llu\n", newNum );
            priorNum = currentNum;
            currentNum = newNum;
        }
    }
    
    那么,为什么递归要花这么长时间呢

    因为有大量的递归和溢出的处理


    上面建议的代码消除了递归,但没有溢出,并且(在我的计算机上)运行不到一秒钟。

    根据@给出的答案,您可以使用提供的无限精度算术库消除他提到的溢出

    例如

    #包括//printf
    #包括//免费
    #包括//mpz\t
    内部主(空)
    {
    mpz_t prevNum、currNum、tempNum、counter;
    mpz_init_set_si(prevNum,0);
    mpz_init_set_si(currNum,1);
    mpz_init_set_si(tempNum,1);
    mpz_init_set_si(计数器,1);
    printf(“0:0\n”);
    而(1){
    char*tempNumRepr=mpz_get_str(NULL,10,tempNum);
    char*counterRepr=mpz\u get\u str(NULL,10,计数器);
    printf(“%s:%s\n”,计数器repr,tempNumRepr);
    免费(tempNumRepr);
    免费(反报告);
    mpz_add(tempNum,currNum,prevNum);//tempNum=currNum+prevNum;
    mpz_add_ui(计数器,计数器,1);//计数器=计数器+1;
    mpz_集(prevNum,currNum);//prevNum=currNum;
    mpz_集(currNum,tempNum);//currNum=tempNum;
    }
    mpz_清除(prevNum);
    mpz_清除(currNum);
    mpz_清除(tempNum);
    mpz_清除(计数器);
    返回退出成功;
    };
    
    要编译它,请确保已安装libgmp,键入:

    ~$ gcc fib.c -lgmp
    
    你很快就能得到大量的斐波那契值:

    ~$ ./a.out
    0: 0
    1: 1
    2: 1
    3: 2
    4: 3
    5: 5
    6: 8
    7: 13
    8: 21
    9: 34
    ...
    90: 2880067194370816120
    91: 4660046610375530309
    92: 7540113804746346429
    93: 12200160415121876738
    94: 19740274219868223167
    95: 31940434634990099905
    96: 51680708854858323072
    97: 83621143489848422977
    98: 135301852344706746049
    99: 218922995834555169026
    100: 354224848179261915075
    ...
    142: 212207101440105399533740733471
    143: 343358302784187294870275058337
    144: 555565404224292694404015791808
    145: 898923707008479989274290850145
    146: 1454489111232772683678306641953
    147: 2353412818241252672952597492098
    148: 3807901929474025356630904134051
    149: 6161314747715278029583501626149
    150: 9969216677189303386214405760200
    ...
    10456: 6687771891046976665010914682715972428661740561209776353485935351631179302708216108795962659308263419533746676628535531789045787219342206829688433844719175383255599341828410480942962469553971997586487609675800755252584139702413749597015823849849046700521430415467867019518212926720410106893075072562394664597041033593563521410003073230903292197734713471051090595503533547412747118747787351929732433449493727418908972479566909080954709569018619548197645271462668017096925677064951824250666293199593131718849011440475925874263429880250725807157443918222920142864819346465587051597207982477956741428300547495546275347374411309127960079792636429623948756731669388275421014167909883947268371246535572766045766175917299574719971717954980856956555916099403979976768699108922030154574061373884317374443228652666763423361895311742060974910298682465051864682016439317005971937944787596597197162234588349001773183227535867183191706435572614767923270023480287832648770215573899455920695896713514952891911913499762717737021116746179317675622780792638129991728650763618970292905899648572351513919065201266611540504973510404007895858009291738402611754822294670524761118059571137973416151185102238975390542996959456114838498320921216851752236455715812273599551395186676228882752252829522673168259864505917922994675966393982705428427387550834530918600733123354437191268657802903434440996622861582962869292133202292740984119730918997492224957849300327645752441866958526558379656521799598935096546592129670888574358354955519855060127168291877171959996776081517513455753528959306416265886428706197994064431298142841481516239689015446304286858347321708226391039390175388745315544138793021359869227432464706950061238138314080606377506673283324908921190615421862717588664540813607678946107283312579595718137450873566434040358736923152893920579043838335105796035360841757227288861017982575677839192583578548045589322945
    ...
    

    使用CTRL+C停止程序。

    21%与25%相差不远,也就是说,您可以使用的一个核心的100%。如果问题是为什么需要这么长时间,那是因为它必须计算前面的每个数字。这需要很长时间,因为这是一种方法,虽然简单,但可以说是效率最低的方法(双重递归)做斐波那契序列计算。它的递归量随着数字的大小呈指数增长,并且它会多次重新计算值。谢谢各位。我知道这种方法效率不高;我只是好奇为什么它没有使用所有的内核。我想现在我必须研究如何在c中分配新线程。实际上,just查找记忆。良好的编程胜过更快的硬件。溢出肯定会妨碍正确性,但从复杂性的角度来看,我不认为它会使计算速度减慢。无论如何,+1感谢使用gmp库的示例
    ~$ ./a.out
    0: 0
    1: 1
    2: 1
    3: 2
    4: 3
    5: 5
    6: 8
    7: 13
    8: 21
    9: 34
    ...
    90: 2880067194370816120
    91: 4660046610375530309
    92: 7540113804746346429
    93: 12200160415121876738
    94: 19740274219868223167
    95: 31940434634990099905
    96: 51680708854858323072
    97: 83621143489848422977
    98: 135301852344706746049
    99: 218922995834555169026
    100: 354224848179261915075
    ...
    142: 212207101440105399533740733471
    143: 343358302784187294870275058337
    144: 555565404224292694404015791808
    145: 898923707008479989274290850145
    146: 1454489111232772683678306641953
    147: 2353412818241252672952597492098
    148: 3807901929474025356630904134051
    149: 6161314747715278029583501626149
    150: 9969216677189303386214405760200
    ...
    10456: 6687771891046976665010914682715972428661740561209776353485935351631179302708216108795962659308263419533746676628535531789045787219342206829688433844719175383255599341828410480942962469553971997586487609675800755252584139702413749597015823849849046700521430415467867019518212926720410106893075072562394664597041033593563521410003073230903292197734713471051090595503533547412747118747787351929732433449493727418908972479566909080954709569018619548197645271462668017096925677064951824250666293199593131718849011440475925874263429880250725807157443918222920142864819346465587051597207982477956741428300547495546275347374411309127960079792636429623948756731669388275421014167909883947268371246535572766045766175917299574719971717954980856956555916099403979976768699108922030154574061373884317374443228652666763423361895311742060974910298682465051864682016439317005971937944787596597197162234588349001773183227535867183191706435572614767923270023480287832648770215573899455920695896713514952891911913499762717737021116746179317675622780792638129991728650763618970292905899648572351513919065201266611540504973510404007895858009291738402611754822294670524761118059571137973416151185102238975390542996959456114838498320921216851752236455715812273599551395186676228882752252829522673168259864505917922994675966393982705428427387550834530918600733123354437191268657802903434440996622861582962869292133202292740984119730918997492224957849300327645752441866958526558379656521799598935096546592129670888574358354955519855060127168291877171959996776081517513455753528959306416265886428706197994064431298142841481516239689015446304286858347321708226391039390175388745315544138793021359869227432464706950061238138314080606377506673283324908921190615421862717588664540813607678946107283312579595718137450873566434040358736923152893920579043838335105796035360841757227288861017982575677839192583578548045589322945
    ...