• 递推和迭代的比较


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

  • 相关阅读:
    ldap集成jenkins
    自动发现实现url+响应时间监控
    5秒跳转
    String的使用
    数字货币转换为中文货币
    字符串的使用(string,StringBuffer,StringBuilder)
    修饰符
    类的继承,抽象类,接口,方法重写和重载
    类的使用
    java中的输入流(Scanner),数据类型,运算符,switch,数组的用法
  • 原文地址:https://www.cnblogs.com/cs-whut/p/11024916.html
Copyright © 2020-2023  润新知