sicp每日一题[1.1-1.29]

news2024/9/20 22:59:11

补一下之前的题目

Exercise 1.1

Below is a sequence of expressions. What is the result printed by the interpreter in response to each expression? Assume that the sequence is to be evaluated in
the order in which it is presented.
在这里插入图片描述

Exercise 1.2

Translate the following expression into prefix form: 5 + 4 + ( 2 − ( 3 − ( 6 + 4 5 ) ) ) 3 ( 6 − 2 ) ( 2 − 7 ) \frac{5+4+(2-(3-(6+\frac45)))}{3(6-2)(2-7)} 3(62)(27)5+4+(2(3(6+54))).

(/ (+ 5
      (+ 4
         (- 2
            (- 3
               (+ 6
                  (/ 4 5))))))
    (* 3
       (- 6 2)
       (- 2 7)))

在这里插入图片描述

Exercise 1.3

Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.

(define (smaller-of-two-numbers x y)
  (if (< x y) x y))

(define (sum-of-two-larger-numbers-of-three x y z)
  (- (+ x y z)
     (smaller-of-two-numbers (smaller-of-two-numbers x y) z)))

在这里插入图片描述

Exercise 1.4

Observe that our model of evaluation allows for combinations whose operators are compound expressions. Use this observation to describe the behavior of the following procedure:

(define (a-plus-abs-b a b)
  ((if (> b 0) + -) a b))

首先定义了一个名为 a-plus-abs-b 的函数,它接受2个参数;然后判断b的正负,如果b为正,则计算 a + b a+b a+b,如果b不为正,则计算 a − b a-b ab,也就实现了 a + ∣ b ∣ a+|b| a+b的效果。

Exercise 1.5

Ben Bitdiddle has invented a test to determine whether the interpreter he is faced with is using applicative order evaluation or normal-order evaluation.He defines the following two procedures:

(define (p) (p))
(define (test x y)
  (if (= x 0) 0 y))

Then he evaluates the expression (test 0 (p)).
What behavior will Ben observe with an interpreter that uses applicative-order evaluation? What behavior will he observe with an interpreter that uses normal-order evaluation? Explain your answer. (Assume that the evaluation rule for the special form if is the same whether the interpreter is using normal or applicative order: The predicate expression is evaluated first, and the result determines whether to evaluate the consequent or the alternative expression.)

applicative-order: 一直执行(test 0 (p))
normal-order:

