Due Date: One week from the posting date @ 10:30 AM. This homework aims at giving you some experience with branching, looping, and functions in both MATLAB and Python. Write your scripts with the corresponding *.py or *.m file names, and add a readme.md file in HW 3 folder of your project if you need to add any additional explanation (Don’t forget to use markdown syntax highlight in your readme file, if needed).




1.  In mathematics, the Fibonacci numbers are the numbers in the following integer sequence, called the Fibonacci sequence, that are characterized by the fact that every number after the first two is the sum of the two preceding ones:

with the following sequence equation,

Write a Python function named fib that takes in an input argument which should be integer number n, and then calculates the $n^{th}$ number in the Fibonacci sequence and outputs it on the screen. Also, if the input argument is not a non-negative integer, it prints an error message on the screen and asks the user to re-enter a non-negative integer number. Also, when it is done with finding the requested Fibonacci number, it asks again the user to either input a new non-negative integer, or enter ‘stop’ to end the function, like the following,

>>> fib('amir')
The input argument amir is not a non-negative integer!
Please enter a non-negative integer: -123
The input argument -123 is not a non-negative integer!
Please enter a non-negative integer: 12.4
The input argument 12.4 is not a non-negative integer!
Please enter a non-negative integer: 0
Fib(0) = 0
Please enter another non-negative integer or type stop: 1
Fib(1) = 1
Please enter another non-negative integer or type stop: 2
Fib(2) = 1
Please enter another non-negative integer or type stop: 3
Fib(3) = 2
Please enter another non-negative integer or type stop: 4
Fib(4) = 3
Please enter another non-negative integer or type stop: 5
Fib(5) = 5
Please enter another non-negative integer or type stop: 6
Fib(6) = 8
Please enter another non-negative integer or type stop: 7
Fib(7) = 13
Please enter another non-negative integer or type stop: 8
Fib(8) = 21
Please enter another non-negative integer or type stop: 9
Fib(9) = 34
Please enter another non-negative integer or type stop: 10
Fib(10) = 55
Please enter another non-negative integer or type stop: 11
Fib(11) = 89
Please enter another non-negative integer or type stop: 12
Fib(12) = 144
Please enter another non-negative integer or type stop: stop

Hint:

  1. First write a function fibo(n_int) that finds the requested Fibonacci number for you, given a non-negative integer input (for example, name it n_int).
  2. Then put this function inside another Python function fib(n) that checks the type of the input argument n and prints the appropriate error message as in the above and then asks the user to enter another number (and then again checks for its type to be integer).
  3. Then if this number is an integer, this function fib(n) passes the integer number n to the function fibo(n_int) which is inside of itself (it is a nested function), in order to get the requested Fibonacci number.
  4. Finally, once the requested Fibonaccy number is obtained, it prints the number value with the requested format as in the above example, AND then asks again the user to input a new non-negative integer, or simply type stop to stop the function.

Note that, if you call the function as fib('stop') in the Python interpreter, it should return nothing to you, just like the following example,

fib('stop')

I highly recommend you to write your function in Jupyter notebook, test it there, and then get the results for the same input arguments as in the above example (a string, negative integer, float, and n=1,…,12, and also ‘stop’) and download all of the notebook as a Markdown file, and put it in your repository folder for this homework. Name the notebook, fib.md.

Also note that, you don’t need to know or use anything beyond Python function syntax, Python built-in functions and methods (like input, isdigit(), print, str(), int(), …), and Python if-blocks.

I recommend you to use Jupyter notebook on your device, since the online version of notebook, can be interrupted repeatedly because of internet connection.


Answer:

Here is an implementation in Python:

