欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

递推和迭代的比较

程序员文章站 2022-10-08 17:44:36
迭代是一种不断用变量的旧值推出新值的过程。例如,程序设计中常用到的计数cnt=cnt+1(或cnt++),就是用变量cnt的值加上1后赋值给cnt;对k的求和s=s+k,就是用变量s的值加上k后赋值给s。这种用变量cnt、s的新值取代旧值的过程,实际上就是迭代。 递推实际上也是根据递推关系式不断推出 ......

      迭代是一种不断用变量的旧值推出新值的过程。例如,程序设计中常用到的计数cnt=cnt+1(或cnt++),就是用变量cnt的值加上1后赋值给cnt;对k的求和s=s+k,就是用变量s的值加上k后赋值给s。这种用变量cnt、s的新值取代旧值的过程,实际上就是迭代。

      递推实际上也是根据递推关系式不断推出新值的过程,与迭代有很多共同之处。很多迭代过程可以应用递推来解决;反过来,很多递推过程也可以应用迭代来解决。

      例如,下面的水手分椰子问题,既可以采用递推法求解,也可以用迭代法求解。

【例1】水手分椰子

      五个水手来到一个岛上,采了一堆椰子后,因为疲劳都睡着了。一段时间后,第一个水手醒来,悄悄地将椰子等分成五份,多出一个椰子,便给了旁边的猴子,然后自己藏起一份,再将剩下的椰子重新合在一起,继续睡觉。不久,第二名水手醒来,同样将椰子等分成五份,恰好也多出一个,也给了猴子。然后自己也藏起一份,再将剩下的椰子重新合在一起。以后每个水手都如此分了一次并都藏起一份,也恰好都把多出的一个给了猴子。第二天,五个水手醒来,发现椰子少了许多,心照不宣,便把剩下的椰子分成五份,恰好又多出一个,给了猴子。问原来这堆椰子至少有多少个?

      (1)编程思路1。

       应用递推来求解,按时间来实施递推。

      设第i个水手藏椰子数为y(i)(i=1、2、…、5)个,第二天5个水手醒来后各分得椰子为y(6)个,则原来这堆椰子数为

    x=5*y(1)+1

      1)如何求取y(1)呢?

      由于第二个水手醒来所面临的椰子数为4y(1),同时也为5y(2)+1,于是有

             4*y(1)=5*y(2)+1

       同样,y(2)与y(3)之间的关系为:4*y(2)=5*y(3)+1

       一般地,有递推关系:4*y(i)=5*y(i+1)+1  (i=1、2、…、5)

      2)递推的初始(边界)值如何确定?

      问题本身没有初始(边界)条件限制,只要求上面5个递推关系式所涉及的6个量y(i)都是正整数。也就是说,若有6个整数y(i)满足5个方程4*y(i)=5*y(i+1)+1  (i=1,2,…,5),即为所求的一个解。

      3)采用顺推法求解。

      将递推式变形为从y(i)推出y(i+1)的形式

            y(i+1)=(4*y(i)-1)/5   (i=1,2,…,5)  

      首先y(1)赋初值k后推出y(2),由y(2)推出y(3),…,依此经5次递推得y(6)。如果某一次推出的不是整数,则中止继续往后推,k增1后赋值给y(1),从头开始。

      这样按时间顺序从前往后递推,若每次递推所得都是整数,则找到了解,打印输出。

      为保证推出的y(i)为整数,则要求4*y(i-1)-1能被5整除(即前一个水手藏起一份后,剩下的4份能够给猴子一个,再被分成五份)。因此,可确定最小的k值为4,即y(1)赋初值4;若在递推过程中,某次y(i)不为整数,则重新赋y(1)从头再来,为保证4*y(1)-1能被5整除,因此 k 的增量可设置为5。

      (2)源程序1。

#include <iostream>

using namespace std;

int main()