(test 0 (p))
(if (= 0 0) 0 (p))
(if #t 0 (p))
0

Exercise 1.6

Alyssa P. Hacker doesn’t see why if needs to be provided as a special form. “Why can’t I just define it as an ordinary procedure in terms of cond?” she asks. Alyssa’s friend Eva Lu Ator claims this can indeed be done, and she defines a new version of if:

(define (new-if predicate then-clause else-clause)
(cond (predicate then-clause)
(else else-clause)))

Eva demonstrates the program for Alyssa:

(new-if (= 2 3) 0 5)
5
(new-if (= 1 1) 0 5)
0

Delighted, Alyssa uses new-if to rewrite the square-root program:

(define (sqrt-iter guess x)
(new-if (good-enough? guess x)
guess
(sqrt-iter (improve guess x) x)))

What happens when Alyssa aempts to use this to compute square roots? Explain.

The default if statement is a special form which means that even when an interpreter follows applicative substitution, it only evaluates one of its parameters- not both. However, the newly created new-if doesn’t have this property and hence, it never stops calling itself due to the third parameter passed to it in sqrt-iter.
Let’s see the difference between if and new-if:

; if
(if #t (display "good") (display "bad"))
good
; Unspecified return value
; new-if
(new-if #t (display "good") (display "bad"))
goodbad
; Unspecified return value

As we can see, both was executed regardless result of the predicate when we use new-if.

Exercise 1.7:

The good-enough? test used in computing square roots will not be very effective for finding the square roots of very small numbers. Also, in real computers, arithmetic operations are almost always performed with limited precision. This makes our test inadequate for very large numbers. Explain these statements, with examples showing how the test fails for small and large numbers. An alternative strategy for implementing good-enough? is to watch how guess changes from one iteration to the next and to stop when the change is a very small fraction of the guess. Design a square-root procedure that uses this kind of end test. Does this work beer for small and large numbers?

对于比较小的数,计算结果会不准确,因为设定的差值可能比所求数还要大;对于比较大的数,程序会一直运行,因为猜测值跟所求数的绝对差值会一直大于设定的差值。
按题目提示重新设计 g o o d − e n o u g h ? good-enough? goodenough?函数可以解决这个问题

(define epsilon (expt 2 -52))
(define tolerance (* (/ 9 4) epsilon))
(define (good-enough? guess)
  (or (= guess 0) (< (abs (- (improve guess) guess)) (* tolerance guess))))

Exercise 1.8:

Newton’s method for cube roots is based on the fact that if y is an approximation to the cube root of x, then a beer approximation is given by the value x y 2 + 2 y 3 \frac{\frac{x}{y^2}+2y}3 3y2x+2y.
Use this formula to implement a cube-root procedure analogous to the square-root procedure.(In Section 1.3.4 we will see how to implement Newton’s method in general as an abstraction of these square-root and cube-root procedures.)

这个题目很简单,只要把计算平方根的算法里的 i m p r o v e improve improve函数修改一下就行

(define (improve guess)
  (/ (+ (* 2 guess) (/ x (square guess))) 3))

Exercise 1.9

Each of the following two procedures defines a method for adding two positive integers in terms of the procedures inc, which increments its argument by 1, and dec, which decrements its argument by 1.

(define (+ a b)
(if (= a 0) b (inc (+ (dec a) b))))
(define (+ a b)
(if (= a 0) b (+ (dec a) (inc b))))

Using the substitution model, illustrate the process generated by each procedure in evaluating (+ 4 5). Are these processes iterative or recursive?

The process generated by the first procedure is recursive:

(+ 4 5) 
(inc (+ (dec 4) 5)) 
(inc (+ 3 5)) 
(inc (inc (+ (dec 3) 5))) 
(inc (inc (+ 2 5))) 
(inc (inc (inc (+ (dec 2) 5)))) 
(inc (inc (inc (+ 1 5)))) 
(inc (inc (inc (inc (+ (dec 1) 5))))) 
(inc (inc (inc (inc (+ 0 5))))) 
(inc (inc (inc (inc 5)))) 
(inc (inc (inc 6))) 
(inc (inc 7)) 
(inc 8)  
(9)

The process generated by the second procedure is iterative:

(+ 4 5) 
(+ (dec 4) (inc 5)) 
(+ 3 6) 
(+ (dec 3) (inc 6)) 
(+ 2 7) 
(+ (dec 2) (inc 7)) 
(+ 1 8) 
(+ (dec 1) (inc 8)) 
(+ 0 9) 
(9)

The easiest way to spot that the first process is recursive (without writing out the substitution) is to note that the “+” procedure calls itself at the end while nested in another expression; the second calls itself, but as the top expression.

Exercise 1.10

The following procedure computes a mathematical function called Ackermann’s function.

(define (A x y)
(cond ((= y 0) 0)
((= x 0) (* 2 y))
((= y 1) 2)
(else (A (- x 1) (A x (- y 1))))))

What are the values of the following expressions?

(A 1 10)
(A 2 4)
(A 3 3)

Consider the following procedures, where A is the procedure defined above:

(define (f n) (A 0 n))
(define (g n) (A 1 n))
(define (h n) (A 2 n))
(define (k n) (* 5 n n))

Giveconcisemathematicaldefinitionsforthefunctionscomputed by the procedures f, g, and h for positive integer values of n. For example, ( k n ) (k n) (kn) computes 5 n 2 5n^2 5n2.

  • (A 1 10)
(A 0 (A 1 9))
(A 0 (A 0 (A 1 8)))
(A 0 (A 0 (A 0 (A 1 7))))
(A 0 (A 0 (A 0 (A 0 (A 1 6)))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 1 5))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 4)))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 3))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 2)))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1))))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 2)))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 4))))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 (A 0 8)))))))
(A 0 (A 0 (A 0 (A 0 (A 0 (A 0 16))))))
(A 0 (A 0 (A 0 (A 0 (A 0 32)))))
(A 0 (A 0 (A 0 (A 0 64))))
(A 0 (A 0 (A 0 128)))
(A 0 (A 0 256))
(A 0 512)
(1024)
  • (A 2 4)
(A 1 (A 2 3))
(A 1 (A 1 (A 2 2)))
(A 1 (A 1 (A 1 (A 2 1))))
(A 1 (A 1 (A 1 2)))
(A 1 (A 1 (A 0 (A 1 1))))
(A 1 (A 1 (A 0 2)))
(A 1 (A 1 4))
; 根据上一问可知,(A 1 n)=2^n
(A 1 16)
(2^16)
  • (A 3 3)
(A 2 (A 3 2))
(A 2 (A 2 (A 3 1)))
(A 2 (A 2 2))
(A 2 (A 2 2))
; 根据上一问可知,(A 2 2)=4
(A 2 4)
(2^16)
  • (f n): 2 n 2n 2n
  • (g n): 0   f o r   n = 0 , 2 n   f o r   n > 0 0~ for~ n=0, 2^n~ for~ n>0 0 for n=0,2n for n>0
  • (h n): 0   f o r   n = 0 , 2   f o r   n = 1 , 2 2 2 2 . . . n t i m e s f o r   n > 1 0~ for~ n=0, 2~ for~ n=1, 2^{2^{2^{2^...n times}}} for~ n > 1 0 for n=0,2 for n=1,2222...ntimesfor n>1

Exercise 1.11

A function f is defined by the rule that
在这里插入图片描述
Write a procedure that computes f by means of a recursive process. Write a procedure that computes f by means of an iterative process.

  • Recursive
(define (f-recur n)
  (if (< n 3)
      n
      (+ (f-recur (- n 1))
         (* 2 (f-recur (- n 2)))
         (* 3 (f-recur (- n 3))))))
  • Iterative
(define (f-iter n)
  ; a相当于f(n-1), b相当于f(n-2), c相当于f(n-3), n相当于计数器
  ; 虽然题目给的公式是倒着算,但是实现的算法是正着算,依次把f(3), f(4)...算出来
  ; 当作为计数器的n恰好等于3时,a此时正好为f(n-1),所以需要再算一次才能得到f(n)
  (define (iter a b c n)
    (if (< n 3)
        a
        (iter (+ a
                 (* 2 b)
                 (* 3 c))
              a
              b
              (- n 1))))

  (iter 2 1 0 n))