def fib(n):

    def fibo(n_int):
        if n_int==0: return 0
        elif n_int==1: return 1
        else:
            return fibo(n_int-1) + fibo(n_int-2)

    if n=='stop':
        return None
    elif str(n).isdigit():    # Make sure n is integer. Here is a bug: all positive numbers are assumed to not precede with + sign.
        n=int(n) 
        print('Fib({}) = {}'.format(n,fibo(n)))
        n = input("Please enter another integer or type stop: ")  # Note that n is read as string!
        return fib(n)
    else: # the input is not an integer
        print( 'The input argument {} is not a non-negative integer!'.format(n) )    
        n = input("Please enter an integer: ")  # Note that n is read as string!
        return fib(n)

Here is an implementation in MATLAB:

function fib()

    % get the input 
    n = input('Please enter a non-negative integer or type stop: ','s');
    if strcmp(n,'stop')
        return
    else
        n = str2double(n);
        if isreal(n) && n>=0 && round(n)==n
            disp( [ 'fib(' , num2str(n) , ') = ' , num2str(getFib(n)) ] );
            fib()
            return
        end
        disp('The input argument is not a non-negative integer!');
        fib()
    end

    function fib = getFib(n_int)
        if n_int == 0
            fib = 0;
        elseif n_int == 1
            fib = 1;
        else
            fib = getFib(n_int-1) + getFib(n_int-2);
        end
    end

end





2.  Python. Branching…, the Pythonic way.

(A) Change the first if statement in the following script to an equivalent one-line if-expression. Test the resulting new script, and make sure it behaves as the original,

#!/usr/bin/env python

abbr = input ("What is the four-letter abbreviation for the National Aeronautics and Space Administration? ")

answer_status = 'wrong'
if abbr == 'NASA':
    answer_status = 'correct'

if answer_status=='correct':
    print('Your answer is correct!')
else:
    print("wrong buddy...try again")

(B) Can you acheive the same goal as in (A) without if-expression or block, but instead using only tuple notation? Explain why your solution works.

Modify the if block and the print statements that are only in the last part of the code,

if answer_status=='correct':
    print('Your answer is correct!')
else:
    print("wrong buddy...try again")

to write a single-line Python statement that only uses print and tuple or list notations, to perform the exact same task as the original print and if-block statement.

Answer:

(A)

#!/usr/bin/env python

abbr = input ("What is the four-letter abbreviation for the National Aeronautics and Space Administration? ")

answer_status = 'correct' if abbr == 'NASA' else 'wrong'

if answer_status=='correct':
    print('Your answer is correct!')
else:
    print("wrong buddy...try again")

(B)

#!/usr/bin/env python

abbr = input ("What is the four-letter abbreviation for the National Aeronautics and Space Administration? ")

answer_status = ('wrong','correct')[abbr=='NASA']

if answer_status=='correct':
    print('Your answer is correct!')
else:
    print("wrong buddy...try again")

(C)

#!/usr/bin/env python

abbr = input ("What is the four-letter abbreviation for the National Aeronautics and Space Administration? ")

answer_status = ('wrong','correct')[abbr=='NASA']

print( ('wrong buddy...try again','Your answer is correct!')[answer_status=='correct'] )





3.  Python.

(A) Write a single-line Python code that reads a string containing comma-separated first-name, last-name, and the city in which a person lives from the Python interpreter command line, and simultaneouly, in the same line of Python code, removes all white-space characters from the input string, and converts all letters of the input variables to lower-case, and converts the string to a tuple and saves in a tuple (first,last,city), such that, for example,

Enter the first name, last name, and the city of the person (comma-separated): Amir, Shahmoradi  ,  Austin

would give,

(first,last,city)
('amir', 'shahmoradi', 'austin')

Hint: Use input function for this purpose. The output of input is a string, which can be manipulated repeatedly on the same line, using multiple string methods that you learned about in the previous lectures.

(B) As discussed in our lecture notes, the one-line if-expression syntax does not provide a functionality like elif keyword as in the if-statement syntax. Our goal here is to learn how to convert a Python if-statement containing elif to a one-line Python expression. Convert the following if-block to a single line if-expression. Modify the if-block inside the following function to one-line if-expression:

def dummy(i):
    if i==0:
        j=0
    elif i==1:
        j=1
    elif i==2:
        j=2
    else: j = 'j is not in [0,1,2]' 
    return j

Answer:

(A)

(first,last,univ) = ((input('Enter the first name, last name, and the city of the person (comma-separated): ').replace(' ','')).lower()).split(',')

(B)

def dummy(i):
    j = 0 if i==0 else (1 if i==1 else (2 if i==2 else 'j is not in [0,1,2]') )
    return j





4.  An arbitrary triangle can be described by the coordinates of its three vertices: $(x1,y1),(x2,y2),(x3,y3)$, numbered in a counterclockwise direction. The area of the triangle is given by the formula,

Write a function get_triangle_area(vertices) that returns the area of a triangle whose vertices are specified by the argument vertices, which is a nested list of the vertex coordinates. Test your implementation with the following test function, which also illustrates how the get_triangle_area function works.

def test_get_triangle_area():
    """
    Verify the area of a triangle with vertex coordinates
    (0,0), (1,0), and (0,2).
    """
    v1 = (0,0); v2 = (1,0); v3 = (0,2)
    vertices = [v1, v2, v3]
    expected = 1
    computed = get_triangle_area(vertices)
    tol = 1E-14
    success = abs(expected - computed) < tol
    msg = 'computed area=%g != %g (expected)' % (computed, expected)
    assert success, msg

Answer:

Here is a Python implementation of the solution:

# getTriangleArea.py
def get_triangle_area(vert):
    area = 0.5 * abs(vert[1][0] * vert[2][1] - vert[2][0] * vert[1][1] -
                     vert[0][0] * vert[2][1] + vert[2][0] * vert[0][1] +
                     vert[0][0] * vert[1][1] - vert[1][0] * vert[0][1])
    return area

Here is a MATLAB implementation of the solution:

% getTriangleArea.m
function area = getTriangleArea(vertices)
    area = 0.5 * abs( vertices{2}(1) * vertices{3}(2) ...
                    - vertices{3}(1) * vertices{2}(2) ...
                    - vertices{1}(1) * vertices{3}(2) ...
                    + vertices{3}(1) * vertices{1}(2) ...
                    + vertices{1}(1) * vertices{2}(2) ...
                    + vertices{2}(1) * vertices{1}(2) ...
                    )
end





5.  Write a logical (boolean) function named is_prime(n) that takes in an integer number n, and finds whether it is a Prime number or not. Example output is the following,

is_prime(n=23)
True
is_prime(12)
False

Note that you do not need and don’t have to use for-loop for this problem (we have not yet discussed loops in our class!). All of it can be done using recursive function concept.

Answer:

Here is an implementation in Python:

def is_prime(n):
    
    is_prime = True
    
    def is_divisible(n,divisor):
        if n<(divisor-1)*divisor: return False
        if n%divisor==0: return True
        else:
            divisor += 1
            return is_divisible(n,divisor)

    if is_divisible(n,divisor=2): is_prime=False
    return is_prime

Here is an implementation in MATLAB:

function outLogical = isPrime(n)

    divisor = 2;
    outLogical = true;
    sqrt_n = round(sqrt(n));
    outLogical = isDivisible(n,sqrt_n,divisor);

    function output = isDivisible(n,sqrt_n,divisor)
        if mod(n,divisor) == 0
            output = false;
        elseif sqrt_n<divisor
            output = true;
            return
        else
            divisor = divisor + 1;
            output = isDivisible(n,sqrt_n,divisor);
        end
    end

end





6.  MATLAB Function generators. Write a nested function that evaluates a polynomial of the form $y = ax^2+bx+c$. The host function genFunc() should be able to take varying number of arguments using varargin with maximum of 3 arguments (a,b,c) to initialize the coefficients of the polynomial. If there is only one argument, then b and c must be set to zero. If there are two input arguments, then c is set to zero. If none are given on input, then the returned function should be zero. If more than 3 arguments exist, then the function should display an error and stop. Also, if the input arguments are not real numbers, then the function should return an error and stop.

On output, the host function should create and return a function handle for the nested function evalFunc(). The nested function should calculate a value of $y$ for a given value of $x$, using the values of $a$, $b$, and $c$ stored in the host function. This is called a function generator, since the host function generates and outputs another function that can be called and used later on in the program. Once you create your function generator, test it in the following way: Call genFunc(1,2,0) and save the output function handle in a variable, say h1. Call genFunc(1,2) and save the output function handle in a variable, say h2. Then these two function handles, should give the same result, given the same input x values.

Answer:

Here is an example implementation of the function:

function [outputQuadraticFuncHandle] = genFunc(varargin)

    switch nargin
        case 0
            a=0;
            b=0;
            c=0;
        case 1
            a=varargin{1};
            b=0;
            c=0;
        case 2
            a=varargin{1};
            b=varargin{2};
            c=0;
        case 3
            a=varargin{1};
            b=varargin{2};
            c=varargin{3};
        otherwise
            error('Too many arguments')   
    end

    function y = evalQuadFunc(x)
        y = a*x.^2 + b*x + c;
    end
    outputQuadFuncHandle = @evalQuadFunc;

end





7.  Consider the following nested list in Python,

q = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h']]

or its equivalent in MATLAB,

q = {{'a', 'b', 'c'}, {'d', 'e', 'f'}, {'g', 'h'}}

Write a for-loop that extracts all the letters in the list and finally prints them all as a single string,

abcdefgh

Answer:
Here is a solution in Python:

q = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h']]
s = ''
for i in q:
    for j in range(len(i)):
        s += i[j]
print(s)
abcdefgh





8.  The significant impact of round-off errors in numerical computation. Consider the following program in Python,

from math import sqrt
for n in range(1, 60):
    r_org = 2.0
    r = r_org
    for i in range(n):
        r = sqrt(r)
    for i in range(n):
        r = r ** 2
    print ('With {} times sqrt and then {} times **2, the number {} becomes: {:.16f}'.format(n,n,r_org,r))

or its equivalent in MATLAB,

formatSpec = 'With %d sqrt, then %d times ^2 operations, the number %.16f becomes: %.16f \n'; % the string format for fprintf function
for n = 1:60
    r_original = 2.0;
    r = r_original;
    for i = 1:n
        r = sqrt(r);
    end
    for i = 1:n
        r = r^2;
    end
    fprintf(formatSpec,n,n,r_original,r);
end

Explain what this code does. Then run the code, and explain why do you the behavior observed. In particular, why do you not recover the original value $2$ after many repetitions of the same forward and reverse task?

Answer:
This code will yield the following output in Python:

from math import sqrt
for n in range(1, 60):
    r_org = 2.0
    r = r_org
    for i in range(n):
        r = sqrt(r)
    for i in range(n):
        r = r ** 2
    print ('With {} times sqrt and then {} times **2, the number {} becomes: {:.16f}'.format(n,n,r_org,r))
