尾遞歸任何語言只是一種循環表示爲遞歸。當你有一個「分叉」的遞歸定義時,從T(n)到T(n-1)和T(n-2)的初始實現很可能是指數的,那麼T(n -2),T(n-3),T(n-3),T(n-4),使每一步的計算加倍。
訣竅是逆轉計算,以便從T(1)和T(2)建立起來。這隻需要每個步驟都有恆定的時間,所以整體計算是線性的。
開始
(let ((n 2)
(t-n 2)
(t-n-1 1))
…)
讓我們用一個do
循環更新:
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
…)
現在你只需要停下來,當你達到你想要的n
:
(defun telephone-number (x)
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n)))
要完成後,檢查你的輸入:
(defun telephone-number (x)
(check-type x (integer 1))
(if (< x 3)
x
(do ((n 2 (1+ n))
(t-n 2 (+ t-n (* n t-n-1)))
(t-n-1 1 t-n))
((= n x) t-n))))
此外,編寫測試和添加文檔這是什麼以及如何使用它。這尚未經過測試。
當寫這個尾遞歸,遞歸你用新值:
(defun telephone (x)
(labels ((tel-aux (n t-n t-n-1)
(if (= n x)
t-n
(tel-aux (1+ n)
(+ t-n (* n t-n-1))
t-n))))
(tel-aux 2 2 1)))
當尾遞歸優化,這種鱗片像環(但常數因子可能會有所不同)。請注意,Common Lisp不要求尾部呼叫優化。
這太好了!但出於某種原因,它看起來像輸出數字不是預期的。 (即(電話10)應該是9496,但是這個代碼是1890。) – user3745602
@ user3745602,正確的版本如下:'(defun電話號碼(x) (校驗類型x(整數1)) (if(
Renzo
@Renzo:你應該讓'n'跟蹤當前的'n',所以從2開始,乘以'n',將'n'與'x'進行比較。感謝您的更正,我會編輯(雖然未經測試)。 – Svante