遞迴與間接遞迴

2021-08-30 03:53:04 字數 2501 閱讀 1532

遞迴技術允許我們將原問題分解為乙個或多個形式上相似的子問題

級數(n!)的定義如下:

n! = 1 * 2 * 3 * .... * (n-2) * (n-1) * n
可以根據定義寫出如下迭代形式的實現

int fact(int n) 

return result;

}

還可以寫出遞迴形式的實現

int fact(int n)
比較這兩個版本:

遞迴函式可以進行如下分類:

the recursive functions are characterized based on:

是否呼叫自身(直接或間接遞迴)

是否存在未決操作(尾遞迴(tail-recursive)或非尾遞迴)

呼叫模式的結構——是否未決操作本身也是遞迴的(線性遞迴或樹狀遞迴)

直接遞迴:

遞迴函式的函式體中存在顯式的自我呼叫時,被稱為直接遞迴。例如,函式foo中包含自我呼叫,因此是直接遞迴。

int foo(int x)
間接遞迴:

函式foo被稱為間接遞迴,如果它包含對另乙個函式的呼叫而該函式最終會呼叫函式foo。

下面的一對函式屬於間接遞迴。由於他們都互相呼叫,因此又被稱為互遞迴函式

int foo(int x) 

int bar(int y)

尾遞迴(tail recursion):

乙個遞迴函式被稱為尾遞迴函式,如果在遞迴呼叫的過程中不存在未決操作的話。

尾遞迴函式通常被描述為:「返回上次遞迴呼叫得到的值作為函式的返回值。」尾遞迴具有很大的價值,因為在(遞迴)計算過程中需要維護的資訊量與遞迴呼叫次數是無關的。某些現代計算機系統在實際上通過迭代過程來完成尾遞迴函式的計算。

「聲名狼藉"的級數函式通常被寫成非尾遞迴的形式:

int fact (int n)

注意到這裡在每次遞迴呼叫返回時存在乙個未決操作——乘法運算。只要存在未決操作,遞迴函式就是

非尾遞迴的。所有未決操作的相關資訊必須被儲存,因此是與遞迴呼叫的次數密切相關的。級數函式也可以被寫成尾遞迴形式:

int fact_aux(int n, int result) 

int fact(n)

輔助函式fact_aux被用來保證

不需要改變

fact(n)的呼叫形式。實際的遞迴函式是

fact_aux,而不

是fact。注意fact_aux中不存在未決操作。通過遞迴呼叫得到的值被不加修改的直接返回。計算過程

中需要維持的資訊量是常數(變數n和變數value的值),與遞迴呼叫的次數無關。

線性和樹狀遞迴:

遞迴函式的另一種分類方式是遞迴呼叫是如何增長的。兩種基本形式是"線性"和"樹狀"。

乙個遞迴函式被稱為線性遞迴,如果其中的未決操作(如果存在的話)並不涉及到遞迴呼叫的話。

例如,「聲名狼藉"的fact函式是線性遞迴。其未決操作是簡單的乘以乙個標量,並不涉及對fact的遞迴呼叫。

乙個遞迴函式被稱為樹狀遞迴(或非線性遞迴),如果未決操作也是遞迴呼叫的話。

fibonacci函式為樹狀遞迴提供了乙個經典的例子。fibonacci數列按如下規則定義:

fib(n) = 0  if n is 0,

= 1  if n is 1,

= fib(n-1) + fib(n-2) otherwise

例如,前7個fibonacci數分別是:

fib(0) = 0   

fib(1) = 1   

fib(2) = fib(1) + fib(0) = 1

fib(3) = fib(2) + fib(1) = 2

fib(4) = fib(3) + fib(2) = 3

fib(5) = fib(4) + fib(3) = 5

fib(6) = fib(5) + fib(4) = 8

這樣引出了如下的實現:

int fib(int n)

注意到函式體中的未決操作是另一次遞迴盜用,因此fib函式是樹狀遞迴。

乙個非尾遞迴函式經常可以借助於乙個附註引數被轉換為尾遞迴函式,該引數被用於存放結果。基本思想是將未決操作合併到輔助引數中,從而消除未決操作。該技術通常還需要乙個輔助函式,這只是為了保持文法的清晰和對外隱藏輔助引數的實際使用。

例如,可以通過使用兩個用於存放結果的輔助引數來實現乙個尾遞迴的fibonacci函式。原有的樹狀遞迴函式fib需要兩個附註引數來存放結果,這並不奇怪,因為它涉及兩個遞迴呼叫。要計算fib(n),則呼叫fib_ax(n 1 0)

int fib_aux(int n, int next, int result)

樹狀遞迴的fib() 函式是乙個複雜度為o(2^n)的演算法,換句話說當n遞增1時問題尺寸加倍。另一方面,線性遞演算法則是

o(n)的,換句話說,需要的工作量是線性增長的。

references: thomas a. anastasio, richard chang.

編譯原理(三)直接左遞迴與間接左遞迴的消除

這個公式是死記住的,怎麼記住呢?理解著記憶。首先寫出這個式子,然後b肯定打頭,就是如此了。p pa b是原左遞迴式子,一定是有b的,不然就永遠遞迴下去了 p yp 比如 p abp b p p xp 比如 p abcp 什麼意思呢,就是就是有左遞迴的那一部分,全部規約成乙個式子,這樣就成為直接遞迴,...

編譯原理 直接左遞迴和間接左遞迴的消除

採用擴充bnf表示 設有產生式 a a a a 設有產生式 a a a a 引進新的非終結符號,將左遞迴改寫為右遞迴。設有產生式 a a a a 設有產生式 a a 1 a 2 a m 1 2 n 其中yi i 1,2,n 均不以符號u為首,增加新非終結 符號u 將上述產生式變換為 a 1a 2a ...

遞迴式與遞迴

如果你想迅速複習演算法來應付考試或者是面試的話,請看本人的其他部落格。遞迴,儘管簡單,卻並不是良藥。況且,在很多大公司看來,遞迴是一種非常麻煩並且耗時耗力的演算法。誠然,從簡單的計算角度來說,遞迴就是這樣的。但是,如果你換乙個角度來看看計算機的世界,遞迴卻是不能被忽略的。如果你是乙個有著人類智慧型的...