With 1 times sqrt and then 1 times **2, the number 2.0 becomes: 2.0000000000000004
With 2 times sqrt and then 2 times **2, the number 2.0 becomes: 1.9999999999999996
With 3 times sqrt and then 3 times **2, the number 2.0 becomes: 1.9999999999999996
With 4 times sqrt and then 4 times **2, the number 2.0 becomes: 1.9999999999999964
With 5 times sqrt and then 5 times **2, the number 2.0 becomes: 1.9999999999999964
With 6 times sqrt and then 6 times **2, the number 2.0 becomes: 1.9999999999999964
With 7 times sqrt and then 7 times **2, the number 2.0 becomes: 1.9999999999999714
With 8 times sqrt and then 8 times **2, the number 2.0 becomes: 2.0000000000000235
With 9 times sqrt and then 9 times **2, the number 2.0 becomes: 2.0000000000000235
With 10 times sqrt and then 10 times **2, the number 2.0 becomes: 2.0000000000000235
With 11 times sqrt and then 11 times **2, the number 2.0 becomes: 2.0000000000000235
With 12 times sqrt and then 12 times **2, the number 2.0 becomes: 1.9999999999991336
With 13 times sqrt and then 13 times **2, the number 2.0 becomes: 1.9999999999973292
With 14 times sqrt and then 14 times **2, the number 2.0 becomes: 1.9999999999973292
With 15 times sqrt and then 15 times **2, the number 2.0 becomes: 1.9999999999973292
With 16 times sqrt and then 16 times **2, the number 2.0 becomes: 2.0000000000117746
With 17 times sqrt and then 17 times **2, the number 2.0 becomes: 2.0000000000408580
With 18 times sqrt and then 18 times **2, the number 2.0 becomes: 2.0000000000408580
With 19 times sqrt and then 19 times **2, the number 2.0 becomes: 2.0000000001573586
With 20 times sqrt and then 20 times **2, the number 2.0 becomes: 2.0000000001573586
With 21 times sqrt and then 21 times **2, the number 2.0 becomes: 2.0000000001573586
With 22 times sqrt and then 22 times **2, the number 2.0 becomes: 2.0000000010885857
With 23 times sqrt and then 23 times **2, the number 2.0 becomes: 2.0000000029511749
With 24 times sqrt and then 24 times **2, the number 2.0 becomes: 2.0000000066771721
With 25 times sqrt and then 25 times **2, the number 2.0 becomes: 2.0000000066771721
With 26 times sqrt and then 26 times **2, the number 2.0 becomes: 1.9999999917775542
With 27 times sqrt and then 27 times **2, the number 2.0 becomes: 1.9999999917775542
With 28 times sqrt and then 28 times **2, the number 2.0 becomes: 1.9999999917775542
With 29 times sqrt and then 29 times **2, the number 2.0 becomes: 1.9999999917775542
With 30 times sqrt and then 30 times **2, the number 2.0 becomes: 1.9999999917775542
With 31 times sqrt and then 31 times **2, the number 2.0 becomes: 1.9999999917775542
With 32 times sqrt and then 32 times **2, the number 2.0 becomes: 1.9999990380770896
With 33 times sqrt and then 33 times **2, the number 2.0 becomes: 1.9999971307544144
With 34 times sqrt and then 34 times **2, the number 2.0 becomes: 1.9999971307544144
With 35 times sqrt and then 35 times **2, the number 2.0 becomes: 1.9999971307544144
With 36 times sqrt and then 36 times **2, the number 2.0 becomes: 1.9999971307544144
With 37 times sqrt and then 37 times **2, the number 2.0 becomes: 1.9999971307544144
With 38 times sqrt and then 38 times **2, the number 2.0 becomes: 1.9999360966436217
With 39 times sqrt and then 39 times **2, the number 2.0 becomes: 1.9999360966436217
With 40 times sqrt and then 40 times **2, the number 2.0 becomes: 1.9999360966436217
With 41 times sqrt and then 41 times **2, the number 2.0 becomes: 1.9994478907329654
With 42 times sqrt and then 42 times **2, the number 2.0 becomes: 1.9984718365144798
With 43 times sqrt and then 43 times **2, the number 2.0 becomes: 1.9965211562778555
With 44 times sqrt and then 44 times **2, the number 2.0 becomes: 1.9965211562778555
With 45 times sqrt and then 45 times **2, the number 2.0 becomes: 1.9887374575497223
With 46 times sqrt and then 46 times **2, the number 2.0 becomes: 1.9887374575497223
With 47 times sqrt and then 47 times **2, the number 2.0 becomes: 1.9887374575497223
With 48 times sqrt and then 48 times **2, the number 2.0 becomes: 1.9887374575497223
With 49 times sqrt and then 49 times **2, the number 2.0 becomes: 1.8682459487159784
With 50 times sqrt and then 50 times **2, the number 2.0 becomes: 1.6487212645509468
With 51 times sqrt and then 51 times **2, the number 2.0 becomes: 1.6487212645509468
With 52 times sqrt and then 52 times **2, the number 2.0 becomes: 1.0000000000000000
With 53 times sqrt and then 53 times **2, the number 2.0 becomes: 1.0000000000000000
With 54 times sqrt and then 54 times **2, the number 2.0 becomes: 1.0000000000000000
With 55 times sqrt and then 55 times **2, the number 2.0 becomes: 1.0000000000000000
With 56 times sqrt and then 56 times **2, the number 2.0 becomes: 1.0000000000000000
With 57 times sqrt and then 57 times **2, the number 2.0 becomes: 1.0000000000000000
With 58 times sqrt and then 58 times **2, the number 2.0 becomes: 1.0000000000000000
With 59 times sqrt and then 59 times **2, the number 2.0 becomes: 1.0000000000000000

