遞歸 (電腦科學)
遞歸(粵拼:dai6 gwai1;英文:recursion)喺程式編寫上,如果係講緊子程式嘅定義,狹義上係指子程式嘅定義會直接用到[註 1]自己,廣義上就指子程式會直接或者間接用到自己[1]:41;如果講子程式嘅執行,指嘅係指子程式會直接或者間接啟動[註 2]自己[1]:121。遞歸可以用來將一條問題分做條問題「細啲嘅版本」噉嚟解[2]。
狹義上子程式嘅遞歸,原則上同邏輯上講嗰種遞歸上係同一樣嘢,都係 「用自己定義自己」,但係子程式嘅啟動牽涉有限資源同副作用等等;廣義嘅遞歸,或者執行嘅時候講嘅遞歸,都可以視為狹義嘅遞歸喺特定語境之下嘅引伸義。
喺程式語言嘅理論上,遞歸有兩種特別情形:如果喺實際執行嘅時候,一個子程式嘅啟動(activation)最多只會再啟動自己一次,呢種叫 linear recursion(線性遞歸)[1]:42。如果一個子程式一係唔啟動自己,一係就喺輸出(return)嘅時候先啟動自己,呢種叫尾遞歸(tail recursion)[1]:43;喺翻譯嘅時候,尾遞歸係可以有方法令佢實際上變成唔涉及遞歸嘅其他嘅流程控制[1]:143。
概論
[編輯]基本諗頭
[編輯]遞歸最重要嘅特徵,係指一個子程式會「用到自己」或者「
- 呢個子程式叫 factorial(x)
- 如果 x == 0:
- Output 係 1
- 否則:
- Output 係 x 乘以 factorial(x-1)
- 如果 x == 0:
——factorial
呢個子程式,會「用返自己」。假如 input x
數值係 5,行呢個程式就會好似噉:
- 個程式行
factorial(x-1)
,呢個factorial
以 4 做 input; - 個程式行
factorial(x-1)
,呢個factorial
以 3 做 input; - 個程式行
factorial(x-1)
,呢個factorial
以 2 做 input; - 個程式行
factorial(x-1)
,呢個factorial
以 1 做 input; - 個程式行
factorial(x-1)
,呢個factorial
以 0 做 input; - 因為
x
數值去到 0,個子程式出 1; return x * factorial(x-1)
,factorial(0)
出 1,1 * 1
出 1;return x * factorial(x-1)
,factorial(1)
出 1,2 * 1
出 2;return x * factorial(x-1)
,factorial(2)
出 2,3 * 2
出 6;return x * factorial(x-1)
,factorial(3)
出 6,4 * 6
出 24;return x * factorial(x-1)
,factorial(4)
出 24,5 * 24
出 120;- 最後成個程式出 120 做答案。
如是者,個程式就計到階乘 。
兩大部份
[編輯]一個用咗遞歸嘅子程式,有兩大組成部份:基本個案同遞歸個案:
- 基本個案[e 2]會「即刻畀 output」,唔會引致任何進一步嘅遞歸,電腦一𠹭佢,就會得到 output。好似係計階乘嗰個例子噉,佢個基本個案就係
如果 x == 0:output 係 1
嗰一截。用親遞歸嘅子程式都梗要有個基本個案,唔係嘅話個子程式就會進入無限迴圈。 - 遞歸個案[e 3]會有進一步遞歸,但係每一次遞歸個問題都會「細咗」或者「簡單咗」,而且會愈嚟愈接近基本個案。喺計階乘嘅例子裡便,佢個遞歸個案係
否則:output 係 x 乘以 factorial(x-1)
[註 3]嗰截——呢段嘢每行一次,個問題都會愈嚟愈接近基本個案。
有啲人會將上述過程想像成「一個堆堆裝住未解嘅遞歸個案,遞歸個案一個個噉堆,堆到去到基本個案,再一個個噉解咗佢」[5]。
遞歸種類
[編輯]「遞歸嘅威力在於佢能夠用有限嘅陳述式,定義無限咁多件物件。同一道理,有限嘅遞歸程式可以描述數量無限嘅運算,而且就算個程式冇講明要重複都得。」
單一定多重
[編輯]遞歸可以分做單一同多重兩種。呢個分類係講緊個子程式會
例如費氏數列[e 4]就可以用多重遞歸嚟計[7]。費氏數列係一個好出名嘅數列,頭兩個數係 0 同 1,跟住落嚟嘅數就係之前兩個數加埋嘅和,即係:
- ,而且
想像以下呢段演算法嘅虛擬碼,用家是但入一個正整數 n,段演算法會計出費氏數列入便第 n 個數係乜[7][8]:
- 定義子程式 fibGen(self, n):
- 如果 n <= 1:
- 畀 n 做答案
- 否則
- 畀 fibGen(n-1) + fibGen(n-2)
- 如果 n <= 1:
好多編程工作者都主張,多重遞歸呢家嘢可免則免。一個程式用咗多重遞歸,好易出現運算複雜度過高嘅問題。例如睇返上面嗰段計費氏數列嘅演算法噉,個子程式每行一次,都會𠹭自己兩次,然後嗰兩個𠹭就會各自產生多兩個𠹭變成四個𠹭——兩重遞歸嘅運算複雜度會傾向同 成正比。而如果段演算法有三重遞歸,個子程式每行一次,就會𠹭自己三次,而三個𠹭都會各自產生多三個𠹭變成九個𠹭——三重遞歸嘅運算複雜度會傾向同 成正比[註 4]。
直接定間接
[編輯]尾遞歸
[編輯]理論分析
[編輯]出名應用
[編輯]喺編程上,遞歸可以用嚟解決一啲可以揼散做一大柞細問題嘅問題,而且當中每一個細問題都屬同一個類型。
對比疊代
[編輯]常見錯誤
[編輯]睇埋
[編輯]文獻
[編輯]- Dijkstra, Edsger W. (1960). "Recursive Programming". Numerische Mathematik. 2 (1): 312–318. doi:10.1007/BF01386232.
參考
[編輯]註釋:
- ↑ 1.0 1.1 1.2 1.3 1.4 Sethi, Ravi (1990) [1989]. Programming Languages: Concepts and Constructs. Reading, MA: Addison-Wesley. ISBN 0-201-10365-6.
- ↑ 2.0 2.1 Jaimini, Vaibhav. "Recursion and Backtracking".
- ↑ Brassard, G., and Bratley, P. Fundamental of Algorithmics, Prentice-Hall, 1996.
- ↑ Reading 14: Recursion,麻省理工學院
- ↑ How Recursion Works — Explained with Flowcharts and a Video. freeCodeCamp.
- ↑ Wirth, Niklaus (1976). Algorithms + Data Structures = Programs. Prentice-Hall. p. 126. "The power of recursion evidently lies in the possibility of defining an infinite set of objects by a finite statement. In the same manner, an infinite number of computations can be described by a finite recursive program, even if this program contains no explicit repetitions."
- ↑ 7.0 7.1 Understanding Multiple Recursion Calls: Unraveling the Fibonacci Problem. Medium.
- ↑ Multiple recursion.
拎
[編輯]- 遞歸簡介 — 數據結構同演算法教學,GeeksForGeeks