; ======================================================================
;
; Structure and Interpretation of Computer Programs
; (trial answer to excercises)
;
; 計算機程序的構造和解釋(習題試解)
;
; created: code17 02/24/05
; modifIEd:
; (保持內容完整不變前提下,可以任意轉載)
; ======================================================================
;; SICP No.1.7
;; 本期第一部分為理解題,第二部分為編程
;; 理解部分:關於誤差
;; 函數"good-enough?"在參數值較大和較小時均會產生誤差問題
;; (1) 正常的計算都會產生誤差,比如1/3是無限循環小數,有無數個有效位(從
;; 左端的一個不為0的數字起至右端最後一位不為0的數字止),而在計算機中
;; 或任何其他形式的機械計算中,分配給每個數字的存儲空間都是有限位的。
;; 因此捨入誤差在所難免。但在一般的普通計算中,因為計算機已經提供了
;; 相當多的存儲位,右端較低位次上需要捨入的量級往往非常小,大部分情
;; 況下對計算結果沒什麼影響。
;; (2) 但當運算結果很大時,小數點左邊位數很多,所進行捨入和非捨入的分界
;; 位置就可能很高,因此誤差就會很大。設想一個最簡單的模型,一共有10
;; 個有效位(小數點和其他控制位另算),那麼1.234567890123捨入為
;; 1.234567890,捨入誤差為0.000000000123;而同樣有效位的1234567890123
;; 則捨入為1234567890000,捨入誤差為123,也就是說所有的1234567890xxx
;; 在計算存儲以後只能獲得同樣的結果。因此在good-enough?的定義中,當參
;; 數很大時,自動的捨入誤差已經相當大,絕對誤差很大的數也可能被捨入為
;; 同樣的數值,從而導致判斷為good-enough,
;; > (* 123456789.1 123456789.1)
;; 15241578774881878.0
;; ;; 實際值15241578774881878.81,捨入誤差0.81,所以
;; > (good-enough? 123456789.1 15241578774881878)
;; #t
;; ;; 而根據表達式(square guess)和x的實際差是0.81>>>0.001
;; (3) 當參數很小時,同樣會發生捨入誤差。但正如我們所說的低位的捨入誤差
;; 絕對值很小。問題在於,在一個固定的絕對誤差標准(比如這裡的<0.001)
;; 下,如果參數本身的量級足夠小,那麼這個絕對誤差標准所允許的誤差相
;; 對於參數本身的相對誤差可能是驚人的。比如
;; > (good-enough? 0.00001 0.0000000001)
;; #t
;; ;; 准確值0.00001 * 0.00001 = 0.0000000001
;; > (good-enough? 0.0001 0.0000000001)
;; #t
;; ;; 比准確值大了10倍的0.0001,一樣可以通過測試。因為
;; ;; 0.0001 * 0.0001 -0.0000000001 同樣小於0.0001
;; 和上一種情況不同,這裡的判斷是嚴格符合我們的程序邏輯的,而不是
;; 自動的捨入造成的問題(這裡的有效位是1位),但這樣的結果是沒有意義的。
;; 而問題在於在這個問題中,無論我們規定多小的誤差標准,總存在足夠小的
;; 參數使得這種絕對誤差標准沒有意義。
;; 由此可見,首先,在考慮誤差相關的計算時,應該盡量避免過大的操作數,在
;; 原始的good-enough?定義中,比較(square guess) 和 x 不如比較guess和
;; (/ x guess),這兩種方法有著類似的含義,但後者的運算數的量級遠小於前者。
;; 其次使用絕對誤差在很多情況下是有問題的,而使用相對誤差控制顯然可以
;; 解決(3)中的問題,在某些情況下也可以降低運算數的量級從而改善(2)。
;; 程序部分:
(define (average x y
) (/
(+ x y
) 2))(define (improve guess x
) (average guess
(/ x guess
)))(define (good-enough? oldguess guess
) (<
(abs
(/
(- guess oldguess
) guess
)) 0.001))(define (sqrt-iter oldguess guess x
) (if (good-enough? oldguess guess
) guess
(sqrt-iter guess
(improve guess x
) x
)))(define (sqrt x
) (sqrt-iter
0.1 1.0 x
));; Test-it
;; > (sqrt 2)
;; 1.4142135623746899
;; > (sqrt 9)
;; 3.000000001396984
;; >