我正在尝试学习一些函数式编程,并且正在为计划(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))))))
我正在试图弄清楚如何使这个函数尾调用递归,但我不知道该怎么做。我需要一些帮助,开始考虑如何使用尾调用优化来优化这样的函数。
答案 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兼容方案