For my class assignment I am working on code that uses loops utilizing Heron's method to find an estimate of the square root while also displaying the iteration and relative change.
I have the following code:
# Problem 1.
def square_root_for(a, x0, max_iter = 10, tol=1e-14):
""" (number, integer, number) -> float
Return an estimate of the square root of a number using the Heron's method.
>>> square_root_for(5, 5)
Iteration | Estimate | Relative Change
-------------------------------------------------
1 | 3.00000000000000 | 0.4000000000000000
2 | 2.33333333333333 | 0.2222222222222222
3 | 2.23809523809524 | 0.0408163265306123
4 | 2.23606889564336 | 0.0009053870529653
5 | 2.23606797749998 | 0.0000004106060359
6 | 2.23606797749979 | 0.0000000000000842
7 | 2.23606797749979 | 0.0000000000000000
2.23606797749979
"""
x = [x0]
x.append(a/x0)
print('Iteration | Estimate | Relative Change')
print('-------------------------------------------------')
for i in range(1,max_iter):
change = (abs(x[i] - x[i-1]) / x[i-1])
if change > tol:
x.append(1/2 * (x[i] (a / x[i])))
else:
break
print('{} | {:.14f} | {:.16f}'.format(i, x[i], change))
return(x[i])
# Don't change or delete the 5 lines of code below.
a = 5
max_iter = 100
tol = 1e-15
x_final = square_root_for(a, a, max_iter, tol)
print('Final estimate using square_root_for is {0}'.format(x_final))
# Problem 2.
def square_root_while(a, x0, tol=1e-14):
""" (number, number, number) -> float
Return an estimate of the square root of a number using the Heron's method.
>>> square_root_while(5, 5)
Iteration | Estimate | Relative Change
-------------------------------------------------
1 | 3.00000000000000 | 0.4000000000000000
2 | 2.33333333333333 | 0.2222222222222222
3 | 2.23809523809524 | 0.0408163265306123
4 | 2.23606889564336 | 0.0009053870529653
5 | 2.23606797749998 | 0.0000004106060359
6 | 2.23606797749979 | 0.0000000000000842
7 | 2.23606797749979 | 0.0000000000000000
2.23606797749979
"""
x = [x0]
x.append(a/x0)
print('Iteration | Estimate | Relative Change')
print('-------------------------------------------------')
i = 1
while i < max_iter 1:
change = (abs(x[i] - x[i-1]) / x[i-1])
if change > tol:
x.append(1/2 * (x[i] (a / x[i])))
else:
break
print('{} | {:.14f} | {:.16f}'.format(i, x[i], change))
i = 1
return x[i]
# Don't change or delete the 4 lines of code below.
a = 5
tol = 1e-15
x_final = square_root_while(a, a, tol)
print('Final estimate using square_root_while is {0}'.format(x_final))
The problem is that my output is incorrect. The correct output (As seen in the docstrings) would be:
Iteration | Estimate | Relative Change
-------------------------------------------------
1 | 3.00000000000000 | 0.4000000000000000
2 | 2.33333333333333 | 0.2222222222222222
3 | 2.23809523809524 | 0.0408163265306123
4 | 2.23606889564336 | 0.0009053870529653
5 | 2.23606797749998 | 0.0000004106060359
6 | 2.23606797749979 | 0.0000000000000842
7 | 2.23606797749979 | 0.0000000000000000
2.23606797749979
My output is:
Iteration | Estimate | Relative Change
-------------------------------------------------
1 | 1.00000000000000 | 0.8000000000000000
2 | 3.00000000000000 | 2.0000000000000000
3 | 2.33333333333333 | 0.2222222222222222
4 | 2.23809523809524 | 0.0408163265306123
5 | 2.23606889564336 | 0.0009053870529653
6 | 2.23606797749998 | 0.0000004106060359
7 | 2.23606797749979 | 0.0000000000000842
2.23606797749979
The problem is in the first two lines of outputs, but I can't figure out what I did wrong. Maybe it is something with the math? A student asked my teacher a similar problem and she said "When you do your first append, it's a little different from the other appends re. the math." I'm not sure if that helps. Any tips would be appreciated I am very stuck.
CodePudding user response:
You need to make the following changes to your code:
- the second line in your function should be
x.append(0.5*(x0 a/x0))
. You need the average of x and a/x (as in every other iteration). - the
print
statement should be executed before theif/else
block to print the last line where your relative change falls below the tolerance.
Try:
def square_root_for(a, x0, max_iter = 10, tol=1e-14):
x = [x0]
x.append(0.5*(x0 a/x0))
print('Iteration | Estimate | Relative Change')
print('-------------------------------------------------')
for i in range(1, max_iter):
change = (abs(x[i] - x[i-1]) / x[i-1])
print('{} | {:.14f} | {:.16f}'.format(i, x[i], change))
if change > tol:
x.append(1/2 * (x[i] (a / x[i])))
else:
break
return(x[i])
>>> square_root_for(5, 5, 100, 1e-15)
Iteration | Estimate | Relative Change
-------------------------------------------------
1 | 3.00000000000000 | 0.4000000000000000
2 | 2.33333333333333 | 0.2222222222222222
3 | 2.23809523809524 | 0.0408163265306123
4 | 2.23606889564336 | 0.0009053870529653
5 | 2.23606797749998 | 0.0000004106060359
6 | 2.23606797749979 | 0.0000000000000842
7 | 2.23606797749979 | 0.0000000000000000
Final estimate using square_root_for is 2.23606797749979
CodePudding user response:
In general, if you find yourself guess at what order lines of code should go in, you should try rewriting the code - by now, you should understand what each line means, look at the below:
def square_root_for(a, first_guess, max_iter=10, tol=1e-14):
print('Iteration | Estimate | Relative Change')
print('-------------------------------------------------')
# put a and the first guess in a list
guesses = [a, first_guess]
# loop for a maximum of max_iter times
for i in range(1, max_iter):
# add a new guess to the end of the list
guesses.append(1 / 2 * (guesses[i] (a / guesses[i])))
# compute the relative change between the last guess [-1] and the one before [-2]
change = abs(guesses[-1] - guesses[-2]) / guesses[-2]
print('{} | {:.14f} | {:.16f}'.format(i, guesses[-1], change))
# stop if the change is at or below tol
if change <= tol:
break
# return the last guess in the list as the result
return guesses[-1]
This returns the same correct result, but note how every line that does something is actually easier to understand, because it more literally describes itself. You almost don't need the comments.