Exercise 1.12

The following paern of numbers is called Pascal’s triangle.
在这里插入图片描述
The numbers at the edge of the triangle are all 1, and each number inside the triangle is the sum of the two numbers above it. Write a procedure that computes elements of Pascal’s triangle by means of a recursive process.

(define (pascal row col)
  (cond ((or (< row 1) (< col 1) (< row col)) 0)     ; 排除非法参数
        ((or (= col 1) (= row col)) 1)               ; 当该位置在两边时,值都为1
        (else (+ (pascal (- row 1) (- col 1))        ; 其他位置的数都等于上一行“肩膀”2数之和
                 (pascal (- row 1) col)))))

Exercise 1.13

Prove that F i b ( n ) Fib(n) Fib(n) is the closest integer to ϕ n 5 \frac{ϕ^n}{\sqrt5} 5 ϕn, where ϕ = 1 + 5 2 ϕ = \frac{1 + \sqrt5}{2} ϕ=21+5 . Hint: Let ψ = 1 − 5 2 \psi=\frac{1 - \sqrt5}{2} ψ=215 . Use induction and the definition of the Fibonacci numbers (see Section 1.2.2) to prove that F i b ( n ) = ϕ n − ψ n 5 Fib(n)=\frac{ϕ^n-\psi^n}{\sqrt5} Fib(n)=5 ϕnψn.

Proof:
在这里插入图片描述

Exercise 1.14

Draw the tree illustrating the process generated by the count-change procedure of Section 1.2.2 in making change for 11 cents. What are the orders of growth of the space and number of steps used by this process as the amount to be changed increases?
在这里插入图片描述
changing an amount a a a using n n n kinds of coins:
The space required by the process is the height of the tree: R ( a , n ) = θ ( a ) R(a, n) = \theta(a) R(a,n)=θ(a)
The number of steps required by the process: R ( a , n ) = θ ( a n ) R(a,n) = \theta (a^n) R(a,n)=θ(an),
a detailed explanation for this: https://sicp-solutions.net/post/sicp-solution-exercise-1-14/
Implement the solution in only n 2 n^2 n2 is here: https://github.com/sarabander/p2pu-sicp/blob/master/1.2/Ex1.14.scm

Exercise 1.15

The sine of an angle (specified in radians)can be computed by making use of the approximation sin ⁡ x ≈ x \sin x \approx x sinxx if x x x is sufficiently small, and the trigonometric identity
sin ⁡ x = 3 sin ⁡ x 3 − 4 sin ⁡ 3 x 3 \sin x = 3\sin\frac{x}{3}-4\sin^3\frac{x}{3} sinx=3sin3x4sin33x
to reduce the size of the argument of sin. (For purposes of this exercise an angle is considered “sufficiently small” if its magnitude is not greater than 0.1 radians.) These ideas are incorporated in the following procedures:

(define (cube x) (* x x x))
(define (p x) (- (* 3 x) (* 4 (cube x))))
(define (sine angle)
  (if (not (> (abs angle) 0.1))
    angle
    (p (sine (/ angle 3.0)))))
  • a. How many times is the procedure p p p applied when (sine 12.15) is evaluated?
  • b. What is the order of growth in space and number of steps (as a function of a a a)used by the process generated by the sine procedure when (sine a) is evaluated?

answer for a: 5
在这里插入图片描述

answer for b: The angle a a a is divided by 3 each time the procedure p p p is applied. Expressing this differently, we can say that p p p is applied once for each complete power of 3 contained within the angle a a a. Therefore, given a positive argument, let the number of times p p p is applied as t t t, then a ∗ 1 3 t < 0.1 a * \frac{1}{3}^t < 0.1 a31t<0.1. We can calculate that t t t is the ceiling of the base 3 logarithm of the argument a a a divided by 0.1, or ⌈ log ⁡ 3 a 0.1 ⌉ \lceil \log{3}^{\frac{a}{0.1}} \rceil log30.1a.
If we measure the required space and the number of steps by counting the invocations of p p p, the order of growth of the process generated by (sine a a a) is logarithmic. Exactly, the number of steps required are ⌈ log ⁡ 3 a 0.1 ⌉ \lceil \log{3}^{\frac{a}{0.1}} \rceil log30.1a. In other words we have O log ⁡ a O\log{a} Ologa order of growth.

Let’s check it, if we increase a a a to 3 times, we’ll find the number of executions of p p p will be increased by one.

  • sine 10, 5 times
    在这里插入图片描述

  • sine 30, 6 times
    在这里插入图片描述

  • sine 90, 7 times
    在这里插入图片描述

Exercise 1.16

Design a procedure that evolves an iterative exponentiation process that uses successive squaring and uses a logarithmic number of steps, as does fast-expt.
(Hint: Using the observation that ( b n 2 ) 2 = ( b 2 ) n 2 (b^{\frac{n}2})^2=(b^2)^{\frac{n}2} (b2n)2=(b2)2n, keep,along with the exponent n n n and the base b b b, an additionalstate variable a a a, and define the state transformation in such a way that the product a b n ab^n abn is unchanged from state to state. At the beginning of the process a is taken to be 1, and the answer is given by the value of a a a at the end of the process. In general, the technique of defining an invariant quantity that remains unchanged from state to state is a powerful way to think about the design of iterative algorithms.)

(define (expt-iter-by-square b n)
  (define (iter b n a)
    (cond ((= n 0) a)
          ((even? n) (iter (square b)
                           (/ n 2)
                           a))
          (else (iter b
                      (- n 1)
                      (* a b)))))
  (iter b n 1))

Exercise 1.17

The exponentiation algorithms in this section are based on performing exponentiation by means of repeated multiplication. In a similar way, one can perform integer multiplication by means of repeated addition. The following multiplication procedure (in which it is assumed that our language can only add, not multiply) is analogous to the expt procedure:

(define (* a b)
  (if (= b 0)
      0
      (+ a (* a (- b 1)))))

This algorithm takes a number of steps that is linear in b. Now suppose we include, together with addition, operations double, which doubles an integer, and halve, which divides an (even) integer by 2. Using these, design a multiplication procedure analogous to fast-expt that uses a logarithmic number of steps.

(define (multiply-recur a b)
  (cond ((= b 0) 0)
        ((even? b) (multiply-recur (double a) (halve b)))
        (else (+ a (multiply-recur a (- b 1))))))

Exercise 1.18

Using the results of Exercise 1.16 and Exercise 1.17, devise a procedure that generates an iterative process for multiplying two integers in terms of adding, doubling, and halving and uses a logarithmic number of steps.

(define (multiply-iter a b)
  (define (iter a b answer)
    (cond ((= b 0) answer)
          ((even? b) (iter (double a) (halve b) answer))
          (else (iter a (- b 1) (+ answer a)))))
  (iter a b 0))

Exercise 1.19

There is a clever algorithm for computing the Fibonacci numbers in a logarithmic number of steps. Recall the transformation of the state variables a a a and b b b in the fib-iter process of Section 1.2.2: a ← a + b a ← a + b aa+b and b ← a b ← a ba. Call this transformation T T T, and observe that applying T T T over and over again n n n times, starting with 1 and 0, produces the pair F i b ( n + 1 ) Fib(n + 1) Fib(n+1) and F i b ( n ) Fib(n) Fib(n). In other words, the Fibonacci numbers are produced by applying T n T^n Tn, the n t h n^{th} nth power of the transformationT , starting with the pair (1, 0). Now consider T T T to be the special case of p = 0 p = 0 p=0 and q = 1 q = 1 q=1 in a family of transformations T p q T_{pq} Tpq , where T p q T_{pq} Tpq transforms the pair ( a , b ) (a, b) (a,b) according to a ← b q + a q + a p a ← bq + aq + ap abq+aq+ap and b ← b p + a q b ← bp + aq bbp+aq. Show that if we apply such a transformation T p q T_{pq} Tpq twice, the effect is the same as using a single transformation T p ′ q ′ T_{p'q'} Tpq of the same form, and compute p ′ p′ p and q ′ q′ q in terms of p p p and q q q. This gives us an explicit way to square these transformations, and thus we can compute T n T^n Tn using successive squaring, as in the fast-expt procedure. Put this all together to complete the following procedure, which runs in a logarithmic number of steps:

(define (fib n)
  (fib-iter 1 0 0 1 n))
(define (fib-iter a b p q count)
  (cond ((= count 0) b)
        ((even? count)
         (fib-iter a
                   b
                    ; compute p′
                    ; compute q′
                   (/ count 2)))
        (else (fib-iter (+ (* b q) (* a q) (* a p))
                        (+ (* b p) (* a q))
                        p
                        q
                        (- count 1)))))

answer:

(define (fib n)
  (fib-iter 1 0 0 1 n))
(define (fib-iter a b p q count)
  (cond ((= count 0) b)
        ((even? count)
         ; count为偶数且不为0时,可以连续进行两次转换
         ; 通过简单的代数计算和化简,可以得到经过两次转换后
         ; a = b(q^2+2pq) + a(q^2+2pq) + a(p^2+q^2)
         ; b = b(p^2+q^2) + a(q^2+2pq)
         ; 所以 q' = (q^2+2pq), p' = (p^2+q^2)
         (fib-iter a
                   b
                   (+ (square p) (square q))
                   (+ (square q) (* 2 p q))
                   (/ count 2)))
         (else (fib-iter (+ (* b q) (* a q) (* a p))
                         (+ (* b p) (* a q))
                         p
                         q
                         (- count 1)))))

Exercise 1.20

The process that a procedure generates is of course dependent on the rules used by the interpreter. As an example, consider the iterative gcd procedure given above. Suppose we were to interpret this procedure using normal-order evaluation, as discussed in Section 1.1.5. (The normal-order-evaluation rule for if is described in Exercise 1.5.) Using the substitution method (for normal order), illustrate the process generated in evaluating (gcd 206 40) and indicate the remainder operations that are actually performed. How many remainder operations are actually performed in the normal-order evaluation of (gcd 206 40)? In the applicative-order evaluation?

answer:

  • normal-order: 18 times, 14 when evaluating the condition and 4 in the final reduction phase.
