1-20題意,有人認爲既然我們已經知道了fast-exp的計算方式,那麼我們的expmod就顯得多餘,我們可以直接計算fast-exp mod n代替expmod 他這樣想對嗎
直接上代碼
(define (reminder x y) (if (< x y) x (reminder (- x y) y)))
(define (expmod base Exp div)
(define (square x) (* x x) )
(define (is-even? x) (= (reminder x 2) 0))
(cond ((= Exp 0) 1)
((is-even? Exp) (reminder (square (expmod base (/ Exp 2) div) ) div ) )
(else (reminder (* base (expmod base (- Exp 1) div) ) div ) ) )
)
(define (fast-exp b n )
(define (square x)(* x x))
(define (is-even? x) (= (reminder n 2) 0))
(define (reminder x n) (if (> x n) (reminder (- x n) n) x))
(cond ((= n 0 ) 1)
((is-even? n) (fast-exp b (/ n 2)))
(else (* b (fast-exp b (- n 1)))))
)
(define (expmod2 base Exp m)
(reminder (fast-exp base Exp) m)
)
第二張圖我等了老半天就是不出來結果
然後我們分析一下,第一張圖顯然結果是正確的,但是第二張圖一目瞭然的結果計算機卻始終無法給出答案,
我們在一聯想,計算機表示數是有限制的,也就是說,當數值超過這個限制,計算機就無法表達或者正確表達
因此雖然fast-exp確實可以提高運算速度,但是,指數運算很容易超過限制,expmod2就顯得有些雞肋了
ex1-21 比較簡單
用square 以及 (* (expmod b (/ e 2) m) (expmod b (/ e 2) m))在於 square只要計算一次遞歸 而後式則相當於二分再乘二即什麼都沒幹,所以O(n)
ex1-22 利用費馬測試變式來降低欺騙
(define (variant-fermat-test n)
(define starttime (runtime))
(define (reminder x y)
(cond ((< x 0) (reminder (+ x y ) y))
(else (if (< x y) x (reminder (- x y ) y) ))
)
)
(define (Gcd a b)
(if (= b 0) a (Gcd b (reminder a b)))
)
(define (Geta)
(define a (+ (random (- n 2)) 2))
(if (= (Gcd a n) 1) a (Geta))
)
(define a (Geta))
(display "*")
(define (symbol n) (cond ((= (reminder n 2) 0) 1) (else -1)))
(define (J a n)
(cond
((= a 1) 1)
((= (reminder a 2) 0) (* (J (/ a 2) n) (symbol (/ (- (* n n) 1)8)) ))
(else (* (J (reminder n a) a) (symbol (/ (* (- a 1) (- n 1)) 4)) ) )
)
)
(define (expmod base Exp div)
(define (reminder x y) (if (< x y) x (reminder (- x y) y)))
(define (square x) (* x x) )
(define (is-even? x) (= (reminder x 2) 0))
(cond ((= Exp 0) 1)
((is-even? Exp) (reminder (square (expmod base (/ Exp 2) div) ) div ) )
(else (reminder (* base (expmod base (- Exp 1) div) ) div ) ) )
)
(define z (J a n))
(display "\n")
(define endtime3 (runtime))
(display (- endtime3 starttime))
(define x (reminder z n))
(display "\n")
(define endtime1 (runtime))
(display (- endtime1 starttime))
(define y (expmod a (/ (- n 1) 2) n) )
(display "\n")
(define endtime2 (runtime))
(display (- endtime2 starttime))
(define flag (= x y))
(display "\n")
(define endtime (runtime))
(display (- endtime starttime))
flag
)
首先,我們可以看出(J a n)佔了絕大多數的時間,現在我們來分析(J a n)的時間
當a=even時,該式=符號*(J a/2 n) 顯然,由於最終給出條件a=1 1 這類似於二分法遞歸
當a!=1 &&a!=even 該式=符號*(reminder (n a) a) 這裏我們進行討論:
n<2a 相當於(J (n-a) a) 將第一個參數變爲偶數,類似於二分操作處理奇數情況
n>2a 假設取完模結果變爲偶數,則類似於上一部分的情況, 至於其他則最多可能多一步還原到n<2a 這裏具體時間不太會算,請會的人教一下(謝謝)
另外這個測試只是保證一半以上概率正確