Big Numbers: Functions

June 28, 2011

The greatest common divisor is calculated by Euclid’s algorithm:

(define (big-gcd m n)
  (let loop ((m (big-abs m)) (n (big-abs n)))
    (cond ((big-lt? m n) (loop m (big-minus n m)))
          ((big-lt? n m) (loop (big-minus m n) n))
          (else m))))

Raising to a power is done by the square-and-multiply algorithm; so is modular exponentiation:

(define (big-expt b e)
  (if (big-zero? e) '(1 1)
    (let loop ((s b) (i e) (a '(1 1)))
      (let ((a (if (big-odd? i) (big-times a s) a))
            (i (big-quotient i '(1 2))))
        (if (big-zero? i) a (loop (big-times s s) i a))))))

(define (big-expm b e m)
  (define (m* x y) (big-modulo (big-times x y) m))
  (cond ((big-zero? e) '(1 1))
        ((big-even? e) (big-expm (m* b b) (big-quotient e '(1 2)) m))
        (else (m* b (big-expm (m* b b) (big-quotient (big-minus e '(1 1)) '(1 2)) m)))))

Square roots are calculated by Newton’s algorithm based on derivatives; the initial value is approximated by taking half the logarithm in the car of the big number:

(define (big-sqrt n)
  (if (not (big-positive? n))
      (error 'big-sqrt "must be positive")
      (let loop ((x (cons (quotient (+ (car n) 1) 2) (cdr n))))
        (let ((y (big-quotient (big-plus x (big-quotient n x)) '(1 2))))
          (if (big-lt? y x) (loop y) x)))))

Random numbers are generated by the Park/Miller algorithm. Since the seed must be between 0 and 232, there are many big numbers that can never be returned by the random number generator.

(define big-rand ; Park/Miller algorithm
  (let ((a (string->big "16807"))
        (m (string->big "2147483647"))
        (s (string->big "1043618065")))
    (lambda args
      (if (eq? (car args) 'seed)
          (begin (set! s (cadr args)) s)
          (let ((first (if (pair? (cdr args)) (car args) '(0)))
                (past (if (pair? (cdr args)) (cadr args) (car args))))
            (set! s (big-modulo (big-times a s) m))
            (big-plus first (big-quotient (big-times s (big-minus past first)) m)))))))

Conversions between native big numbers and our big numbers are done a digit at a time; note the awkward method for determining the internal base of our big numbers:

(define integer->big
  (let ((base (string->number (big->string '(2 0 1)))))
    (lambda (n)
      (if (zero? n) '(0)
        (if (negative? n) (big-negate (integer->big (- n)))
          (let loop ((n n) (bs '()))
            (if (zero? n) (cons (length bs) (reverse bs))
              (loop (quotient n base) (cons (modulo n base) bs)))))))))

(define big->integer
  (let ((base (string->number (big->string '(2 0 1)))))
    (lambda (b)
      (if (big-zero? b) 0
        (if (big-negative? b) (* -1 (big->integer (big-negate b)))
          (let loop ((bs (reverse (cdr b))) (n 0))
            (if (null? bs) n
              (loop (cdr bs) (+ (car bs) (* n base))))))))))

The complete big number library is shown on the next page, and at http://programmingpraxis.codepad.org/hgy1QKi2.

Advertisement

Pages: 1 2 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: