Leading Digits Of Powers Of 2
June 23, 2017
We start with a function to calculate the leading digit of a power of 2:
> (expt 2 32) 4294967296
As Cook did, we first write a program that calculates the power of 2, converts it to a string, then extracts its first digit:
(define (leading-digit two-to-n) ; slow (- (char->integer (string-ref (number->string (expt 2 two-to-n)) 0)) 48))
> (leading-digit 32) 4
That’s slow; the problem is the conversion from number to string, which is a quadratic algorithm. Cook points out that using logarithms is much faster:
(define (leading-digit two-to-n) ; fast (let* ((q (* (log10 2) two-to-n)) (r (- q (truncate q)))) (let loop ((i 2)) (if (< r (log10 i)) (- i 1) (loop (+ i 1))))))
> (leading-digit 32) 4
We could make that even faster by precalculating the base-10 logarithms and unrolling the loop. Whichever version of leading-digit
we use, the following function performs the counting:
(define (lead2 n) (let ((counts (make-vector 10 0))) (do ((i 0 (+ i 1))) ((= i n) counts) (let ((d (leading-digit i))) (vector-set! counts d (+ (vector-ref counts d) 1))))))
> (lead2 1000000) #(0 301030 176093 124937 96911 79182 66947 57990 51154 45756)
Those counts are astonishingly close to the predictions of Benford’s law:
> (let ((counts (lead2 1000000))) (do ((i 2 (+ i 1))) ((= i 11)) (let ((benford (inexact->exact (- (round (* (log10 i) 1000000)) (round (* (log10 (- i 1)) 1000000)))))) (display (- i 1)) (display #\tab) (display benford) (display #\tab) (display (vector-ref counts (- i 1))) (display #\tab) (display (- benford (vector-ref counts (- i 1)))) (newline)))) 1 301030 301030 0 2 176091 176093 -2 3 124939 124937 2 4 96910 96911 -1 5 79181 79182 -1 6 66947 66947 0 7 57992 57990 2 8 51153 51154 -1 9 45757 45756 1
This experiment would also work for powers of 3, or 4, or any number except 10 (because the leading digit of powers of 10 is always 1). You can run the program at http://ideone.com/L7YKvs.
In Python
I looked at when the fractional part of the power begins to look useless in 64-bit floating point, then generated random samples of one million powers with a 32-bit exponent; those look ok. (In Julia.)
Using decimal in Python as indicated by Andrew Dalke in John Cook’s blog, the str operation is very fast. The code below needs about 1 second.
Solution in GNU bc:
Which outputs: