Reciprocal Palindromes
November 30, 2021
We wrote a palindrome generator in a previous exercise, which makes this easy:
(define-generator (palindromes) (do ((k 0 (+ k 1))) ((= k 10)) (yield k)) (do ((i 1 (* i 10))) (#f) (do ((j i (+ j 1))) ((= j (* 10 i))) (let ((ds (digits j))) (yield (undigits (append ds (reverse ds)))))) (do ((j i (+ j 1))) ((= j (* 10 i))) (let ((ds (digits j))) (do ((k 0 (+ k 1))) ((= k 10)) (yield (undigits (append ds (list k) (reverse ds))))))))) (define (nth-reciprocal-palindrome-sum n) (let ((pal (palindromes))) (pal) ; discard 0 (do ((p (pal) (pal)) (sum 0 (+ sum (/ 1.0 p)))) ((< n p) sum)))) > (nth-reciprocal-palindrome-sum 1000) 3.3190020006509102 > (nth-reciprocal-palindrome-sum 1000000) 3.3674689702500697 > (nth-reciprocal-palindrome-sum 1000000000) 3.3702320909393553 > (nth-reciprocal-palindrome-sum 1000000000000) 3.370280445226788 > (nth-reciprocal-palindrome-sum 100000000000000) 3.3702829780846546 > (nth-reciprocal-palindrome-sum 100000000000000000) 3.37028322578849
Okay, this is getting ridiculous.
You are adding up the reciprocals in reverse order of size, so you will converge on a sum that is unchanged when the next value is added in (seems to be about 3.370283225788489823 using doubles). We could generate the palindromes in reverse order (starting from somewhere suitably small) but a possibly nicer solution is to use the Kahan summation algorithm (https://en.wikipedia.org/wiki/Kahan_summation_algorithm). Alternatively, you can peek at https://oeis.org/A118031 to see the answer.
Some code using Kahan summation:
And some output, first column is compensated sum, second is “naive” sum, third is iterations. Convergence is slow so will have to run for a while to compare with OEIS value:
After running for nearly 24 hours and adding up the first trillion reciprocals, we’ve only got as far as the 11th decimal place, but looks like the summation algorithm is doing its job (https://oeis.org/A118031 gives 3.370283259497..):
Each decimal place takes around 10 times longer than the last, so I’ve stopped it there rather than running until Christmas. It would be fun to add something to the code that uses extrapolation to estimate where we are currently converging to.
It goes a little faster if the todouble() parameter is made a const reference.
https://pastebin.com/fpniQL4u