(define (leftmost-digit base n) (if (< n base) n (let ((leftmost-pair (leftmost-digit (* base base) n))) (if (< leftmost-pair base) leftmost-pair (quotient leftmost-pair base)))))The idea is this: if we have a one digit number, we just return it, otherwise we recursively call

`leftmost-digit`

with the *square*of the base. Squaring the base will mean gobbling up pairs of digits in the recursive call, so we'll get back either a one or two digit answer from the recursion. If it is one digit, we return it, otherwise it's two digits and we divide by the base to get the left one.

For example, if our number is 12345678 and the base is 10, we'll make a recursive call with base 100. The recursive call will deal with number as if it were written

`12 34 56 78`

in base 100 and return the answer 12. Then we'll divide that by 10 to get the 1.Since we're squaring the base, we're doubling the number of digits we're dealing with on each recursive call. This leads to the solution in O(log log n) time. If we instrument

`quotient`

, you can see:(leftmost-digit 10 816305093398751331727331379663195459013258742431006753294691576) 816305093398751331727331379663195459013258742431006753294691576 / 100000000000000000000000000000000 8163050933987513317273313796631 / 10000000000000000 816305093398751 / 100000000 8163050 / 10000 816 / 100A sixty-three digit number trimmed down to one digit with only five divisions.

So a simple solution to the puzzle is:

(define (leftmost-digit+ base n) (if (< n base) (values n 0) (call-with-values (lambda () (leftmost-digit+ (* base base) n)) (lambda (leftmost-pair count) (if (< leftmost-pair base) (values leftmost-pair (* count 2)) (values (quotient leftmost-pair base) (+ (* count 2) 1)))))))The second value is the count of how many digits we discard. If the number is less than the base, we return it and we discarded nothing. Otherwise, we make the recursive call with the base squared and get back two values, the leftmost pair and the number of pairs it discarded. If the leftmost pair is a single digit, we return it, otherwise we divide by the base. The number of digits discarded is simply twice the number discarded by the recursive call, plus one more if we had to divide.

But I don't see an easy way to separate finding the digit from finding the position. At first it seemed straightforward to just count the digits being discarded, but you can't decide whether to increment the count at each stage without determining if the leftmost part of the recursive call contains one or two digits.

by Joe Marshall (noreply@blogger.com) at August 22, 2014 10:57 PM