(gcd 206 40)
  (if (= 40 0) 206
      (gcd 40 (remainder 206 40)))

(gcd 40 (remainder 206 40))
  (if (= (remainder 206 40) 0) 40          ; (if (= 6 0) 40), remainder + 1
      (gcd (remainder 206 40) (remainder 40 (remainder 206 40))))

(gcd (remainder 206 40) (remainder 40 (remainder 206 40)))
  (if (= (remainder 40 (remainder 206 40)) 0)     ; (if (= 4 0) 6), remainder + 2
      (remainder 206 40)
      (gcd (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))

(gcd (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))
 (if (= (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) 0)       ; (if (= 2 0) 4), remainder + 4
     (remainder 40 (remainder 206 40))
     (gcd (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))))

(gcd (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))
(if (= (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))) 0)     ; (if (= 0 0) 2), remainder + 7
    (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))               ; remainder + 4, 后面的命令并未执行
    (gcd (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40))))
         (remainder (remainder (remainder 206 40) (remainder 40 (remainder 206 40))) (remainder (remainder 40 (remainder 206 40)) (remainder (remainder 206 40) (remainder 40 (remainder 206 40)))))))
  • applicative-order: 4 times
 (gcd 206 40) 
 (gcd 40 (remainder 206 40))  
 (gcd 40 6)   
 (gcd 6 (remainder 40 6))   
 (gcd 6 4)   
 (gcd 4 (remainder 6 4))   
 (gcd 4 2)   
 (gcd 2 (remainder 4 2))   
 (gcd 2 0)   
 2 

Exercise1.21

Use the smallest-divisor procedure to find the smallest divisor of each of the following numbers: 199, 1999, 19999.

  • 199: 199
  • 1999: 1999
  • 19999: 7

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 t i m e d − p r i m e − t e s t timed-prime-test timedprimetest procedure, when called with an integer n n n, prints n n n and checks to see if n n n is prime. If n n n is 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 s e a r c h − f o r − p r i m e s search-for-primes searchforprimes 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 ) \Theta(\sqrt n) Θ(n ), you should expect that testing for primes around 10,000 should take about 1 0 \sqrt 10 1 0 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 ) \Theta(\sqrt n) Θ(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?

answer:

(define (search-for-primes n count)
  (cond ((and (< count 3) (prime? n)) (and (timed-prime-test n) (search-for-primes (+ n 1) (+ count 1))))
        ((< count 3) (search-for-primes (+ n 1) count))))

; 由于这里已经判断了是否为质数,所以打印的时候就不用判断了
(define (start-prime-test n start-time)
  (report-prime (- (runtime) start-time)))
  • 这是修改start-prime-test前的执行时间
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

从1000到10000,执行时间只有2倍左右的差距,但是从10000到100000,从100000到1000000确实差不多是 1 0 \sqrt 10 1 0倍的差距。这可能跟我们追踪的时间只是其中部分代码有关,当执行次数较少时,那些“无关”代码的执行时间占比比较大,所以从1000到10000执行时间只有2倍的差距;随着执行次数增多,“无关”代码的执行时间占比越来越少,执行时间的关系就逐渐符合数学分析的结果了。

  • 这是修改start-prime-test后的执行时间,可以发现时间急剧降低,但是代价是没法监测 p r i m e ? prime? prime?的执行时间了。。
    在这里插入图片描述

Exercise 1.23

The smallest-divisor procedure shown at the start of this section does lots of needless testing: After it checks toseeif thenumberisdivisibleby2thereisnopoint in checking to see if it is divisible by any larger even numbers. is 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 primesfoundinExercise1.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?

answer:

(define (next n)
  (if (= n 2) 3 (+ n 2)))

根据上道题目的教训,当n太小时执行时间的差距可能不会跟数学分析相同,这次我特意选了比较大的数字:1000000000000000,但是执行时间的差距也还是达不到2倍的差距,只有差不多1.5倍。原因主要是因为 n e x t next next里有有个 i f if if判断,虽然输入的检测少了一半,但是每次都要额外做一次 i f if if检查,然后再+2,而原来是直接进行+1操作,所以执行的操作其实并没有减少。执行时间的降低大概是因为 i f if if所对应的机器步骤比加法要少,所以虽然算法操作没有变,但是机器步骤却变少了,所以执行时间还是降低了。

  • 1.22
    在这里插入图片描述

  • 1.23
    在这里插入图片描述

Exercise1.24

Modify the t i m e d − p r i m e − t e s t timed-prime-test timedprimetest 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 ) \Theta(\log{n}) Θ(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?

由于 f a s t − p r i m e ? fast-prime? fastprime?的时间复杂度是 Θ ( log ⁡ n ) \Theta(\log{n}) Θ(logn),为了便于计算,取10为底,则 log ⁡ 10 1000 = 3 , log ⁡ 10 1000000 = 6 \log_{10}^{1000}=3, \log_{10}^{1000000}=6 log101000=3,log101000000=6,所以执行时间应该有2倍左右的差距,由下图可以看出,实际情况跟数学分析很接近。
在这里插入图片描述

Exercise 1.25

Alyssa P. Hacker complains that we went to a lot of extra work in writing expmod. Aer 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.

这个写法是可以实现功能的,但是速度会变慢,用下面的代码来演示一下:

; The Fermat Test
; calculate the remainder of base^exp modulo m
(define (expmod base exp m) 
  (cond ((= exp 0) 1) 
        ((even? exp) 
          (remainder 
            (square (expmod base (/ exp 2) m)) ; (1) 
            m)) 
        (else 
          (remainder 
            (* base (expmod base (- exp 1) m)) 
            m))))

; The modified procedures 
(define (modified-expmod base exp m) 
  (remainder (fast-expt base exp) m))

; Helper procedures 
(define (fast-expt b n) 
  (cond ((= n 0) 1) 
        ((even? n) (square (fast-expt b (/ n 2)))) 
        (else (* b (fast-expt b (- n 1)))))) 

; calculate run time of a procedure
(define (report-elapsed-time start-time) 
  (display " *** ") 
  (display (- (runtime) start-time)))


; Test the speed 
(define start-time1 (runtime)) 
(expmod 999999 1000000 1000000) 
(report-elapsed-time start-time1)
(newline)

(define start-time2 (runtime)) 
(modified-expmod 999999 1000000 1000000) 
(report-elapsed-time start-time2)

在这里插入图片描述

Exercise 1.26

Louis Reasoner is having great difficulty doing Exercise 1.24. His f a s t − p r i m e ? fast-prime? fastprime? test seems to run more slowly than his p r i m e ? prime? prime? test. Louis calls his friend Eva Lu Ator over to help. When they examine Louis’s code, they f ind that he has rewrien the expmod procedure to use an explicit multiplication, rather than calling s q u a r e square 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 ) \Theta(\log n) Θ(logn) process into a Θ n \Theta n Θn process.” Explain.

