I am a teacher!

导航

递推和迭代的比较

      迭代是一种不断用变量的旧值推出新值的过程。例如,程序设计中常用到的计数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]中,随时可以查看;而迭代过程中不保留中间数据。

posted on 2019-06-14 18:28  aTeacher  阅读(4025)  评论(0编辑  收藏  举报