Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: ohthatjames/sicp-study
base: 4a51aa375c
...
head fork: ohthatjames/sicp-study
compare: 46a239d9cf
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
View
80 chapter1/ex1_22.scm
@@ -0,0 +1,80 @@
+; Exercise 1.22. Most Lisp implementations include a primitive called runtime that returns an integer that specifies the amount of
+; time the system has been running (measured, for example, in microseconds). The following timed-prime-test procedure, when called
+; with an integer n, prints n and checks to see if n is prime. If n is prime, the procedure prints three asterisks followed by the
+; amount of time used in performing the test.
+
+(define (smallest-divisor n)
+ (find-divisor n 2))
+(define (find-divisor n test-divisor)
+ (cond ((> (square test-divisor) n) n)
+ ((divides? test-divisor n) test-divisor)
+ (else (find-divisor n (+ test-divisor 1)))))
+(define (divides? a b)
+ (= (remainder b a) 0))
+
+(define (prime? n)
+ (= n (smallest-divisor n)))
+
+(define (timed-prime-test n)
+ (start-prime-test n (runtime)))
+(define (start-prime-test n start-time)
+ (if (prime? n)
+ (report-prime n (- (runtime) start-time))
+ #f))
+(define (report-prime n elapsed-time)
+ (newline)
+ (display n)
+ (display " *** ")
+ (display elapsed-time)
+ elapsed-time)
+
+; Using this procedure, write a procedure search-for-primes that checks the primality of consecutive odd integers in a specified
+; range. Use your procedure to find the three smallest primes larger than 1000; larger than 10,000; larger than 100,000; larger
+; than 1,000,000. Note the time needed to test each prime. Since the testing algorithm has order of growth of (n), you should
+; expect that testing for primes around 10,000 should take about 10 times as long as testing for primes around 1000. Do your timing
+; data bear this out? How well do the data for 100,000 and 1,000,000 support the n prediction? Is your result compatible with the
+; notion that programs on your machine run in time proportional to the number of steps required for the computation?
+
+(define (search-for-primes n)
+ (search-for-primes-iter (if (even? n) (+ n 1) n) 3))
+
+(define (search-for-primes-iter n count)
+ (if (zero? count)
+ 0
+ (if (timed-prime-test n)
+ (search-for-primes-iter (+ n 2) (- count 1))
+ (search-for-primes-iter (+ n 2) count))))
+
+; Testing for 1,000,000 returns:
+; 1000003 *** 0.
+; 1000033 *** 0.
+; 1000037 *** 0.
+
+; Using larger numbers:
+
+; 100000007 *** .01999999999999999
+; 100000037 *** 3.0000000000000027e-2
+; 100000039 *** 1.9999999999999962e-2
+
+; 1000000007 *** .07
+; 1000000009 *** .08000000000000002
+; 1000000021 *** .08000000000000007
+
+; 10000000019 *** .25
+; 10000000033 *** .25
+; 10000000061 *** .25
+
+; 100000000003 *** .79
+; 100000000019 *** .7899999999999996
+; 100000000057 *** .7799999999999998
+
+; 1000000000039 *** 2.4699999999999998
+; 1000000000061 *** 2.46
+; 1000000000063 *** 2.4800000000000004
+
+; 10000000000037 *** 7.859999999999998
+; 10000000000051 *** 7.920000000000002
+; 10000000000099 *** 7.949999999999996
+
+; Each jump appears to be about sqrt(10)
+
View
71 chapter1/ex1_23.scm
@@ -0,0 +1,71 @@
+; Exercise 1.23. The smallest-divisor procedure shown at the start of this section does lots of needless testing: After it checks
+; to see if the number is divisible by 2 there is no point in checking to see if it is divisible by any larger even numbers. This
+; suggests that the values used for test-divisor should not be 2, 3, 4, 5, 6, ..., but rather 2, 3, 5, 7, 9, .... To implement this
+; change, define a procedure next that returns 3 if its input is equal to 2 and otherwise returns its input plus 2. Modify the
+; smallest-divisor procedure to use (next test-divisor) instead of (+ test-divisor 1). With timed-prime-test incorporating this
+; modified version of smallest-divisor, run the test for each of the 12 primes found in exercise 1.22. Since this modification halves
+; the number of test steps, you should expect it to run about twice as fast. Is this expectation confirmed? If not, what is the
+; observed ratio of the speeds of the two algorithms, and how do you explain the fact that it is different from 2?
+
+(define (smallest-divisor n)
+ (find-divisor n 2))
+(define (next test-divisor)
+ (if (= test-divisor 2) 3 (+ test-divisor 2)))
+(define (find-divisor n test-divisor)
+ (cond ((> (square test-divisor) n) n)
+ ((divides? test-divisor n) test-divisor)
+ (else (find-divisor n (next test-divisor)))))
+(define (divides? a b)
+ (= (remainder b a) 0))
+
+(define (prime? n)
+ (= n (smallest-divisor n)))
+
+(define (timed-prime-test n)
+ (start-prime-test n (runtime)))
+(define (start-prime-test n start-time)
+ (if (prime? n)
+ (report-prime n (- (runtime) start-time))
+ #f))
+(define (report-prime n elapsed-time)
+ (newline)
+ (display n)
+ (display " *** ")
+ (display elapsed-time)
+ elapsed-time)
+
+(define (search-for-primes n)
+ (search-for-primes-iter (if (even? n) (+ n 1) n) 3))
+
+(define (search-for-primes-iter n count)
+ (if (zero? count)
+ 0
+ (if (timed-prime-test n)
+ (search-for-primes-iter (+ n 2) (- count 1))
+ (search-for-primes-iter (+ n 2) count))))
+
+; 100000007 *** 2.0000000000000018e-2
+; 100000037 *** 2.0000000000000018e-2
+; 100000039 *** 1.0000000000000009e-2
+
+; 1000000007 *** 4.0000000000000036e-2
+; 1000000009 *** 4.0000000000000036e-2
+; 1000000021 *** 5.0000000000000044e-2
+
+; 10000000019 *** .1499999999999999
+; 10000000033 *** .16000000000000014
+; 10000000061 *** .1499999999999999
+
+; 100000000003 *** .4900000000000002
+; 100000000019 *** .48
+; 100000000057 *** .48
+
+; 1000000000039 *** 1.54
+; 1000000000061 *** 1.5699999999999994
+; 1000000000063 *** 1.5699999999999994
+
+; 10000000000037 *** 4.85
+; 10000000000051 *** 4.85
+; 10000000000099 *** 4.850000000000001
+
+; It is faster, but not twice as fast. Maybe because even though we've halved the number of operations that need to be performed in that function, we haven't halved everything? Or the if statement is slower than just incrementing by 1?
View
68 chapter1/ex1_24.scm
@@ -0,0 +1,68 @@
+; Exercise 1.24. Modify the timed-prime-test procedure of exercise 1.22 to use fast-prime? (the Fermat method), and test each of the 12 primes you found in that exercise. Since the Fermat test has (log n) growth, how would you expect the time to test primes near 1,000,000 to compare with the time needed to test primes near 1000? Do your data bear this out? Can you explain any discrepancy you find?
+
+(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))))
+
+(define (fermat-test n)
+ (define (try-it a)
+ (= (expmod a n n) a))
+ (try-it (+ 1 (random (- n 1)))))
+
+(define (fast-prime? n times)
+ (cond ((= times 0) true)
+ ((fermat-test n) (fast-prime? n (- times 1)))
+ (else false)))
+
+(define (timed-prime-test n)
+ (start-prime-test n (runtime)))
+(define (start-prime-test n start-time)
+ (if (fast-prime? n 1000)
+ (report-prime n (- (runtime) start-time))
+ #f))
+(define (report-prime n elapsed-time)
+ (newline)
+ (display n)
+ (display " *** ")
+ (display elapsed-time)
+ elapsed-time)
+
+(define (search-for-primes n)
+ (search-for-primes-iter (if (even? n) (+ n 1) n) 3))
+
+(define (search-for-primes-iter n count)
+ (if (zero? count)
+ 0
+ (if (timed-prime-test n)
+ (search-for-primes-iter (+ n 2) (- count 1))
+ (search-for-primes-iter (+ n 2) count))))
+
+
+(search-for-primes 1000000000)
+(search-for-primes 10000000000)
+(search-for-primes 100000000000)
+(search-for-primes 1000000000000)
+(search-for-primes 10000000000000)
+
+; 1000000007 *** 2.0000000000000018e-2
+; 1000000009 *** 9.999999999999981e-3
+; 1000000021 *** 1.0000000000000009e-2
+; 10000000019 *** 9.999999999999981e-3
+; 10000000033 *** 2.0000000000000018e-2
+; 10000000061 *** .01999999999999999
+; 100000000003 *** 1.0000000000000009e-2
+; 100000000019 *** 1.0000000000000009e-2
+; 100000000057 *** 1.0000000000000009e-2
+; 1000000000039 *** 2.0000000000000018e-2
+; 1000000000061 *** 1.9999999999999962e-2
+; 1000000000063 *** 2.0000000000000018e-2
+; 10000000000037 *** 2.0000000000000018e-2
+; 10000000000051 *** 1.9999999999999962e-2
+; 10000000000099 *** 2.0000000000000018e-2
+
+; Seems to be much faster than log(10) growth... No idea why....
View
18 chapter1/ex1_26.scm
@@ -0,0 +1,18 @@
+; Exercise 1.26. Louis Reasoner is having great difficulty doing exercise 1.24. His fast-prime? test seems to run more slowly than
+; his prime? test. Louis calls his friend Eva Lu Ator over to help. When they examine Louis's code, they find that he has rewritten
+; the expmod procedure to use an explicit multiplication, rather than calling square:
+
+(define (expmod base exp m)
+ (cond ((= exp 0) 1)
+ ((even? exp)
+ (remainder (* (expmod base (/ exp 2) m)
+ (expmod base (/ exp 2) m))
+ m))
+ (else
+ (remainder (* base (expmod base (- exp 1) m))
+ m))))
+
+; ``I don't see what difference that could make,'' says Louis. ``I do.'' says Eva. ``By writing the procedure like that, you have
+; transformed the O(log n) process into a O(n) process.'' Explain.
+
+Because you have to calculate the expmod value twice, so any benefit of the expmod function that successive squares is lost.

No commit comments for this range

Something went wrong with that request. Please try again.