遞推和迭代的比較


      迭代是一種不斷用變量的舊值推出新值的過程。例如,程序設計中常用到的計數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]中,隨時可以查看;而迭代過程中不保留中間數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM