3

我正在尝试学习一些函数式编程,并且正在解决方案(球拍)中的项目欧拉问题以帮助我入门。我目前正在处理问题 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))))))

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

4

2 回答 2

6

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

这是一种解决方案:

#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)
于 2013-06-23T11:29:54.797 回答
2

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

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

然后将顶行的值设置为 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 的方案

于 2013-06-23T11:55:21.597 回答