1. 程式人生 > 實用技巧 >NOIP--阿克曼函式的遞迴轉非遞迴解法

NOIP--阿克曼函式的遞迴轉非遞迴解法

ackerman函式(阿克曼函式,以下簡稱ack函式)是一個雙引數遞迴函式,用遞迴計算程式碼如下

int ack(int m,int n)
{  
if (m==0)
return n+1;
else if (n==0)
return ack(m-1,1);
else return ack(m-1,ack(m,n-1)); }


ack函式像Dirichlet函式一樣,是因為為了澄清某種概念而在數學(計算科學)史有一席之地。
ack函式最初是作為一個非線性的鏈式遞迴的例子而被提出來的,它增長很快,事實上m>3時關於n是遠高於線性遞推的指數級增長(關於m增長地更快)而反直覺,即使精確計算ack(4,1)也是做不到的,即使ack(3,3)也有61,如果不記憶化搜尋,需要遞迴2000餘次才可以計算出來!如果不去考慮ack(3,n)的指數增長的通項而被小數字迷惑企圖直接計算,那麼會算得毫無頭緒,所以ack函式被廣泛用於計算機預賽,筆試,數學自主招生考試中。
那麼既然ack函式增長地如此之快,那麼ack(m,1)的反函式增長地極其緩慢了。事實上,路徑壓縮的並查集經過足夠久的查詢,每次查詢你平均複雜度就是反阿克曼函式級別的(也就是log*級別的,至於為什麼,我至晚在2012年就想知道這個問題了,現在終於知道…我恐怕短時間內沒法知道(~^~),演算法

導論裡面有對此的推導,有興趣的同學可以去看一下),好吧,這是一些有趣的冷知識,和我和ack函式的歷史淵源,就當隨筆寫出來。
本以為我和ack的緣分在預賽題中知道它怎麼做後就沒有什麼新進展了,最多以後講到這個題,但是萬萬沒想到我會以一種奇怪的方式遇到這個函式,科學史上雋永有名的函式例如Dirichlet函式,ack函式果然白髮如新。

最近學習ics裡的彙編原理初步和數算的棧實現遞迴方法,事實上這兩者是相通的。我雖然沒有編寫過具體彙編程式碼,但是大概原則上知道了怎麼把函式呼叫用堆疊實現,怎麼用棧機制非遞迴實現遞迴。紙上得來終覺淺,我決定找一個例題,我編了一個複雜點的題—ackerman函式的非遞迴實現(遞迴函式非遞迴化)。
經過一番除錯除錯對了,但是人還是很懵懂,看來還要加強理論學習,不過不得不說這是個很好的例題。


int Ack(int M,int N)
{  
int top=0,m,n;
int stack[10000][4]={{M,N}};//記錄資訊m,n,Ack(m,n),跳轉出口種類; while (true) { m=stack[top][0]; n=stack[top][1];
if (m==0)
stack[top][2]=n+1;
else if (n==0) { top++;
stack[top][0]=m-1;
stack[top][1]=1;
stack[top][3]=1; continue; l1:
stack[top][2]=stack[top+1][2]; } else { top++;
stack[top][0]=m-1; top++;
stack[top][0]=m;
stack[top][1]=n-1;
stack[top][3]=2;
continue; l2:
stack[top][1]=stack[top+1][2];
stack[top][3]=3;
continue; l3:
stack[top][2]=stack[top+1][2]; }
if (top==0)
break; top--;
switch (stack[top+1][3]) {
case 1:goto l1;
case 2:goto l2;
case 3:goto l3; } } return stack[0][2]; }




NOIP資訊學視訊地址

視訊地址

連結:https://pan.baidu.com/s/1tHo1DFMaDuMZAemNH60dmw
提取碼:7jgr