Instead of a linear recursion, the rewritten e x p m o d expmod expmod generates a tree recursion,whose execution time grows exponentially with the depth of the tree, which is the logarithm of N N N. Therefore, the execution time is linear with N N N.

Exercise 1.27

Demonstrate that the Carmichael numbers listed in Footnote 1.47 really do fool the Fermat test. That is, write a procedure that takes an integer n n n and tests whether a n a^n an is congruent to a modulo n n n for every a < n a < n a<n,and try your procedure on the given Carmichael numbers.

修改后的 f a s t − p r i m e ? fast-prime? fastprime?如下, n n n表示要检测的数, a a a表示 2 ≤ a < n 2\le a \lt n 2a<n的任意整数,结果也确实检查不出来那些Carmichael numbers是质数。

(define (fast-prime? n a)
  (cond ((= a n) true)
        ((= (expmod a n n) a) (fast-prime? n (+ a 1)))
        (else false)))

(fast-prime? 561 2)      ; 能被3整除
(fast-prime? 1105 2)     ; 能被5整除
(fast-prime? 1729 2)     ; 能被7整除
(fast-prime? 2465 2)     ; 能被5整除
(fast-prime? 2821 2)     ; 能被7整除
(fast-prime? 6601 2)     ; 能被7整除

在这里插入图片描述

Exercise 1.28

One variant of the Fermat test that cannot be fooled is called the M i l l e r − R a b i n t e s t Miller-Rabin test MillerRabintest (Miller 1976; Rabin 1980). This starts from an alternate form of Fermat’s Little Theorem, which states that if n n n is a prime number and a a a is any positive integer less than n n n,then a a a raised to the ( n − 1 ) (n-1) (n1)-st poweris congruent to 1 modulo n n n. To test the primality of a number n n n by the Miller-Rabin test, we pick a random number a < n a < n a<n and raise a a a to the ( n − 1 ) (n-1) (n1)-st power modulo n n n using the e x p m o d expmod expmod procedure. However, whenever we perform the squaring step in e x p m o d expmod expmod, we check to see if we have discovered a “nontrivial square root of 1 modulon,” that is, a number not equal to 1 or n − 1 n-1 n1 whose square is equal to 1 modulo n n n. It is possible to prove that if such a nontrivial square root of 1 exists, then n n n is not prime. It is also possible to prove that if n n n is an odd number that is not prime, then,for at least half the numbers a < n a < n a<n, computing an 1 in this way will reveal a nontrivial square root of 1 modulo n n n. (This is why the Miller-Rabin test cannot be fooled.) Modify the expmod procedure to signal if it discovers a nontrivial square root of 1, and use this to implement the Miller-Rabin test with a procedure analogous to f e r m a t − t e s t fermat-test fermattest. Check your procedure by testing various known primes and non-primes. Hint: One convenient way to make e x p m o d expmod expmod signal is to have it return 0.

answer:

; calculate the remainder of base^exp modulo m
(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder
          (sqmod (expmod base (/ exp 2) m) m)
          m))
        (else
         (remainder
          (* base (expmod base (- exp 1) m))
          m))))

; Return 0 if `x^2 mod m` is equal to `1 mod m` and x != m - 1 and x != 1;
; x^2 otherwise."
(define (sqmod x m)  
  (cond ((and (= (remainder (square x) m) 1)    ; 1 mod m = 1 
              (not (= x (- m 1))) 
              (not (= x 1)))
         0)
        (else (square x))))

