Think Python 2nd Edition Exercise 7-1

Question:

“Square Roots” loop:

while True:
    y = (x+ a/x) / 2
    if y == x:
        return x
    x = y

Copy the loop from “Square Roots” and encapsulate it in a function called mysqrt that takes a as a parameter, chooses a reasonable value of x, and returns an estimate of the square root of a.
To test it, write a function named test_square_root that prints a table like this:

a   mysqrt(a)     math.sqrt(a)  diff
-   ---------     ------------  ----
1.0 1.0           1.0           0.0
2.0 1.41421356237 1.41421356237 2.22044604925e-16
3.0 1.73205080757 1.73205080757 0.0
4.0 2.0           2.0           0.0
5.0 2.2360679775  2.2360679775  0.0
6.0 2.44948974278 2.44948974278 0.0
7.0 2.64575131106 2.64575131106 0.0
8.0 2.82842712475 2.82842712475 4.4408920985e-16
9.0 3.0           3.0           0.0

Here’s what I wrote:

import math

def mysqrt(a):
    x = a/5
    while True:
        y = (x+ a/x) / 2
        if y == x:
            return x
        x = y

def test_square_root():
    a = 1.0
    print('a', repr(mysqrt(a)).rjust(6), repr(math.sqrt(a)).rjust(12), 'diff'.rjust(10))
    print("-      ---------            ------------          ----")
    while a < 10.0:
        print(a, "  ", mysqrt(a), "  ", math.sqrt(a), "  ", abs(mysqrt(a)-math.sqrt(a)))
        a += 1

test_square_root()

Here’s what I got:

a    1.0          1.0       diff
-      ---------            ------------          ----
1.0    1.0    1.0    0.0
2.0    1.414213562373095    1.4142135623730951    2.220446049250313e-16
3.0    1.7320508075688772    1.7320508075688772    0.0
4.0    2.0    2.0    0.0
5.0    2.23606797749979    2.23606797749979    0.0
6.0    2.449489742783178    2.449489742783178    0.0
7.0    2.6457513110645907    2.6457513110645907    0.0
8.0    2.82842712474619    2.8284271247461903    4.440892098500626e-16
9.0    3.0    3.0    0.0

As you can see, the array is not neatly aligned. How can I print an array if the elements do not have the same length? Thank you!

Asked By: Zilong Li

||

Answers:

Adjust the print statement to print each element to a fixed number of decimal places using {0:.8f} {0:.10f} and {0:.4f} respectively, or your desired coloumn width.

Answered By: Steve Hope

You can use the tab t in the print statement:

print("t", a, "t", y, "t", square_method, "t" "diff:", diff,)
Answered By: Leigh Daubermann

here is my code which looks good:

from decimal import *
import math

def mysquare_root(a):
     x = 1
    while True:
        #print(x)
        y = (x + a/x) / 2
        if x == y:
            break
        x = y
    return (x)


def square_root(a):
    x = math.sqrt(a)
    return x
def difference(a):
    d = square_root(a) - mysquare_root(a)
    return d

# the below function prints the table, but the alignment is not good.
def list_func():
    for a in range(1, 10):
         print(float(a), " " * 20, f"{mysquare_root(a)}", " "*20, f"{square_root(a)}"," "*20,f"{difference(a)}")
list_func()

# by using the below functions ("dec_1, dec_2") we are fixing the decimals after point to 
#..........'15' numbers for uniform alignment.
# the 1st function is for square_root which is built in function
# 2nd is for alignment of difference after built_in_ function values.
def dec_1(a):
    d = Decimal(mysquare_root(a))
    #print(d)
    d1 = round(d, 15)
    #print(d1)
    #print(d1)
    return Decimal(d1)

def dec_2(a):
    d = Decimal(square_root(a))
    #print(d)
    d1 = round(d, 15)
    #print(d1)
    return Decimal(d1)

