SICP Exercise 1.25: A simpler expmod?
2021-09-30
Categories: Programming
Exercise 1.25: Alyssa P. Hacker complains that we went to a lot of extra work in writing
expmod
. After all, she says, since we already know how to compute exponentials, we could have simply written:1(define (expmod base exp m) 2 (remainder (fast-expt base exp) m))Is she correct? Would this procedure serve as well for our fast prime tester? Explain.
First, look at the original algorithm:
1 (define (expmod base exp m) 2 (cond ((= exp 0) 1) 3 ((even? exp) 4 (remainder 5 (square (expmod base (/ exp 2) m)) 6 m)) 7 (else 8 (remainder 9 (* base (expmod base (- exp 1) m)) 10 m))))
Trying to run both:
1$ racket 1.25-expmod.scm 2>(expmod 2 10 10) 3> (expmod 2 5 10) 4> >(expmod 2 4 10) 5> > (expmod 2 2 10) 6> > >(expmod 2 1 10) 7> > > (expmod 2 0 10) 8< < < 1 9< < <2 10< < 4 11< <6 12< 2 13<4 144
1$ racket 1.25-expmod-fast-expt.scm 2>(expmod 2 10 10) 3<4 44
Looks like she is correct.
But there is a problem: instead of breaking down the problem into smaller numbers, she tries to fully compute expt
before computing remainder
.
Let’s see what happens with a large number:
1$ gtime -v racket 1.25-expmod.scm 2>(expmod 2 100000000 100000000) 3> (expmod 2 50000000 100000000) 4> >(expmod 2 25000000 100000000) 5> > (expmod 2 12500000 100000000) 6> > >(expmod 2 6250000 100000000) 7> > > (expmod 2 3125000 100000000) 8> > > >(expmod 2 1562500 100000000) 9> > > > (expmod 2 781250 100000000) 10> > > > >(expmod 2 390625 100000000) 11> > > > > (expmod 2 390624 100000000) 12> > > >[10] (expmod 2 195312 100000000) 13> > > >[11] (expmod 2 97656 100000000) 14> > > >[12] (expmod 2 48828 100000000) 15> > > >[13] (expmod 2 24414 100000000) 16> > > >[14] (expmod 2 12207 100000000) 17> > > >[15] (expmod 2 12206 100000000) 18> > > >[16] (expmod 2 6103 100000000) 19> > > >[17] (expmod 2 6102 100000000) 20> > > >[18] (expmod 2 3051 100000000) 21> > > >[19] (expmod 2 3050 100000000) 22> > > >[20] (expmod 2 1525 100000000) 23> > > >[21] (expmod 2 1524 100000000) 24> > > >[22] (expmod 2 762 100000000) 25> > > >[23] (expmod 2 381 100000000) 26> > > >[24] (expmod 2 380 100000000) 27> > > >[25] (expmod 2 190 100000000) 28> > > >[26] (expmod 2 95 100000000) 29> > > >[27] (expmod 2 94 100000000) 30> > > >[28] (expmod 2 47 100000000) 31> > > >[29] (expmod 2 46 100000000) 32> > > >[30] (expmod 2 23 100000000) 33> > > >[31] (expmod 2 22 100000000) 34> > > >[32] (expmod 2 11 100000000) 35> > > >[33] (expmod 2 10 100000000) 36> > > >[34] (expmod 2 5 100000000) 37> > > >[35] (expmod 2 4 100000000) 38> > > >[36] (expmod 2 2 100000000) 39> > > >[37] (expmod 2 1 100000000) 40> > > >[38] (expmod 2 0 100000000) 41< < < <[38] 1 42< < < <[37] 2 43< < < <[36] 4 44< < < <[35] 16 45< < < <[34] 32 46< < < <[33] 1024 47< < < <[32] 2048 48< < < <[31] 4194304 49< < < <[30] 8388608 50< < < <[29] 44177664 51< < < <[28] 88355328 52< < < <[27] 85987584 53< < < <[26] 71975168 54< < < <[25] 8628224 55< < < <[24] 49394176 56< < < <[23] 98788352 57< < < <[22] 90875904 58< < < <[21] 27817216 59< < < <[20] 55634432 60< < < <[19] 23962624 61< < < <[18] 47925248 62< < < <[17] 95861504 63< < < <[16] 91723008 64< < < <[15] 96568064 65< < < <[14] 93136128 66< < < <[13] 38832384 67< < < <[12] 47123456 68< < < <[11] 5383936 69< < < <[10] 66852096 70< < < < < 39593216 71< < < < <79186432 72< < < < 12890624 73< < < <87109376 74< < < 87109376 75< < <87109376 76< < 87109376 77< <87109376 78< 87109376 79<87109376 8087109376 81 Command being timed: "racket 1.25-expmod.scm" 82 User time (seconds): 0.26 83 System time (seconds): 0.07 84 Percent of CPU this job got: 95% 85 Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.35 86 Average shared text size (kbytes): 0 87 Average unshared data size (kbytes): 0 88 Average stack size (kbytes): 0 89 Average total size (kbytes): 0 90 Maximum resident set size (kbytes): 87988 91 Average resident set size (kbytes): 0 92 Major (requiring I/O) page faults: 0 93 Minor (reclaiming a frame) page faults: 27796 94 Voluntary context switches: 0 95 Involuntary context switches: 385 96 Swaps: 0 97 File system inputs: 0 98 File system outputs: 0 99 Socket messages sent: 0 100 Socket messages received: 0 101 Signals delivered: 0 102 Page size (bytes): 4096 103 Exit status: 0
1$ gtime -v racket 1.25-expmod-fast-expt.scm 2>(expmod 2 100000000 100000000) 3<87109376 487109376 5 Command being timed: "racket 1.25-expmod-fast-expt.scm" 6 User time (seconds): 0.37 7 System time (seconds): 0.12 8 Percent of CPU this job got: 96% 9 Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.51 10 Average shared text size (kbytes): 0 11 Average unshared data size (kbytes): 0 12 Average stack size (kbytes): 0 13 Average total size (kbytes): 0 14 Maximum resident set size (kbytes): 151672 15 Average resident set size (kbytes): 0 16 Major (requiring I/O) page faults: 0 17 Minor (reclaiming a frame) page faults: 56633 18 Voluntary context switches: 0 19 Involuntary context switches: 439 20 Swaps: 0 21 File system inputs: 0 22 File system outputs: 0 23 Socket messages sent: 0 24 Socket messages received: 0 25 Signals delivered: 0 26 Page size (bytes): 4096 27 Exit status: 0
So, her version:
- requires more memory
- slower
Trying with a larger number, it took… > 2 minutes and 10 GB to run:
1$ gtime -v racket 1.25-expmod-fast-expt.scm 2>(expmod 2 100000000000 100000000000) 3<81787109376 481787109376 5 Command being timed: "racket 1.25-expmod-fast-expt.scm" 6 User time (seconds): 122.83 7 System time (seconds): 99.90 8 Percent of CPU this job got: 86% 9 Elapsed (wall clock) time (h:mm:ss or m:ss): 4:17.10 10 Average shared text size (kbytes): 0 11 Average unshared data size (kbytes): 0 12 Average stack size (kbytes): 0 13 Average total size (kbytes): 0 14 Maximum resident set size (kbytes): 10491416 15 Average resident set size (kbytes): 0 16 Major (requiring I/O) page faults: 0 17 Minor (reclaiming a frame) page faults: 60640485 18 Voluntary context switches: 45 19 Involuntary context switches: 598751 20 Swaps: 0 21 File system inputs: 0 22 File system outputs: 0 23 Socket messages sent: 0 24 Socket messages received: 0 25 Signals delivered: 0 26 Page size (bytes): 4096 27 Exit status: 0
References:
Related Posts:
- SICP Exercise 2.77: expected a procedure that can be applied to arguments, given #f
- SICP Exercise 2.43: Eight queens: interchange the order of the nested mappings
- SICP Exercise 2.42: Eight queens puzzle
- SICP Exercise 2.41: Triple sum
- SICP Exercise 2.35: Counting leaves of a tree