and this code will yield the following output in MATLAB,

formatSpec = 'With %d sqrt, then %d times ^2 operations, the number %.16f becomes: %.16f \n'; % the string format for fprintf function
for n = 1:60
    r_original = 2.0;
    r = r_original;
    for i = 1:n
        r = sqrt(r);
    end
    for i = 1:n
        r = r^2;
    end
    fprintf(formatSpec,n,n,r_original,r);
end
>> roundoff
With 1 sqrt, then 1 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000000004 
With 2 sqrt, then 2 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999996 
With 3 sqrt, then 3 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999996 
With 4 sqrt, then 4 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999964 
With 5 sqrt, then 5 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999964 
With 6 sqrt, then 6 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999964 
With 7 sqrt, then 7 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999999714 
With 8 sqrt, then 8 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000000235 
With 9 sqrt, then 9 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000000235 
With 10 sqrt, then 10 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000000235 
With 11 sqrt, then 11 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000000235 
With 12 sqrt, then 12 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999991336 
With 13 sqrt, then 13 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999973292 
With 14 sqrt, then 14 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999973292 
With 15 sqrt, then 15 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999999973292 
With 16 sqrt, then 16 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000117746 
With 17 sqrt, then 17 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000408580 
With 18 sqrt, then 18 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000000408580 
With 19 sqrt, then 19 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000001573586 
With 20 sqrt, then 20 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000001573586 
With 21 sqrt, then 21 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000001573586 
With 22 sqrt, then 22 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000010885857 
With 23 sqrt, then 23 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000029511749 
With 24 sqrt, then 24 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000066771721 
With 25 sqrt, then 25 times ^2 operations, the number 2.0000000000000000 becomes: 2.0000000066771721 
With 26 sqrt, then 26 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 27 sqrt, then 27 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 28 sqrt, then 28 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 29 sqrt, then 29 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 30 sqrt, then 30 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 31 sqrt, then 31 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999999917775542 
With 32 sqrt, then 32 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999990380770896 
With 33 sqrt, then 33 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999971307544144 
With 34 sqrt, then 34 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999971307544144 
With 35 sqrt, then 35 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999971307544144 
With 36 sqrt, then 36 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999971307544144 
With 37 sqrt, then 37 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999971307544144 
With 38 sqrt, then 38 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999360966436217 
With 39 sqrt, then 39 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999360966436217 
With 40 sqrt, then 40 times ^2 operations, the number 2.0000000000000000 becomes: 1.9999360966436217 
With 41 sqrt, then 41 times ^2 operations, the number 2.0000000000000000 becomes: 1.9994478907329654 
With 42 sqrt, then 42 times ^2 operations, the number 2.0000000000000000 becomes: 1.9984718365144798 
With 43 sqrt, then 43 times ^2 operations, the number 2.0000000000000000 becomes: 1.9965211562778555 
With 44 sqrt, then 44 times ^2 operations, the number 2.0000000000000000 becomes: 1.9965211562778555 
With 45 sqrt, then 45 times ^2 operations, the number 2.0000000000000000 becomes: 1.9887374575497223 
With 46 sqrt, then 46 times ^2 operations, the number 2.0000000000000000 becomes: 1.9887374575497223 
With 47 sqrt, then 47 times ^2 operations, the number 2.0000000000000000 becomes: 1.9887374575497223 
With 48 sqrt, then 48 times ^2 operations, the number 2.0000000000000000 becomes: 1.9887374575497223 
With 49 sqrt, then 49 times ^2 operations, the number 2.0000000000000000 becomes: 1.8682459487159784 
With 50 sqrt, then 50 times ^2 operations, the number 2.0000000000000000 becomes: 1.6487212645509468 
With 51 sqrt, then 51 times ^2 operations, the number 2.0000000000000000 becomes: 1.6487212645509468 
With 52 sqrt, then 52 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 53 sqrt, then 53 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 54 sqrt, then 54 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 55 sqrt, then 55 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 56 sqrt, then 56 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 57 sqrt, then 57 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 58 sqrt, then 58 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 59 sqrt, then 59 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 
With 60 sqrt, then 60 times ^2 operations, the number 2.0000000000000000 becomes: 1.0000000000000000 

