4

我被要求編寫一個程序,通過遞歸過程來計算帕斯卡三角形的元素。我可以創建一個過程,返回三角形中的單個行或特定行中的一個數字。有沒有更有效的方法來編寫這個遞歸過程?

這裏是我的解決方案:

(define (f n) 
    (cond ((= n 1) '(1)) 
     (else 
     (define (func i n l) 
      (if (> i n) 
       l 
       (func (+ i 1) n (cons (+ (convert (find (- i 1) (f (- n 1)))) 
             (convert (find i (f (- n 1))))) 
            l)))) 
     (func 1 n '())))) 

(define (find n l) 
    (define (find i n a) 
    (if (or (null? a) (<= n 0)) 
     '() 
     (if (>= i n) 
      (car a) 
      (find (+ i 1) n (cdr a))))) 
    (find 1 n l)) 

(define (convert l) 
    (if (null? l) 
     0 
     (+ l 0))) 

這似乎很好地工作,但它變得非常低效的尋找開始(f 8)較大行的元素。通過遞歸過程解決這個問題是否有更好的過程?

另外,如果我想使用迭代過程(尾遞歸),我該如何編寫它?

+0

回覆我的編輯,你的代碼可能是與使用更加可讀'cond'而不是嵌套'if's,以及使用命名'let' ins內在的定義但是我僅將修改保留爲僅用於格式化。 – 2014-09-29 16:07:04

回答

3

有幾種方法可以優化算法,其中一個最好的方法是使用dynamic programming來有效地計算每個值。這是我自己的solution對一個類似的問題,其中包括引用更好地理解這種方法 - 這是一個尾遞歸,迭代過程。關鍵的一點是,它使用變異操作來更新預先計算值的向量,這是一個簡單的事情,以適應執行打印列表給定行:

(define (f n) 
    (let ([table (make-vector n 1)]) 
    (let outer ([i 1]) 
     (when (< i n) 
     (let inner ([j 1] [previous 1]) 
      (when (< j i) 
      (let ([current (vector-ref table j)]) 
       (vector-set! table j (+ current previous)) 
       (inner (add1 j) current)))) 
     (outer (add1 i)))) 
    (vector->list table))) 

另外,從@ Sylwester的solution借款我們可以編寫一個純粹的函數式尾遞歸迭代版本,它使用列表來存儲預先計算的值;在我的測試,這比以前的版本更慢:

(define (f n) 
    (define (aux tr tc prev acc) 
    (cond ((> tr n) '())   
      ((and (= tc 1) (= tr n)) 
      prev) 
      ((= tc tr) 
      (aux (add1 tr) 1 (cons 1 acc) '(1))) 
      (else 
      (aux tr 
       (add1 tc) 
       (cdr prev) 
       (cons (+ (car prev) (cadr prev)) acc))))) 
    (if (= n 1) 
     '(1) 
     (aux 2 1 '(1 1) '(1)))) 

無論哪種方式,它的工作如預期的更大的投入,這將是快n值幾十萬順序:

(f 10) 
=> '(1 9 36 84 126 126 84 36 9 1) 
+2

+1 Yay for DP。 :-D – 2014-09-29 16:00:20

+0

我是新來的計劃,所以我不太瞭解像表,make-vector等結構和功能。我嘗試檢出文檔,但它並不真正有用。你能簡要解釋一下他們每個人做什麼嗎? – user3450695 2014-09-29 16:30:12

+0

@ user3450695第一個版本使用向量,將它們視爲固定大小的列表,允許在給定索引的情況下快速檢索,並且還允許修改值 - 就像其他編程語言中的數組一樣。也許你會發現這個[documentation](http://docs.racket-lang.org/reference/vectors.html)更易於理解。如果第一個解決方案太複雜,那麼堅持第二個版本,它只使用基本程序。 – 2014-09-29 16:48:06

1

已經有很多soluitons,他們指出,使用動態編程是一個很好的選擇。我認爲這可以寫得更簡單一點。以下是我將作爲一個簡單的基於列表的解決方案。它是基於觀察,如果行n是(a b c d e),那麼行n + 1是(a(+ a b)(+ b c)(+ c d)(+ d e)e)。一個容易計算的方法是迭代(0 a b c d e)收集((+ 0 a)(+ a b)...(+ d e)e)的尾部。

(define (pascal n) 
    (let pascal ((n n) (row '(1))) 
    (if (= n 0) row 
     (pascal (- n 1) 
       (maplist (lambda (tail) 
          (if (null? (cdr tail)) 1 
           (+ (car tail) 
            (cadr tail)))) 
         (cons 0 row)))))) 

(pascal 0) ;=>  (1) 
(pascal 1) ;=> (1 1) 
(pascal 2) ;=> (1 2 1) 
(pascal 3) ;=> (1 3 3 1) 
(pascal 4) ;=> (1 4 6 4 1) 

本作使用的輔助功能MAPLIST的:

(define (maplist function list) 
    (if (null? list) list 
     (cons (function list) 
      (maplist function (cdr list))))) 

(maplist reverse '(1 2 3)) 
;=> ((3 2 1) (3 2) (3)) 
+2

downvote的任何原因?我願意改進?我認爲這是使用動態編程計算這些行的相當乾淨的方式(我們一次只保留兩行)。它直接解決了問題的最後部分:「另外,如果我想使用迭代過程(尾遞歸),我將如何編寫它?」這是尾遞歸/迭代。 – 2014-09-30 16:57:21

相關問題