{

    int i,k,x,y[7];

    k=4;  y[1]=k;

    i=2;

    while (i<=6)

    {

       if ((4*y[i-1]-1)%5!=0)          

          {

                 k=k+5;  y[1]=k; i=2;    // 若y(i)不是整数,k增1重试

          }

          else

          {

                 y[i]=(4*y[i-1]-1)/5;   // 递推求后一个水手藏起的椰子y(i)

           i++;

          }

       }

    x=5*y[1]+1;

    cout<<"原有椰子至少"<<x<<"个。"<<endl;

    for (i=1; i<=5; i++)

              cout<<"第 "<<i<<" 个水手面临椰子 "<<5*y[i]+1<<" 个,藏 "<<y[i]<<"个。"<<endl;

    cout<<"最后一起分时有椰子 "<<5*y[6]+1<<" 个,每人分得"<<y[6]<<"个。"<<endl;

    return 0;

}

       (3)编程思路2。

      采用倒推法求解,即改为y(6)赋初值k后递推出y(5),由y(5)递推出y(4),依此经5次递推得y(1),“由后向前”递推式为:

             y(i)=(5*y(i+1)+1)/4  (i=1、2、…、5)

      为确保从y(6)推出整数y(5),显然y(6)(即初始参数k)只能取3、7、11、…,即取k%4==3。因而k赋初值为3,k的增量为4。

       (4)源程序2。

#include <iostream>

using namespace std;

int main()

{

    int i,k,x,y[7];

    k=3;  y[6]=k;

    i=5;

    while (i>=1)

    {

       if ((5*y[i+1]+1)%4!=0)          

          {

                 k=k+4;  y[6]=k; i=5;    // 若y(i)不是整数,k增1重试

          }

          else

          {

                 y[i]=(5*y[i+1]+1)/4;   // 递推求前一个水手藏起的椰子y(i)

           i--;

          }

       }

    x=5*y[1]+1;

    cout<<"原有椰子至少"<<x<<"个。"<<endl;

    for (i=1; i<=5; i++)

          cout<<"第 "<<i<<" 个水手面临椰子 "<<5*y[i]+1<<" 个,藏 "<<y[i]<<"个。"<<endl;

    cout<<"最后一起分时有椰子 "<<5*y[6]+1<<" 个,每人分得"<<y[6]<<"个。"<<endl;

    return 0;

}

       在思路(1)中,采用顺推法,从前向后推,即从大到小推,试到k=3124才完成,从k=4到k=3124,试了625次;在思路(2)中,采用倒推法,从后往前推,即从小往大推,只要试到k=1023即可完成,从k=3到k=1023,试了256次。可见,在应用递推时,选用合适的递推方向关系到递推的效率。

      (5)编程思路3。

      用迭代法求解。

      从最后5位水手一起分椰子时的椰子数residual入手,设residual的初始值为6(每个水手至少能分1个,丢1个给猴子),但这不可能,因为residual的值一定是第5位水手分成5份后,藏1份,剩下的4份,即每次剩下的一定是4的倍数,因此residual值一定满足两个条件:(1)是4的倍数;(2)减1后能被5整除。即residual的值为16、36、56、76、…。

      对residual值向前推导。看看能否前推5次,且每次剩下的椰子数均是4的倍数。例如,当residual=16时,第5位水手面临的椰子数应为peachnum=present/4*5+1=16/4*5+1=21,而第5位水手面临的椰子数是第4位水手藏起1份后剩下的4份,显然21不是4的倍数,因此residual=16不可行,修改residual的值,使residual=residual+20=36,重新推导。

      迭代时,迭代初值为 present=residual,迭代关系式为peachnum=present/4*5+1,         present=peachnum,迭代控制条件为:在保证每次迭代后,present的值为4的倍数的情况下,迭代次数能达到5次。若迭代过程中,得到的present的值不是4的倍数,则修改residual的值,使residual=residual+20=36,重新迭代求解。

      (6) 源程序3。

#include <iostream>

using namespace std;

int main()

{

   int residual,present,peachnum,count;

   residual=16;

   count=0;

   present=residual;

   while (count<=4)

   {

               if(present%4!=0)

               {

                      count=0;

             residual+=20;

                present=residual;        

               }

         peachnum=present/4*5+1;

         count++;

         present=peachnum;

       }

   cout<<"原有椰子至少"<<peachnum<<"个。"<<endl;

   return 0;

}

 

      比较递推与迭代,两者的时间复杂度是相同的。所不同的是,递推往往设置数组,而迭代只要设置迭代的简单变量即可。

       递推过程中数组变量带有下标,推出过程比迭代更为清晰。也正因为递推中应用数组,因此保留了递推过程中的中间数据。例如,每个水手i藏起的椰子都保存在数组y[i]中,随时可以查看;而迭代过程中不保留中间数据。