Approximate overview
Do we have to do HW7 over fall break?
No, you can do it the week you return. Most of you seem to do work the week it is due, not the weekend before.
Is it okay if I turn in HW6 late?
Yes.
What is your estimated time for HW7?
Three-to-four hours. No implementation!
Why is it so hard to predict how long your assignment will take? (Both for Sam and for Students.)
The wonder of computing. If you get it right, or mostly right, it’s quick. If you have a bug, it may take a nearly infinite amount of time to find that bug.
Get help! (SHAW has a 24 hour help line.) (SAM has an occasional help line.)
It’s hard to tell how many bugs you have.
Sam blathers for awhile.
It’s about proofs. Of correctness of algorithms. And developing those algorithms.
It should be straightforward.
Ask Sam for help when you can’t get the last part of the last problem.
Your goal: Rewrite expmod iteratively. In case you’ve forgetten, we
want to compute x^n mod m, with appropriate restrictions.
An example that may or may not help. Let’s compute 5^23.
Let’s make it simpler by adding some mod
Let’s compute 5^23 mod 7
Working with someone(s) near you, write this algorithm iteratively (and prove it correct)!
long
expmod (long x, long n, long m)
{
// x > 0, n >= 0, m > 1
long y = x % m;
// x > 0, n >= 0, y = x mod m
long p = n
// x > 0, n >= 0, y = x mod m, p = n
long t = 1;
// x > 0, n >= 0, y = x mod m, p = n, t = 1
// Our numbers have been cleverly chosen to ensure that the
// invariant holds when we start.
// Invariant: x^n mod m = t * y^p mod m.
while (p > 0) {
// Invariant: x^n mod m = t * y^p mod m
// p > 0
if (p % 2 == 0) {
y = (y * y) % m;
p = p / 2;
// ynew = (yold * yold) % m
// pnew = (pold) / 2
// x^n mod m = t * yold^pold mod m
// x^n mod m = t * (yold*yold)^(pold/2) mod m by the law of even exponents
// x^n mod m = t * (yold*yold mod m)^(pold/2) mod m by modulo rule 13a
// x^n mod m = t * ynew^pnew mod m by substitution
// Invariant: x^n mod m = t * y^p mod m
}
else {
t = (t * y) % m;
p = p - 1;
// x^n mod m = t * yold^pold mod m
// x^n mod m = t * (yold * yold^(pold-1)) mod m // By the def'n of exponents
// x^n mod m = (t * yold) * yold^(pold-1) mod m // By the transitive property
// x^n mod m = (t * yold mod m) * yold^(pold-1) mod m // modulo rule 13a
// x^n mod m = tnew * yold^pnew mod m // See above
// Invariant: x^n mod m = t * y^p mod m
}
// Invariant: x^n mod m = t * y^p mod m (because it holds for both ifs)
} // while
// Invariant: x^n mod m = t * y^p mod m
// p == 0
// x^n mod m = (t * 1) mod m and t < m
return t;
} // expmod
Why does this terminate?
Why is this O(logn)
Suppose we didn’t trust our proof (e.g., because proofs can be wrong as often as code is, or because, well, things sometimes behave differently in the world of C), what kind of tests might we write?
long result = 1; for (i = 1; i <= n; i++) { result = (result * x) % m; }{ result = (result* x) % m; test(expmod(x, i, m), result; }LONG_MAX, for each
exponent from 0 to sqrt(LONG_MAX), compare to the inefficient result.
More broadly, an invariant is a characteristic of something, a characteristic that you intend to maintain.
Can you think of other characteristics we’ve tried to maintain? (TPS)
Invariants get used in, say, the design of heaps, bsts, balanced bsts, ….
Even if we only do it informally, identifying characteristics to maintain is likely to be useful.