Вопрос

Lucas numbers are similar to Fib numbers except it starts with a 2 instead of a 1:

 2, 1, 3, 4, 7, 11, 18,

I want to write a function to produce a list of Lucas series numbers in decreasing order until the nth element.

I wrote this but I traced it and it is way too slow to implement into my list-building function.

(defun lucas (n)
  (cond 
    ((equal n 0) 2)
    ((equal n 1) 1)
    (t (+ (lucas (- n 1))
          (lucas (- n 2))))))

Here is what I wrote for the list function. The problem is (lucas n) is very slow and I don't want to have to call a helper function when I'm already using 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))))))

What I am trying to achieve:

(lucas-list 3) => '(4 3 1 2))

Any suggestions/help is appreciated

Это было полезно?

Решение

The (pseudo-)linear time algorithm for the Fibonacci numbers can easily be extended to the Lucas numbers:

(define (lucas n)
  (let loop ((a 2) (b 1) (n n))
    (if (= n 0)
      a
      (loop b (+ a b) (- n 1)))))

This can be mapped over the integers to get the desired result:

> (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)

But there's actually a faster way: if you realize that the above algorithm computes Lᵢ₋₁ and Lᵢ₋₂ before it computes Lᵢ, then saving those in a list should save a lot of time. That gives:

(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)))))

In action:

> (lucas 20)
(9349 5778 3571 2207 1364 843 521 322 199 123 76 47 29 18 11 7 4 3 1 2)

Другие советы

An elegant way to optimise fibonacci-type procedures is with a memoïze function which caches every previously calculated result:

In Racket (using hash tables; generic, scales very well)

(define (memoize fn)
  (let ((cache (make-hash)))
    (lambda arg (hash-ref! cache arg (lambda () (apply fn arg))))))

In R6RS Scheme (less efficient and less generic, but still excellent for this purpose)

(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)])))) 

Applied to the lucas procedure (this works like a Python decorator):

(define lucas
  (memoize
   (lambda (n)
     (cond
       ((= n 0) 2)
       ((= n 1) 1)
       (else   (+ (lucas (- n 1)) (lucas (- n 2))))))))

and the list procedure, taking advantage of the fact that using an accumulator reverses the result, becomes:

(define (lucas-list n)
  (let loop ((i 0) (res null))
    (if (= i n)
        res
        (loop (+ i 1) (cons (lucas i) res)))))

Test:

(display (lucas-list 20))
=> {9349 5778 3571 2207 1364 843 521 322 199 123 76 47 29 18 11 7 4 3 1 2}

How about:

(defun lucas-list (n)
  (if (equal n 0)
      '()
      (cons (lucas n) (lucas-list (- n 1)))))
(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))))

Or:

(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))))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top