3

Section 1.2.6 of SICP gives the following procedure:

    (define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder (square (expmod base (/ exp 2) m))
                    m))
        (else
         (remainder (* base (expmod base (- exp 1) m))
                    m))))

The authors claim that it "computes the exponential of a number modulo another number". For example (expmod 5 3 n) should return (5^3) mod n.

However, from a mathematical point of view, I just can't see how it works. As reinforced by footnote 46, it is intended to use the property that for any positive integers a, b, and n, (ab) mod n = [(a mod n)(b mod n)] mod n, but I fail to see how it is actually using it. Consider (expmod 5 3 3):

  1. First, we call (expmod 5 3 3). Mathematically, this means that we're asking for (5^3) mod 3.
  2. As the second parameter is odd, we compute (remainder (* 5 (expmod 5 (- 3 1) 3)) 3) i.e. (remainder (* 5 (expmod 5 2 3)) 3). Mathematically, this is [5 * [(5^2) mod 3]] mod 3. As the initial 5 in this expression does not have a mod 3 attached, this expression is not in the (ab) mod n = [(a mod n)(b mod n)] mod n form, so it fails to use the intended property.

So, given that this it appears to not be using the intended property, why does this algorithm work? What property of modular arithmetic have I overlooked?

J. Mini
  • 1,868
  • 1
  • 9
  • 38
  • Regarding point 2, note that (a * (b mod n)) mod n is *also* equal to (a * b) mod n. – molbdnilo Nov 04 '20 at 08:31
  • @molbdnilo Strange, if something that nice was true, I would have expected it to be front and centre in [Wikipedia's list of properties](https://en.wikipedia.org/wiki/Modulo_operation#Properties_(identities)). Have I missed it? – J. Mini Nov 04 '20 at 17:06
  • It follows trivially from the addition rule. (Most of the things worth knowing are not mentioned in Wikipedia at all.) – molbdnilo Nov 04 '20 at 18:57
  • @molbdnilo From the addition rule? If it's trivial, I can't see it so easily. – J. Mini Nov 09 '20 at 14:45

2 Answers2

2

This is the definition of fast-exp from 1.2.4 that is refered to:

(define (fast-expt b n)
  (cond ((= n 0) 1)
        ((even? n) (square (fast-expt b (/ n 2))))
        (else (* b (fast-expt b (- n 1))))))

If we rename things to closer match expmod it looks like this:

(define (expt base exp)
  (cond ((= exp 0) 1)
        ((even? exp)
         (square (expt base (/ exp 2))))
        (else
         (* base (expt base (- exp 1))))))

To get a naive expmod we can, for now, just calculate the remainder of each clause:

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder (square (expt base (/ exp 2))) m))
        (else
         (remainder (* base (expt base (- exp 1))) m))

So far we have not used the footnote (ab) mod m = ((a mod m)(b mod m) mod m). Of course a special case of this is (aa) mod m = ((a mod m)(a mod m) mod m) which gives (remainder (square a) m) = (remainder (sqaure (remainder a m)) m). We can use this with the even clause so that

         (remainder (square (expt base (/ exp 2))) m)

becomes:

         (remainder (square (remainder (expt base (/ exp 2)) m))
                    m)

In the middle of this we have the remainder of an exponent so this is equivalent to:

         (remainder (square (expmod base (/ exp 2) m)) m)

Using the new even clause we have

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder (square (expmod base (/ exp 2) m)) 
                    m))
        (else
         (remainder (* base (expt base (- exp 1))) m))

To simplify the odd clause lets use E in place of (expt base (- exp 1)) for now.

By using the defining properties of mod we can say for any number a:

         a = (+ (* (quotient a m) m) (remainder a m))

So it's also true that:

         E = (+ (* (quotient E m) m) (remainder E m))

substituting this into our odd clause:

         (remainder (* base E) m)

gives:

         (remainder (* base (+ (* (quotient E m) m) (remainder E m))) m)

We can ignore (* (quotient E m) m) because any term containg this is divisible by m and so will evaluate to 0 when doing the outer remainder, so this equivalent to:

         (remainder (* base (remainder E m)) m)

expanding E to it's orignal value:

         (remainder (* base (remainder (expt base (- exp 1)) m)) m)

once again, in the middle, we have the remainder of an exponent so this becomes:

         (remainder (* base (expmod base (- exp 1) m)) m)

And our expmod is now:

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder (square (expmod base (/ exp 2) m)) 
                    m))
        (else
         (remainder (* base (expmod base (- exp 1) m))
                    m))))
codybartfast
  • 7,323
  • 3
  • 21
  • 25
  • So, ultimately, the odd branch does not use the property in question? – J. Mini Nov 04 '20 at 17:01
  • `(remainder (* base (remainder E m)) m)` appears to be of the form [a*(b mod m)] mod m. As this was derived from `(remainder (* base E) m)`, am I correct to conclude that you've given a proof of the same property that Sorawee Porncharoenwase has? i.e. (ab) mod n = [a (b mod n)] mod n? – J. Mini Nov 04 '20 at 18:31
  • @J.Mini That's right I didn't use it in the odd clause. There might be a way to use it when eliminating `(* (quotient E m) m)` but I'm not smart enough to see it, and in any case it seems a little contrived. I didn't look at Sorawee's answer closely, was busy writing my own :-), perhaps I'd say this answer builds on his? – codybartfast Nov 05 '20 at 11:59
1
(ab) mod n = [a (b mod n)] mod n

is also true.

Here's a proof by induction on a.

Base case: when a = 0, (0b) mod n = 0 mod n = [0 (b mod n)] mod n.

Inductive case:

By induction hypothesis, assume that (ab) mod n = [a (b mod n)] mod n is true. We need to prove that ((a+1) b) mod n = [(a + 1) (b mod n)] mod n.

((a+1) b) mod n
= (ab + b) mod n
= (ab mod n) + (b mod n)
= [a (b mod n)] mod n + (b mod n)             by induction hypothesis
= [a (b mod n)] mod n + (b mod n) mod n
= [a (b mod n) + (b mod n)] mod n
= [(a + 1) (b mod n)] mod n

as desired.

This concludes the proof that

(ab) mod n = [a (b mod n)] mod n

In fact, you can see that

(ab) mod n = [(a mod n) (b mod n)] mod n

is a result that follows from it. Here's a proof:

(ab) mod n 
= [a (b mod n)] mod n            by what we just proved
= [(b mod n) a] mod n
= [(b mod n) (a mod n)] mod n    by what we just proved
= [(a mod n) (b mod n)] mod n
Sorawee Porncharoenwase
  • 6,305
  • 1
  • 14
  • 28
  • I'm surprised that this wasn't [on Wikipedia](https://en.wikipedia.org/wiki/Modulo_operation#Properties_(identities)). The version that you've proven is stronger than the one that they give. Why would they give a specific case when there is a more powerful version that requires no additional concepts? Moreover, I'm surprised that it wasn't in SICP's footnote. It makes me wonder if I misread it. Did I? – J. Mini Nov 04 '20 at 17:03
  • Well, Wikipedia doesn't contain everything, Also, there's nothing wrong with `[(a mod n) (b mod n)] mod n`. One could argue that it's SICP's mistake that they didn't follow the equation accurately, and the fix is to use `(* (remainder base m) ...)` instead of `(* base ...)`. In fact, from performance's point of view, it's better to use `(* (remainder base m) ...)`. See [my PR](https://github.com/racket/math/pull/52) to improve the performance of `modular-expt` in Racket, for example. – Sorawee Porncharoenwase Nov 05 '20 at 00:40