I'm trying to make a function that returns a list of perfect numbers:
A perfect number is a positive integer that is equal to the sum of its proper positive divisors
For instance, 28 is perfect because its factors are 1, 2, 4, 7, 14 and 1 + 2 + 4 + 7 + 14 = 28.
My initial code was:
(defun factors (n)
"Return a list of the factors of N, except N itself."
(let ((factors (list 1))
(i 2))
(while (<= i (/ n 2))
(if (zerop (% n i))
(setq factors (cons i factors)))
(setq i (1+ i)))
factors))
(defun is-perfect (n)
"Return `t` is N is a perfect number, `nil` otherwise."
(and (/= 1 n)
(let* ((factors (factors n))
(factors-sum (seq-reduce '+ factors 0)))
(= n factors-sum))))
(defun perfect-numbers (n)
"Return the perfect numbers smaller than or equal to N."
(seq-filter 'is-perfect (number-sequence 2 n)))
It works, but gets noticeably slow for 10000. Remembering that I recently had trouble with a self modifying function I thought I'd try to exploit this to optimize perfect-numbers
. The idea is to keep the list of the perfect numbers we already computed. I tried this:
(defun perfect-numbers (n)
"Return the perfect numbers smaller than or equal to N."
(let* ((perfect-numbers '(6)) ; my hope was that this list would get updated each time I call the function
(highest (car (last perfect-numbers)))
(new-perfect-numbers (seq-filter 'is-perfect (number-sequence (1+ highest) n))))
(setq perfect-numbers (append perfect-numbers new-perfect-numbers))
(seq-filter (apply-partially '>= n) perfect-numbers)))
However in this case, perfect-numbers
does not seems to be updated, and I don't understand why:
ELISP> (perfect-numbers 1000)
(6 28 496)
ELISP> (symbol-function 'perfect-numbers)
(lambda
(n)
"Return the perfect numbers smaller than or equal to N."
(let*
((perfect-numbers
'(6)) ; the list did not get updated :(
(highest
(car
(last perfect-numbers)))
(new-perfect-numbers
(seq-filter 'is-perfect
(number-sequence
(1+ highest)
n))))
(setq perfect-numbers
(append perfect-numbers new-perfect-numbers))
(seq-filter
(apply-partially '>= n)
perfect-numbers)))