尾调用优化球拍

时间:2013-06-23 10:47:48

标签: recursion functional-programming scheme racket

我正在尝试学习一些函数式编程,并且正在为计划(racket)做项目euler问题让我开始。我现在在problem 15,我认为我有一个正确的函数来计算晶格中的路径数。问题是对于大量的gridSize,该函数需要很长时间才能运行。

(define uniqueTraverse
  (lambda (x y gridSize)
    (cond
      ((and (eq? x gridSize) (eq? y gridSize)) 1)
      ((eq? x gridSize) (uniqueTraverse x (+ y 1) gridSize))
      ((eq? y gridSize) (uniqueTraverse (+ x 1) y gridSize))
      (else (+ (uniqueTraverse (+ x 1) y gridSize)
               (uniqueTraverse x (+ y 1) gridSize))))))

我正在试图弄清楚如何使这个函数尾调用递归,但我不知道该怎么做。我需要一些帮助,开始考虑如何使用尾调用优化来优化这样的函数。

2 个答案:

答案 0 :(得分:6)

问题是您反复重复计算相同的结果。 要解决这个问题,你不需要尾调用 - 你需要记住旧的 结果并返回它们而不重新计算它们。这种技术称为memoization。

这是一个解决方案:

#lang racket

(define old-results (make-hash))

(define uniqueTraverse
  (lambda (x y gridSize)
    (define old-result (hash-ref old-results (list x y) 'unknown))
    (cond 
      ; if the result is unknown, compute and remember it
      [(eq? old-result 'unknown)
       (define new-result
         (cond
           ((and (eq? x gridSize) (eq? y gridSize)) 1)
           ((eq? x gridSize) (uniqueTraverse x (+ y 1) gridSize))
           ((eq? y gridSize) (uniqueTraverse (+ x 1) y gridSize))
           (else (+ (uniqueTraverse (+ x 1) y gridSize)
                    (uniqueTraverse x (+ y 1) gridSize)))))
       (hash-set! old-results (list x y) new-result)
       new-result]
      ; otherwise just return the old result
      [else old-result])))

(uniqueTraverse 0 0 2)

答案 1 :(得分:2)

记忆是一种方式,另一种是使用不同的数据表示。

我使用表示为矩阵的网格或矢量矢量。

然后将顶行的值设置为1(因为只有顶部边缘的路径。

之后,行中第一行的下一行是1,第二行是上面第一列中的条目的值,加上行中的条目或值,

递归行中的每个点,然后递归每行。

然后答案是你完成递归后最后一行的最后一点。

对于3x3网格

1 1 1
1 2 3
1 3 6

6

如果键非常靠近,(连续或几乎如此),向量表示将比哈希更高效。

(define (make-lattice-point-square n)
(let ((lps (make-vector (+ n 1))))
 (let loop ((i 0))
  (if (> i n)
      lps
      (begin
          (vector-set! lps i (make-vector (+ n 1)))
          (loop (++ i)))))))

(define (lattice-ref lat x y)
;; where x is row, y is column thought it's not really important
(vector-ref (vector-ref lat y) x)) 

(define (lattice-set! lat x y value)
 (vector-set! (vector-ref lat y) x value))

;; paths through a point are equal the the paths through the above point,
;; plus the paths through the left, those along the top and left edges 
;; only have one possible path through them

(define (ways-exit-lattice n)
 (let ((lps (make-lattice-point-square n)))
  (letrec 
    ((helper 
      (lambda (x y)
    (if (or (= x 0) (= y 0))
             (lattice-set! lps x y 1)
         (lattice-set! lps x y
                (+ (lattice-ref lps (- x 1) y)
              (lattice-ref lps x (- y 1)))))))
     (lattice-walker
      (lambda (x y)
      (cond ((and (= x n) (= y n)) 
                 (begin (helper x y) (lattice-ref lps x y)))
                ((= y n) 
                 (begin 
                  (helper x y)
                  (lattice-walker (++ x) 0)))
                (else 
                 (begin
                  (helper x y)
                  (lattice-walker x (++ y))))))))
   (lattice-walker 0 0))))  

注意所有对latice-walker的调用都是尾调用。

使用RSR5兼容方案