; Miller-Rabin test
; if n is a prime number and a is any positive integer less than n, then a raised to the (n-1)-st power is congruent to 1 modulo n.
; we pick a random number a < n and raise a to the (n 1)-st power modulo n to test if it is congruent to 1 modulon.
(define (miller-rabin-test n)
  (define (try-it a)
    (define (check-it x)
      (and (not (= x 0)) (= x 1)))
    (check-it (expmod a (- n 1) n)))         ; 检查a^(n-1)是否为n的倍数,且a^(n-1)÷n余数为1
  (try-it (+ 1 (random (- n 1)))))           ; random returns a nonnegative integer less than its integer input(from 1 to n).

; runs the test a given number of times, as specified by a parameter
(define (fast-prime? n times)
  (cond ((< n 2) false)            ; 2是最小的质数,所以小于2的数都不是质数
        ((= times 0) true)
        ((miller-rabin-test n) (fast-prime? n (- times 1)))
        (else false)))

(define (prime? n)
  (fast-prime? n 100))

(prime? 2)
(prime? 3)
(prime? 5)
(prime? 7)
(prime? 0)
(prime? 1)
(prime? 4)
(prime? 6)
(prime? 8)
(prime? 9)
(prime? 561)      ; 能被3整除
(prime? 1105)     ; 能被5整除
(prime? 1729)     ; 能被7整除
(prime? 2465)     ; 能被5整除
(prime? 2821)     ; 能被7整除
(prime? 6601)     ; 能被7整除

在这里插入图片描述

Exercise 1.29

Simpson’s Rule is a more accurate methodof numerical integration than the method illustrated above. Using Simpson’s Rule, the integral of a function f f f between a a a and b b b is approximated as
h 3 ( y 0 + 4 y 1 + 2 y 2 + 4 y 3 + 2 y 4 + . . . + 2 y n − 2 + 4 y n − 1 + y n ) \frac{h}{3}(y_0+4y_1+2y_2+4y_3+2y_4+...+2y_{n-2}+4y_{n-1}+y_n) 3h(y0+4y1+2y2+4y3+2y4+...+2yn2+4yn1+yn),
where h = b − a n h = \frac{b − a}n h=nba, for some even integer n n n, and y k = f ( a + k h ) y_k = f(a + kh) yk=f(a+kh). (Increasing n n n increases the accuracy of the approximation.) Define a procedure that takes as arguments f , a , b f, a, b f,a,b, and n n n and returns the value of the integral, computed using Simpson’s Rule. Use your procedure to integrate cube between 0 and 1 (with n = 100 n = 100 n=100 and n = 1000 n = 1000 n=1000), and compare the results to those of the integral procedure shown above.

; template of "summation according to Simpson’s Rule".
(define (simpson-sum term a next b n)
  ; 由于n取得是偶数,所以得到的factor会是2, 4, 2, 4, ... , 1
  ; 与Simpson’s Rule相比,第一项加了2遍,最后需要把第一项减掉一次
  (define factor (cond ((= n 0) 1)
                       ((even? n) 2)
                       (else 4)))
  (if (> a b)
      0
      (+ (* factor (term a))
         (simpson-sum term (next a) next b (- n 1)))))


(define (integral f a b n)
  (define h (/ (- b a) n))
  (define (integral-next x)
    (+ x h))
  (* (/ h 3.0)
     ; 把第一项减掉一次
     (- (simpson-sum f a integral-next b n) (f a))))


(integral cube 0 1 100)
(integral cube 0 1 1000)

; 输出
0.25
0.25

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2150358.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++ Primer Plus习题】16.10

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: #include <iostream> #include <string> #include <…

Vue2学习笔记(02条件渲染 、监视数据的原理)

1、v-if和v-show的区别 2、Vue监视数据的原理

8.安卓逆向-安卓开发基础-安卓四大组件1

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

Python酷库之旅-第三方库Pandas(122)

目录 一、用法精讲 541、pandas.DataFrame.take方法 541-1、语法 541-2、参数 541-3、功能 541-4、返回值 541-5、说明 541-6、用法 541-6-1、数据准备 541-6-2、代码示例 541-6-3、结果输出 542、pandas.DataFrame.truncate方法 542-1、语法 542-2、参数 542-3…

Linux进阶命令-scp

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 经过上一章Linux日志的讲解&#xff0c;我们对Linux系统自带的日志服务已经有了一些了解。我们接下来将讲解一些进阶命令&am…

有没有自带财务管理功能的海外仓系统?

在全球化的商业环境中&#xff0c;海外仓作为连接国际市场的物流枢纽&#xff0c;其重要性日益凸显。然而&#xff0c;随着业务范围的扩展和费用类型的多样化&#xff0c;海外仓在财务管理上面临着诸多挑战。传统的手工计费和对账方式不仅耗时费力&#xff0c;而且容易出错&…

常用的k8s容器网络模式有哪些?

常用的k8s容器网络模式包括Bridge模式、Host模式、Overlay模式、Flannel模式、CNI&#xff08;ContainerNetworkInterface&#xff09;模式。K8s的容器网络模式多种多样&#xff0c;每种模式都有其特点和适用场景。Bridge模式适用于简单的容器通信场景&#xff1b;Host模式适用…

将阮一峰老师的《ES6入门教程》的源码拷贝本地运行和发布

