计算机程序的构造和解,计算机程序的构造与解释(SICP)习题02

Posted weixin_39715187

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机程序的构造和解,计算机程序的构造与解释(SICP)习题02相关的知识,希望对你有一定的参考价值。

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 nis prime, the procedure prints three

asterisks followed by the amount of time used in performing the

test.

(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))

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?

先把上述代码用common lisp改写

cl并没有runtime函数,我也没有找到返回微秒的函数,只能用get-internal-real-time,它返回的单位和具体实现有关,我用ccl返回是毫秒。

search-for-primes 函数

测试结果,由于计时精度是毫秒,只好用比题目中大的数来测试

从结果上看,用的时间大约是√10倍

Exercise

1.23.Thesmallest-divisorprocedure

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

fortest-divisorshould

not be 2, 3, 4, 5, 6,...,

but rather 2, 3, 5, 7, 9,....

To implement this change, define a

procedurenextthat

returns 3 if its input is equal to 2 and otherwise returns its

input plus 2. Modify

thesmallest-divisorprocedure

to use(next

test-divisor)instead

of(+ test-divisor

1).

Withtimed-prime-testincorporating

this modified version ofsmallest-divisor,

run the test for each of the 12 primes found in

exercise1.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?

增加 next 和修改 find-divisor

测试结果

之前测试结果

从测试结果上看,比值并不等于2,会比2小一点。应该是因为并不是完全减少了一半的步骤,而是又添加了 if

判断(next方法里),增加了一点点新的步骤。

Exercise 1.24.Modify

thetimed-prime-testprocedure

of exercise1.22to

usefast-prime?(the

Fermat method), and test each of the 12 primes you found in that

exercise. Since the Fermat test

has

(logn)

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?

预期应该是(square

n) 是 n 的两倍,看测试结果

随着n的增加,并不是简单的log

n增长,看上面代码结果,大约后一项分别为前一项的3,4,5倍。

从1.22 到

1.24,算法的增长度只是大概的预测整体走势,在不影响大趋势的走向时,会有些其他小的因素影响算法的结果,造成与预测结果的些许偏差。这里1.24由于n越来越大,可能是计算机计算大数比小数更麻烦,所以结果并没有呈现2倍增长。

算法复杂度具有指导意义,但并不是百分百精确。

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

(define (expmod base exp m)

(remainder (fast-expt base exp) m))

Is she correct? Would this procedure serve as well for our fast

prime tester? Explain.

不正确,原来的expmod方法,会把每步计算的数控制在m以内,而fast-expt会直接算平方,对于大数的计算,还是相当费时的。

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 (log n) process

into a

(n) process.''

Explain.

从线性递归变成了树形递归,原来的expmod方法把问题规模每回都减小一半(偶数情况),而现在的expmod

树形递归,两次调用分别为一半的过程,就变成n了。

Exercise 1.27.Demonstrate

that the Carmichael numbers listed in

footnote47really

do fool the Fermat test. That is, write a procedure that takes an

integernand

tests whetheranis

congruent toamodulonfor

everya<n,

and try your procedure on the given Carmichael

numbers.

测试结果:

Exercise 1.28.One

variant of the Fermat test that cannot be fooled is called

theMiller-Rabin

test(Miller

1976; Rabin 1980). This starts

froman

alternate form of Fermat's Little Theorem, which states that

ifnis

a prime number andais

any positive integer less thann,

thenaraised

to the (n-

1)st power is congruent to 1

modulon.

To test the primality of a

numbernby

the Miller-Rabin test, we pick a random

numbera<nand

raiseato

the (n-

1)st power modulonusing

theexpmodprocedure.

However, whenever we perform the squaring step

inexpmod, we check

to see if we have discovered a ``nontrivial square root of 1

modulon,'' that is,

a number not equal to 1

orn-

1 whose square is equal to 1

modulon. It is

possible to prove that if such a nontrivial square root of 1

exists, thennis

not prime. It is also possible to prove that

ifnis

an odd number that is not prime, then, for at least half the

numbersa<n,

computingan-1in

this way will reveal a nontrivial square root of 1

modulon. (This is

why the Miller-Rabin test cannot be fooled.) Modify

theexpmodprocedure

to signal if it discovers a nontrivial square root of 1, and use

this to implement the Miller-Rabin test with a procedure analogous

tofermat-test. Check your

procedure by testing various known primes and non-primes. Hint: One

convenient way to

makeexpmodsignal

is to have it return 0.

(defun miller-rabin-test (n)

(labels ((try-it (a)

(= (miller-rabin-expmod a (1- n) n) 1)))

(try-it (1+

(random (1- n))))))

(defun miller-rabin-expmod (base exp m)

(cond ((= exp 0) 1)

((evenp

exp)

(check-nontrivial-sqrt1

(miller-rabin-expmod base (/ exp 2) m) m))

(t (rem (*

base (miller-rabin-expmod base (- exp 1) m)) m))))

(defun check-nontrivial-sqrt1 (x m)

(let ((remainder (rem (square x) m)))

(if (and (/=

x 1) (/= x (1- m)) (= remainder 1))

0

remainder)))

题中说,如果n是非素数的奇数,至少有一半的数a

测试结果,对于那些能够欺骗费马检查的数,有大部分将会遇到1取模n的非平凡平方根。

(defun full-miller-rabin-counter

(n)

(let ((counter 0))

(dotimes (i n counter)

(if (= (miller-rabin-expmod i (1- n) n) 1)

(incf counter)))))

FULL-MILLER-RABIN-COUNTER

(mapcar #'full-miller-rabin-counter '(561 1105 1729 2465

6601))

(10 30 162 70 330)

而对于其它非素数的奇数,概率更高

CL-USER>

(full-miller-rabin-counter 10001)

22

CL-USER> (full-miller-rabin-counter 10002)

1

CL-USER> (full-miller-rabin-counter 10003)

18

CL-USER> (full-miller-rabin-counter 10005)

2

CL-USER> (full-miller-rabin-counter 10007)

10006

当然10007是素数

以上是关于计算机程序的构造和解,计算机程序的构造与解释(SICP)习题02的主要内容,如果未能解决你的问题,请参考以下文章

如何学习计算机程序的构造和解释

读书笔记《计算机程序的构造和解释》一

读书笔记《计算机程序的构造和解释》一

《计算机程序的构造和解释(第2版)》PDF下载

计算机程序的构造和解释pdf

计算机程序的构造和解释原书第2版PDF