def print_it():
    for a in range(1, 10):
         print(float(a), " " * 20, f"{dec_1(a)}", ' '*9, f"{dec_2(a)}", ' '*9, f"{difference(a)}")

print_it()

here is the solution what i got:

1.0       1.000000000000000           1.000000000000000           0.0
2.0       1.414213562373095           1.414213562373095           2.220446049250313e-16
3.0       1.732050807568877           1.732050807568877           0.0
4.0       2.000000000000000           2.000000000000000           0.0
5.0       2.236067977499790           2.236067977499790           0.0
6.0       2.449489742783178           2.449489742783178           0.0
7.0       2.645751311064591           2.645751311064591           0.0
8.0       2.828427124746190           2.828427124746190           4.440892098500626e-16
9.0       3.000000000000000           3.000000000000000           0.0
Answered By: S Saikiran

Here is the perfect solution that I figured out:

from decimal import *
import math
# below function is used to find the square roots of a number using the newton's method(i.e; using formula)
def mysquare_root(a):
    x = 1
    while True:
        y = (x + a/x) / 2
        if x == y:
            break
        x = y
    return x

# below function is used to find the square roots of a number using the built_in function
def square_root(a):
    x = math.sqrt(a)
    return x

# below function gives the difference between thr toe functions above
def difference(a):
    d = square_root(a) - mysquare_root(a)
    return d


# Normally we can print the values as usual by giving selected no. of spaces between one and other values
# but, because of different float values, it is not possible to print uniformly as a table.
# In below function we use the decimal to over come the possibility.

def dec_1(a):

    # this function is to print the values of function 'mysquare_root()' uniformly as a table.
    # in order to access the decimals of a value we use 'Decimal' in_built function and we can round it off.
    d = Decimal(mysquare_root(a))
    #print(d)
    # here we are rounding off the decimal values to '15' after point.
    d1 = round(d, 15)
    #print(d1)
    # if we round off all the values, even perfect square will have '15' zeros after point. we don't want it.
    # so, we are rounding off the perfect square values to '0' decimal values after point
    if d1 - Decimal(mysquare_root(a)) == 0:
        d1 = round(d, 0)
        #print(d1)
        # in order to get one decimal value after point, simply we return a float value
        return float(d1)
    #print(d1)
    # we are return value which is rounded to certain values after point.
    return (d1)
#print(dec_1(2))

# the below function works same as above function, but for in_built method to find square root.
def dec_2(a):
    # This function is working for to print the values of function 'square_root()'
    d = Decimal(square_root(a))
    #print(d)
    d1 = round(d, 15)
    if d1 - Decimal(mysquare_root(a)) == 0:
        d1 = round(d, 0)
        return float(d1)
    #print(d1)
    return Decimal(d1)
#print(dec_2(2))

def print_it():
    # below two print statements arranged as per requirement.
    print("numbers", " "*14, 'mysquare_root', ' '*8, 'square_root', ' '*10, "difference")
    print("-------", " " * 14, '-------------', ' ' * 8, '-----------', ' ' * 10, "----------")
    for a in range(1, 10):
        # here we are going to print square roots 1-9
        # here we are giving the 18 spaces between the one float value to another in table.
        # if there is only 15 decimal values after point, we already covered 14 position after float value, so we will give 4 spaces
        # here to do spacing between values, we just comparing float value and int value to get how much space is needed.

        print(float(a), " " * 18, f'{dec_1(a)}', " "*18 if dec_1(a) == int(dec_1(a)) else " "* 4, f'{dec_2(a)}', " "*18 if dec_1(a) == int(dec_2(a)) else " "* 4, f"{difference(a)}")

print_it()

here is my output

