我被要求编写一个程序,通过递归过程计算Pascal三角形的元素。我可以创建一个过程,返回三角形中的单个行或特定行中的数字。
这是我的解决方案:
(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 :(得分: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)
答案 1 :(得分:1)
已经提出了许多解决方案,他们确实指出,使用动态编程是一个不错的选择。我认为这可以写得更简单一些。这就是我作为一个简单的基于列表的解决方案所做的事情。基于观察结果,如果行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))