5
\$\begingroup\$
A “perfect number” is defined as a number equal to the sum of all its factors less than itself. For example, the first perfect number is 6, because its factors are 1, 2, 3, and 6, and 1+2+3=6. The second perfect number is 28, because 1+2+4+7+14=28. What is the third perfect number? Write a procedure (next-perf n) that tests numbers starting with n and continuing with n+1, n+2, etc. until a perfect number is found. Then you can evaluate (next-perf 29) to solve the problem. Hint: you’ll need a sum-of-factors subprocedure.
[Note: If you run this program when the system is heavily loaded, it may take half an hour to compute the answer! Try tracing helper procedures to make sure your program is on track, or start by computing (next-perf 1) and see if you get 6.]

Source

I am aware of Euclid's algorithm for solving this problem, but with the "n+1,n+2" part of the problem statement I think I shouldn't do that. So I did it in a brute force way.

(define (nextPerf n)
    (define (sumOfFactors a)
        (cond ((= n a) 0)
            ((= (remainder n a) 0) (+ a (sumOfFactors (+ a 1))))
        (else (sumOfFactors (+ a 1)))))
    (if (= (sumOfFactors 1) n) n
        (nextPerf (+ n 1))))

Is this good code? How can I make it better?

\$\endgroup\$
4
\$\begingroup\$

You get:

Hint: you’ll need a sum-of-factors subprocedure.

So I would make sum-of-factors a separate procedure. It's a little confusing as is, since sumOfFactors takes a single argument - and that argument isn't the number whose factors you're summing. That is:

(define (sum-of-factors n)
    ...)

(define (next-perf n)
    (if (= (sum-of-factors n) n)
        n
        (next-perf (+ n 1)))

Note that hyphenated-name is the preferred syntax for function names, as opposed to camelCase.

Once we're there, you can write sum-of-factors to do the looping:

(define (sum-of-factors n)
    (define (add-factor fac) (if (= (remainder n fac) 0) fac 0))
    (define (accum fac) (if (= fac n) 0 (+ (add-factor fac) (accum (+ 1 fac)))
    (accum 1))

I split it up into a few more bite-size functions. I find that helps readability.


In Racket (which I guess isn't up for discussion), you can do the latter way better since there's an in-range procedure that gives you want you need:

(define (sum-of-factors n)
    (for/sum ([fac (in-range 1 (- n 1))])
        (if (= (remainder n fac) 0) fac 0))))

Don't think there's a Scheme equivalent unfortunately, so maybe this is more of a tease than an answer.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.