What is happening is that, 1 is returned for n >= 52 as square root of 2, that is, after 52 times square-root operation, the degree of accuracy required for representing the result goes beyond the degree of accuracy available in a MATLAB double precision number. Consequently, the later squaring operation on 1.00000000000000 will leave the number unchanged and therefore, 2 is not recovered.





9.  Consider the following code,

eps = 1.0
while 1.0 != 1.0 + eps:
    print ('...............', eps)
    eps /= 2.0
print ('final eps:', eps)

or its equivalent in MATLAB,

eps = 1.0;
while 1.0 ~= 1.0 + eps
    disp(num2str(eps));
    eps = eps / 2.0;
end
disp(['final eps:', num2str(eps)]);

Explain what the code is doing. Run the code and observe the output. How could 1.0 != 1.0 + eps be False?

Answer:
Here is the output of the code,

............... 1.0
............... 0.5
............... 0.25
............... 0.125
............... 0.0625
............... 0.03125
............... 0.015625
............... 0.0078125
............... 0.00390625
............... 0.001953125
............... 0.0009765625
............... 0.00048828125
............... 0.000244140625
............... 0.0001220703125
............... 6.103515625e-05
............... 3.0517578125e-05
............... 1.52587890625e-05
............... 7.62939453125e-06
............... 3.814697265625e-06
............... 1.9073486328125e-06
............... 9.5367431640625e-07
............... 4.76837158203125e-07
............... 2.384185791015625e-07
............... 1.1920928955078125e-07
............... 5.960464477539063e-08
............... 2.9802322387695312e-08
............... 1.4901161193847656e-08
............... 7.450580596923828e-09
............... 3.725290298461914e-09
............... 1.862645149230957e-09
............... 9.313225746154785e-10
............... 4.656612873077393e-10
............... 2.3283064365386963e-10
............... 1.1641532182693481e-10
............... 5.820766091346741e-11
............... 2.9103830456733704e-11
............... 1.4551915228366852e-11
............... 7.275957614183426e-12
............... 3.637978807091713e-12
............... 1.8189894035458565e-12
............... 9.094947017729282e-13
............... 4.547473508864641e-13
............... 2.2737367544323206e-13
............... 1.1368683772161603e-13
............... 5.684341886080802e-14
............... 2.842170943040401e-14
............... 1.4210854715202004e-14
............... 7.105427357601002e-15
............... 3.552713678800501e-15
............... 1.7763568394002505e-15
............... 8.881784197001252e-16
............... 4.440892098500626e-16
............... 2.220446049250313e-16
final eps: 1.1102230246251565e-16