你好同学&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 阮一峰老师的《ES6入门教程》应该是很多同学学习 ES6 知识的重要参考吧&#xff0c;应该也有很多同学在看该文档的时候&#xff0c;想知道这个教程的前端源码是怎么实现的&#xff0c;也可能有同学下载…

掌握Python-uinput:打造你的输入设备控制大师

文章目录 掌握Python-uinput&#xff1a;打造你的输入设备控制大师背景&#xff1a;为何Python-uinput不可或缺&#xff1f;Python-uinput是什么&#xff1f;如何安装Python-uinput&#xff1f;简单库函数使用方法创建虚拟设备模拟按键模拟鼠标移动模拟滚轮滚动关闭设备 场景应…

IP Source Guard技术原理与应用

目录 IP Source Guard概述 IP Source Guard源数据表项 IP Source Guard源数据-静态添加 IP Source Guard查看 IP Source Guard使用注意事项 IP Source Guard概述 局域网IP冲突给网络的运维带来很大困扰存在以下风险&#xff1a; 使用手工配置IP地址的方式上网&#xff0c…

Redis——C++库redisplusplus在Linux环境下的安装

目录 第一步&#xff0c;安装hiredis第二步&#xff0c;下载redis源码第三步&#xff0c;编译/安装 redis-plus-plus使用redis-plus-plus(以Centos为例)Ubuntu的Makefile 第一步&#xff0c;安装hiredis redis-plus-plus 是基于 hiredis 实现的&#xff0c;而hiredis 是⼀个 C…

【图像检索】基于傅里叶描述子的形状特征图像检索,matlab实现

博主简介&#xff1a;matlab图像代码项目合作&#xff08;扣扣&#xff1a;3249726188&#xff09; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本次案例是基于傅里叶描述子的形状特征图像检索&#xff0c;用matlab实现。 一、案例背景和算法…

企业数字化底座与数字化转型方案(可编辑的81页PPT)

方案介绍&#xff1a;在当今数字化转型的浪潮中&#xff0c;企业数字化底座与数字化转型方案是企业应对市场变化、提升竞争力的关键举措。通过构建数字化底座&#xff0c;实现数据的集中管理和共享&#xff1b;通过数字化转型方案的实施&#xff0c;推动企业的全面数字化改造。…

进阶版水仙花数水是指一个n位数,各个位数字的n次方之和等于该数字本身

两种方法&#xff1a; 第一种&#xff0c;是输入一个数值&#xff0c;判断是否为水仙花数 //打印水仙花数 //水仙花数是指一个n位数&#xff0c;各个位数字的n次方之和等于该数字本身 //如&#xff1a;1531^35^33^3 // //分析&#xff1a; //153/1015 //15/101 //1/100 #incl…

.whl文件下载及pip安装

以安装torch_sparse库为例 一、找到自己需要的版本&#xff0c;点击下载。 去GitHub的pyg-team主页中找到pytorch-geometric包。网址如下&#xff1a; pyg-team/pytorch_geometric​github.com/pyg-team/pytorch_geometric 然后点击如图中Additional Libraries位置的here&am…

Qt 多线程TCP客户端使用QTimer进行重连服务器———附带详细代码和讲解

文章目录 0 背景1 原理1.1 QThread的线程归属1.2 Qtimer使用1.3 TCP客户端使用 2 问题解决2.1 解决思路2.2 解决方法 3 完整的代码示例3.1 tcp_client类3.2 主界面类 附录参考 0 背景 在子线程中&#xff0c;使用Qtimer来进行定时重连TCP服务器&#xff0c;总是会出现跨线程创…

U盘显示未被格式化:深度解析、恢复策略与预防之道

现象透视&#xff1a;U显示未被格式化的迷局 在日常的数字生活中&#xff0c;U盘作为我们随身携带的数据仓库&#xff0c;承载着无数重要的文件与回忆。然而&#xff0c;当U盘突然弹出“未被格式化”的警告时&#xff0c;这份便捷瞬间转化为焦虑与不安。这一提示不仅意味着U盘…

uboot:源码分析-启动第一阶段-start.S解析

start.S引入 进入start.S文件中&#xff0c;发现57行中就是_start标号的定义处 SourceInsight中添加行号 在SI中&#xff0c;如果我们知道我们要找的文件的名字&#xff0c;但是我们又不知道他在哪个目录下&#xff0c;我们要怎样找到并打开这个文件&#xff1f;方法是在SI中先…

多重指针变量(n重指针变量)实例分析

0 前言 指针之于C语言&#xff0c;就像子弹于枪械。没了子弹的枪械虽然可以用来肉搏&#xff0c;却失去了迅速解决、优雅解决战斗的能力。但上了膛的枪械也非常危险&#xff0c;时刻要注意是否上了保险&#xff0c;使用C语言的指针也是如此&#xff0c;要万分小心&#xff0c;…

usemeno和usecallback区别及使用场景

1. useMemo 用途: useMemo 用于缓存计算结果。它接受一个函数和依赖项数组&#xff0c;只有当依赖项发生变化时&#xff0c;才会重新计算该函数的返回值。否则&#xff0c;它会返回缓存的值。 返回值: useMemo 返回的是函数执行后的结果。 使用场景: 当一个计算量大的函数在每…