我从here找到了一个常规备忘录,这使得fib
更快
(define (memoize fn)
(let ((cache (make-hash)))
(λ arg (hash-ref! cache arg (thunk (apply fn arg))))))
(define fib
(memoize
(lambda (n)
(if (< n 2) n (+ (fib (sub1 n)) (fib (- n 2)))))))
我尝试在自然递归中实现Racket中的interleaving string问题
(define (is-interleave-rec s1 s2 s3)
(if (eq? (+ (string-length s1) (string-length s2))
(string-length s3))
(aux-rec s1 0 s2 0 s3 0)
#f))
(define (aux-rec s1 p1 s2 p2 s3 p3)
(cond
[(eq? p3 (string-length s3)) #t]
[(eq? p1 (string-length s1))
(equal? (substring s2 p2) (substring s3 p3))]
[(eq? p2 (string-length s2))
(equal? (substring s1 p1) (substring s3 p3))]
[(and (eq? (string-ref s1 p1) (string-ref s3 p3))
(eq? (string-ref s2 p2) (string-ref s3 p3)))
(or (aux-rec s1 (add1 p1) s2 p2 s3 (add1 p3))
(aux-rec s1 p1 s2 (add1 p2) s3 (add1 p3)))]
[(eq? (string-ref s1 p1) (string-ref s3 p3))
(aux-rec s1 (add1 p1) s2 p2 s3 (add1 p3))]
[(eq? (string-ref s2 p2) (string-ref s3 p3))
(aux-rec s1 p1 s2 (add1 p2) s3 (add1 p3))]
[else #f]))
然后是一个memoization版本
(define (is-interleave-mem s1 s2 s3)
(if (eq? (+ (string-length s1) (string-length s2))
(string-length s3))
(aux-mem s1 0 s2 0 s3 0)
#f))
(define aux-mem
(memoize
(λ (s1 p1 s2 p2 s3 p3)
(cond
[(eq? p3 (string-length s3)) #t]
[(eq? p1 (string-length s1))
(equal? (substring s2 p2) (substring s3 p3))]
[(eq? p2 (string-length s2))
(equal? (substring s1 p1) (substring s3 p3))]
[(and (eq? (string-ref s1 p1) (string-ref s3 p3))
(eq? (string-ref s2 p2) (string-ref s3 p3)))
(or (aux-mem s1 (add1 p1) s2 p2 s3 (add1 p3))
(aux-mem s1 p1 s2 (add1 p2) s3 (add1 p3)))]
[(eq? (string-ref s1 p1) (string-ref s3 p3))
(aux-mem s1 (add1 p1) s2 p2 s3 (add1 p3))]
[(eq? (string-ref s2 p2) (string-ref s3 p3))
(aux-mem s1 p1 s2 (add1 p2) s3 (add1 p3))]
[else #f]))))
令我惊讶的是,memoization版本较慢,测试用例是
(define as (make-string 10000 #\a))
(define zs (make-string 10000 #\z))
(define bs (make-string 10000 #\b))
(define az (string-append as zs))
(define abz (string-append as bs zs))
(time (is-interleave-rec az bs abz))
(time (is-interleave-mem az bs abz))
结果将是
cpu time: 4 real time: 4 gc time: 0
#t
cpu time: 5333 real time: 5348 gc time: 67
#t
我认为原因是哈希表有很多参数,我想知道是否可以改进它?
答案 0 :(得分:3)
我更改了代码如下:
(define allcalls 0)
(define memcalls 0)
(define (memoize fn)
(let ((cache (make-hash)))
(λ arg
(set! allcalls (add1 allcalls))
(hash-ref! cache arg
(thunk
(set! memcalls (add1 memcalls))
(apply fn arg))))))
跟踪调用aux-mem
的次数,以及调用基础过程的次数。
添加
(displayln allcalls)
(displayln memcalls)
最后我
cpu time: 2 real time: 2 gc time: 0
#t
cpu time: 7046 real time: 7040 gc time: 30
#t
20001
20001
意味着aux-mem
永远不会使用相同的参数调用两次。
所以你的记忆完全无效(毕竟,记忆的目的是返回一个已经被要求并因此在之前计算过的结果),而这里所做的只是增加开销。