What is happening is that after a certain number of divisions performed on the value of eps, the value goes beyond the highest float precision representable by Python standard ($0.0000000000000001$), and therefore the value of eps is eventually rounded to exact zero. The nonzero eps value computed above is called machine epsilon or machine zero and is an important parameter to know, since it can lead to disasters in your very important complex calculations.





10.  MATLAB. Suppose you want to find the largest prime number that is smaller than a given input value by the user. Write a function named getLargestPrime that does so, using for-loop, break, and MATLAB’s intrinsic function isprime(). Here is a test,

>> getLargestPrime(123)
ans =
   113

Answer:

function integer = getLargestPrime(upper)
    if (upper<1)
        disp('input value cannot be less than 1. Goodbye!')
        return
    end
    for integer = upper:-1:1
        if isprime(integer)
            break
        end
    end
end





11.  Python Consider the following list,

numbers = list(range(10))
print(numbers)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Now run the following code, given the above list. Explain the weird behavior that you observe.

for n in numbers:
    i = len(numbers)//2
    del numbers[i]
    print ('n={}, del {}'.format(n,i), numbers)

Answer:

numbers = list(range(10))
for n in numbers:
    i = len(numbers)//2
    del numbers[i]
    print ('n={}, del {}'.format(n,i), numbers)
n=0, del 5 [0, 1, 2, 3, 4, 6, 7, 8, 9]
n=1, del 4 [0, 1, 2, 3, 6, 7, 8, 9]
n=2, del 4 [0, 1, 2, 3, 7, 8, 9]
n=3, del 3 [0, 1, 2, 7, 8, 9]
n=8, del 3 [0, 1, 2, 8, 9]

What is really happening is that the list over which we are looping changes its content because of the modifications during on the list in the for-loop. The message in this exercise is to never modify a list that you are looping over. Modification is indeed technically possible, as shown above, but you really need to know what you are doing. Otherwise you will experience very strange program behavior.





12.  Python Write a Python function that when executed, asks the user to enter an integer number, then the function count and outputs the number of prime numbers that are smaller than the given input integer number. Here is the answer to this question using only the knowledge of recursive functions and if-blocks,

def is_prime(n):
    
    is_prime = True
    
    def is_divisible(n,divisor):
        if n<(divisor-1)*divisor: return False
        if n%divisor==0: return True
        else:
            divisor += 1
            return is_divisible(n,divisor)

    if is_divisible(n,divisor=2): is_prime=False
    return is_prime

def get_primes(n):
    count = 0
    if n == 1:
        return count
    else:
        if is_prime(n):
            count = 1
        n -= 1
        return count + get_primes(n)
get_primes(13)
5

(A) Now rewrite get_primes(n) and the other functions in the above code using for-loop this time. Name the new functions get_prime_for(n) and is_prime_for(n), with for in the names indicating that the functions now use for-loops.

Answer:

def is_prime_for(x):
    if x > 1:
        n = x // 2
        for i in range(2, n + 1):
            if x % i == 0:
                return False
        return True
    else:
        return False

def get_primes_for(n):
    count = 0
    for i in range(2,n):
        if is_prime(i):
            count += 1
    return count

Here is a test,

get_primes_for(13)
5

(B) Now compare the performance of the two functions get_primes(n=500) and get_primes_for(n500) using Jupyter’s or IPython’s %timeit magic function. Which one is faster?

Answer:

%timeit get_primes(500)
1000 loops, best of 3: 1.32 ms per loop
%timeit get_primes_for(500)
1000 loops, best of 3: 1.69 ms per loop  

Interesting, recursive functions seem to be faster than Python for-loops!

Comments