有没有更有效的方法来编写这个递归过程?

时间:2014-09-29 15:38:40

标签: recursion functional-programming scheme tail-recursion pascals-triangle

我被要求编写一个程序,通过递归过程计算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)开头的较大行的元素效率非常低。有没有更好的程序通过递归过程解决这个问题?

另外,如果我想使用迭代过程(尾递归),我该如何编写呢?

2 个答案:

答案 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))