Exercise 1.07
In Ex 1.7, we redefine the good-enough?
test from
the [Newton’s Square roots] (https://benjamin-philip.github.io/sicp/example-newtons-square-root)
example inorder to make
it compatible will small numbers and very large numbers.
The Question
Exercise 1.07: 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 better for small and large numbers?
My thoughts
This exercise is an attempt to fix the problems we faced in Newton’s square roots example
We found that the good-enough?
test was pathetic at returning
accurate results (i.e in the level of accuracy given) for small
numbers and in the case of larger numbers, just took too long.
An alternative strategy
If you read the latter parts of the question, you notice that another way of testing if our guess is good enough? is to stop when the changes are very minute i.e, The changes are only a certain fraction of the guess. This helps as the number of iterations for smaller numbers increases (which increases accuracy) and the iterations for larger numbers decreases (which decreases accuracy as we don’t need such precision for larger numbers.)
If we re-write this in simple language this would be:
Divide guess
by the difference of it and it’s improvement
and check if it it is lesser than a given amount.
Rewrite good-enough?
We can now copy our square-roots file (cp path/to/file /path/to/new-file
)
and change good-enough?
. We shall take the “given number” as 0.0001.
(define (good-enough? guess x)
(< (/ (- (improve-guess guess x) guess) guess) 0.0001))
This gives us the script:
(define (average x y)
(/ (+ x y) 2)) ;; divide the sum of x and y by 2
(define (improve-guess guess x)
(average guess (/ x guess)));; return the average of guess and guess divided by x
(define (square x)
(* x x));; returns the square of a numeral
(define (good-enough? guess x)
(< (/ (- (improve-guess guess x) guess) guess) 0.0001))
(define (sqrt-iter guess x)
(if (good-enough? guess x)
guess
(sqrt-iter (improve-guess guess x)
x)))
;; if our guess is good enough, return it,
;;else call (sqrt-iter) with an improved guess
(define (square-root x)
(sqrt-iter 1.0 x)) ;; Call sqrt-iter with the guess of 1
Now let us run it and see how it works:
1 ]=> (square-root 4)
;Value: 2.5
Now 2.5 is obviously wrong. The problem is the moment the guess, is greater than
or equal to the real square root, (/ (- (improve-guess guess x) guess) guess)
returns a negative number, In the case where guess = 2.5 , x = 4
,
I got `-.1800000000000000000008 as the answer.
Trying (/ (- guess (improve-guess guess x)) guess)
was quite the opposite:
I was getting negative numbers for numbers lesser than the square root. However,
Fixing good-enough?
So how do you fix good-enough?
one option is to compare the absolute value
of the division. So let’s try that:
(define (good-enough? guess x)
(< (abs (/ (- (improve-guess guess x) guess) guess) 0.0001))
1 ]=> (square-root 256)
;Value: 16.00000352670594
Now that’s what we want!
The Answer
Yes. Yes it does..