计算机程序的构造和解,计算机程序的构造与解释(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的主要内容,如果未能解决你的问题,请参考以下文章