Lucas数与Fib数相似,但它以2而不是1:
开头 2, 1, 3, 4, 7, 11, 18,
我想编写一个函数来按递减顺序生成Lucas序列号列表,直到第n个元素。
我写了这个,但我跟踪了它,实现我的列表构建功能的速度太慢了。
(defun lucas (n)
(cond
((equal n 0) 2)
((equal n 1) 1)
(t (+ (lucas (- n 1))
(lucas (- n 2))))))
这是我为list函数编写的内容。问题是(lucas n)非常慢,我不想在我已经使用let时调用辅助函数。
(defun lucas-list(n)
(cond
((equal n 0) (cons n nil))
(t (let ((n1 (lucas n)) (n2 (lucas(- n 1))))
(cons n1 (lucas-list n2))))))
我想要实现的目标:
(lucas-list 3) => '(4 3 1 2))
感谢任何建议/帮助
答案 0 :(得分:3)
(pseudo - )线性时间algorithm for the Fibonacci numbers可以很容易地扩展到卢卡斯数字:
(define (lucas n)
(let loop ((a 2) (b 1) (n n))
(if (= n 0)
a
(loop b (+ a b) (- n 1)))))
这可以映射到整数以获得所需的结果:
> (map lucas '(0 1 2 3 4 5))
(2 1 3 4 7 11)
> (reverse (map lucas '(0 1 2 3 4 5)))
(11 7 4 3 1 2)
但实际上有一种更快的方法:如果你意识到上述算法在计算Lᵢ之前计算Lᵢ₋1和Lᵢ₋2,那么将它们保存在列表中应该可以节省大量时间。这给了:
(define (lucas n)
(let loop ((a 2) (b 1) (n n) (l '()))
(if (= n 0)
l
(loop b (+ a b) (- n 1) (cons a l)))))
行动中:
> (lucas 20)
(9349 5778 3571 2207 1364 843 521 322 199 123 76 47 29 18 11 7 4 3 1 2)
答案 1 :(得分:1)
优化斐波那契类型程序的一种优雅方法是使用memoïze函数缓存每个先前计算的结果:
在Racket中(使用哈希表;通用,非常好地扩展)
(define (memoize fn)
(let ((cache (make-hash)))
(lambda arg (hash-ref! cache arg (lambda () (apply fn arg))))))
在R6RS计划中(效率较低且不太通用,但仍然非常适用于此目的)
(define (memoize proc)
(let ([cache '()])
(lambda (x)
(cond
[(assq x cache) => cdr]
[else (let ([ans (proc x)])
(set! cache (cons (cons x ans) cache))
ans)]))))
应用于lucas过程(这类似于Python装饰器):
(define lucas
(memoize
(lambda (n)
(cond
((= n 0) 2)
((= n 1) 1)
(else (+ (lucas (- n 1)) (lucas (- n 2))))))))
和列表程序利用累加器反转结果的事实变为:
(define (lucas-list n)
(let loop ((i 0) (res null))
(if (= i n)
res
(loop (+ i 1) (cons (lucas i) res)))))
测试:
(display (lucas-list 20))
=> {9349 5778 3571 2207 1364 843 521 322 199 123 76 47 29 18 11 7 4 3 1 2}
答案 2 :(得分:0)
怎么样:
(defun lucas-list (n)
(if (equal n 0)
'()
(cons (lucas n) (lucas-list (- n 1)))))
答案 3 :(得分:0)
(defun lucas-list-decreasing (n &aux (table (make-hash-table)))
(labels ((lucas-int (n)
(or (gethash n table)
(setf (gethash n table)
(cond ((equal n 0) 2)
((equal n 1) 1)
(t (+ (lucas-int (- n 1))
(lucas-int (- n 2)))))))))
(lucas-int n)
(loop for i downfrom (1- n) downto 0
collect (gethash i table))))
或者:
(defun lucas (n &aux (table (make-hash-table)))
(labels ((lucas-int (n)
(or (gethash n table)
(setf (gethash n table)
(cond ((equal n 0) 2)
((equal n 1) 1)
(t (+ (lucas-int (- n 1))
(lucas-int (- n 2)))))))))
(values (lucas-int n)
table)))
(defun lucas-list-decreasing (n)
(multiple-value-bind (value table)
(lucas n)
(declare (ignore value))
(loop for i downfrom (1- n) downto 0
collect (gethash i table))))