Exercise 1.23 of SICP
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?
12 Primes from 1.22:
- 10000000019 *** .14100003242492676
- 10000000033 *** .1399998664855957
- 10000000061 *** .14000010490417483
- 100000000003 *** .5310001373291016
- 100000000019 *** .5320000648498535
- 100000000057 *** .54699993133544923
- 1000000000039 *** 1.7660000324249268
- 1000000000061 *** 1.75
- 1000000000063 *** 1.76500010490417483
- 10000000000037 *** 5.672000169754028
- 10000000000051 *** 5.672000169754028
- 10000000000099 *** 5.65599989891052253
Using time-prime-test with the new next function
- 10000000019 *** .07799983024597168
- 10000000033 *** .07800006866455078
- 10000000061 *** .07800006866455078
- 100000000003 *** .2969999313354492
- 100000000019 *** .29600000381469727
- 100000000057 *** .2969999313354492
- 1000000000039 *** .9849998950958252
- 1000000000061 *** .9840002059936523
- 1000000000063 *** .9839999675750732
- 10000000000037 *** 3.171999931335449
- 10000000000051 *** 3.1570000648498535
- 10000000000099 *** 3.171999931335449
| Using next | Exercise 1.22 | Factor of speedup |
| 0.07799983 | 0.141000032 | 1.807696658 |
| 0.078000069 | 0.139999866 | 1.794868503 |
| 0.078000069 | 0.140000105 | 1.79487156 |
| 0.296999931 | 0.531000137 | 1.787879664 |
| 0.296000004 | 0.532000065 | 1.797297493 |
| 0.296999931 | 0.546999931 | 1.841751036 |
| 0.984999895 | 1.766000032 | 1.792893625 |
| 0.984000206 | 1.75 | 1.778454912 |
| 0.983999968 | 1.765000105 | 1.793699353 |
| 3.171999931 | 5.67200017 | 1.788146372 |
| 3.157000065 | 5.67200017 | 1.796642399 |
| 3.171999931 | 5.655999899 | 1.78310215 |
Judging by the speedup it does look like there is about a 2 fold increase in speed of determining if a number is prime.
(define (runtime) (time->seconds (current-time))) (define (square x) (* x x)) (define (smallest-divisor n) (find-divisor n 2)) (define (next n) (if (= n 2) 3 (+ n 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) (newline) (display n) (start-prime-test n (runtime))) (define (start-prime-test n start-time) (if (prime? n) (report-prime (- (runtime) start-time)))) (define (report-prime elapsed-time) (display " *** ") (display elapsed-time))