numbers                mysquare_root          square_root            difference
-------                -------------          -----------            ----------
1.0                    1.0                    1.0                    0.0
2.0                    1.414213562373095      1.414213562373095      2.220446049250313e-16
3.0                    1.732050807568877      1.732050807568877      0.0
4.0                    2.0                    2.0                    0.0
5.0                    2.236067977499790      2.236067977499790      0.0
6.0                    2.449489742783178      2.449489742783178      0.0
7.0                    2.645751311064591      2.645751311064591      0.0
8.0                    2.828427124746190      2.828427124746190      4.440892098500626e-16
9.0                    3.0                    3.0                    0.0

I tried to give you the clarity on this code. I hope you will get it.

Answered By: S Saikiran

Try this code, it results in a format closest to what you require:

import math
epsilon= 0.00000000001

def mysqrt(a):
  x= a/2
  while True:
      y = (x + a/x) / 2
      if abs(y-x) < epsilon:
        return y
        break
      x = y

def right_digit(n):
  '''Takes a floating point number and returns its right
  most digit in 'str' format'''
  trunc= f'{n:.11f}'
  dig= trunc[len(trunc)-1] 
  return dig

def test_square_root(n):
  print('a', ' '*1, 'mysqrt(a)', ' '*3, 'math.sqrt(a)', ' diff')
  print('-', ' '*1, '-'*9, ' '*3, '-'*12, ' ' + '-'*4)
  
  for i in range(9):
    x= i+1
    
    print(f'{x:0.1f}', end=' ') 
    
    if mysqrt(x)- int(mysqrt(x)) < 0.001:
      y=1      
    elif right_digit(mysqrt(x)) == '0':
      y=10
    else:
      y=11
      
    print(f'{mysqrt(x):<13.{y}f}', end=' ')
    print(f'{mysqrt(x):<13.{y}f}', end=' ')

    diff= math.sqrt(x) - mysqrt(x)
    print(f'{diff:.12g}')

test_square_root(9)

The resulting table looks something like this:

a   mysqrt(a)     math.sqrt(a)  diff
-   ---------     ------------  ----
1.0 1.0           1.0           0
2.0 1.41421356237 1.41421356237 2.22044604925e-16
3.0 1.73205080757 1.73205080757 0
4.0 2.0           2.0           0
5.0 2.2360679775  2.2360679775  0
6.0 2.44948974278 2.44948974278 0
7.0 2.64575131106 2.64575131106 0
8.0 2.82842712475 2.82842712475 4.4408920985e-16
9.0 3.0           3.0           0

I have used f-strings to format this table but you can also use .format() method. There are 2 special cases that you have to keep in mind:

  1. Any number that is a perfect square must result in a single decimal point in its square root (applicable for 1,4,9)
  2. The leftmost decimal digit can’t be zero (applicable for 5)

You can see in the code above that there are 3 conditional statements included in test_square_root function to take care of these issues.

Answered By: BOT_bkcd

Here is a solution based solely on topics covered in previous chapters of the book.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
   
# Think Python Chapter 7
    
# exercise 7-1

import math

def mysqrt(a):
    '''calculates square root using Newtons method
    '''
    x = a / 2
    while True:
        y = (x + a / x) / 2
        if x - y < 0.00001:
            break
        x = y
    return y


def diff(a):
    '''calculates difference my function for square root and standard function
    '''
    d = mysqrt(a) - math.sqrt(a)
    return d


def test_mysqrt(a):
    '''prints list of values and differences for 
    calculating square root my way and python way
    '''
    print(
        "a", " " * 2,
        "mysqrt(a)", " " * 11,
        "math.sqrt(a)", " " * 8,
        "diff"
        )  # topline
    while a > 1:
        a = a-1
        len_a = 3 - len(str(a))  #calculate extra spaces
        len_my = 20 - len(str(mysqrt(a)))
        len_ma = 20 - len(str(math.sqrt(a)))
        print(
            a, " " * len_a,
            mysqrt(a), " " * len_my,
            math.sqrt(a), " " * len_ma,
            diff(a)
            )
    return


test_mysqrt(10)

Hope this helps.

Answered By: Harm Deens