Euler’s Sum Of Powers Conjecture
April 17, 2015
We’ll assume n = 5 and give three solutions of increasing efficiency. First the obvious brute-force solution:
(define (euler n) (do ((e 5 (+ e 1))) ((= e n)) (do ((d (- e 1) (- d 1))) ((zero? d)) (do ((c (- d 1) (- c 1))) ((zero? c)) (do ((b (- c 1) (- b 1))) ((zero? b)) (do ((a (- b 1) (- a 1))) ((zero? a)) (when (= (+ (expt a 5) (expt b 5) (expt c 5) (expt d 5)) (expt e 5)) (display a) (display "^5 + ") (display b) (display "^5 + ") (display c) (display "^5 + ") (display d) (display "^5 = ") (display e) (display "^5") (newline)))))))) > (time (euler 145)) 27^5 + 84^5 + 110^5 + 133^5 = 144^5 (time (euler 145)) 5919 collections 188949 ms elapsed cpu time, including 517 ms collecting 197025 ms elapsed real time, including 429 ms collecting 49846940832 bytes allocated, including 49849177312 bytes reclaimed
There are two sources of inefficiency in that program: recomputation of fifth-powers that have previously been calculated, and all the computations of all the loops. Our second version caches the fifth-power computations:
(define (euler n) (let ((cache (make-vector n))) (do ((i 1 (+ i 1))) ((= i n)) (vector-set! cache i (expt i 5))) (do ((e 5 (+ e 1))) ((= e n)) (do ((d (- e 1) (- d 1))) ((zero? d)) (do ((c (- d 1) (- c 1))) ((zero? c)) (do ((b (- c 1) (- b 1))) ((zero? b)) (do ((a (- b 1) (- a 1))) ((zero? a)) (when (= (+ (vector-ref cache a) (vector-ref cache b) (vector-ref cache c) (vector-ref cache d)) (vector-ref cache e)) (display a) (display "^5 + ") (display b) (display "^5 + ") (display c) (display "^5 + ") (display d) (display "^5 = ") (display e) (display "^5") (newline))))))))) > (time (euler 145)) 27^5 + 84^5 + 110^5 + 133^5 = 144^5 (time (euler 145)) 5951 collections 104692 ms elapsed cpu time, including 264 ms collecting 108670 ms elapsed real time, including 407 ms collecting 50129778992 bytes allocated, including 50127622192 bytes reclaimed
An improvement of not quite two times. Our third implementation short-circuits the loops, stopping them when the requested sum is no longer possible:
(define (euler n) (let ((cache (make-vector n))) (do ((i 1 (+ i 1))) ((= i n)) (vector-set! cache i (expt i 5))) (do ((e 5 (+ e 1))) ((= e n)) (list-of (list a b c d e) (d range (- e 1) 0) (c range (- d 1) 0) (c-sum is (+ (vector-ref cache c) (vector-ref cache d))) (< c-sum (vector-ref cache e)) (b range (- c 1) 0) (b-sum is (+ c-sum (vector-ref cache b))) (< b-sum (vector-ref cache e)) (a range (- b 1) 0) (= (+ b-sum (vector-ref cache a)) (vector-ref cache e)) (display a) (display "^5 + ") (display b) (display "^5 + ") (display c) (display "^5 + ") (display d) (display "^5 = ") (display e) (display "^5") (newline))))) (time (euler 145)) 1298 collections 49421 ms elapsed cpu time, including 111 ms collecting 50556 ms elapsed real time, including 101 ms collecting 10934479232 bytes allocated, including 10933030352 bytes reclaimed
That’s about four times better than the original. But I’m not particularly happy with it; does anybody have any better ideas? Suggestions are welcome. You might also want to parameterize the size of n. You can run the program at http://ideone.com/RVcDBZ.
In Python. I could not find anything clever.
A much faster method from the Lander and Parkin paper. The answer comes in a flash. I feel this can be improved, but this is already much faster.
@Paul: Please provide a citation to the paper.
@ programmingpraxis: the paper: Lander and Parkin
I made a straightforward implementation. I feel it can be speeded up more. I did not use the bit at the bottom of page 101 with the mod 30 stuff.
Another version in Python. It only produces “primitive” solutions, where the terms do not have a common factor. It produces the 2 tables from the Lander and Parkin paper. Also an example from Wikipedia for k=7 is found after a long time (about 40 minutes).
Somehow the superscript 5’s got left out of the 2 last terms, I found this in the html source: 133 = 144
@Marijn: Fixed. Thanks.