The purpose of this notebook is to collect all the finger exercises I did from Introduction to Computation and Programming Using Python, third edition, With Application to Computational Modeling and Understanding Data by John V. Guttag.
(Btw it is not complete, I didn’t do for example some very basic finger exercises toward the beginning of the book.)
Chapter 3: Some Simple Numerical Programs
3.1 Exhaustive Enumeration
Write a program that asks the user to enter an integer and prints two integers, root and pwr, such that 1 < pwr < 6 and root**pwr is equal to the integer entered by the user. If no such pair of integers exists, it should print a message to that effect.
x =int(input("Enter an integer: "))answer_root, answer_pwr =None, Noneif x ==0: answer_root, answer_pwr =0, 2else:if x <0:for pwr inrange(3, 6, 2):for root inrange(abs(x) +1): root *=-1if root**pwr == x: answer_root, answer_pwr = root, pwrbreakelse:for pwr inrange(2, 6):for root inrange(abs(x) +1):if root**pwr == x: answer_root, answer_pwr = root, pwrbreakif answer_root isnotNone:print(f"For {x}, root is {answer_root} and power is {answer_pwr}")else:print("No such pair exists.")
For -8, root is -2 and power is 3
Write a program that prints the sum of the prime numbers greater than 2 and less than 1000. Hint: you probably want to have a loop that is a primality test nested inside a loop that iterates over the odd integers between 3 and 999.
x =0for i inrange(3, 1000, 2): is_prime =Truefor j inrange(2, i):if i % j ==0: is_prime =Falsebreakif is_prime: x += iprint(x)
76125
3.2 Approximate Solutions and Bisection Search
What would have to be changed to make the code in Figure 3-5 work for finding an approximation to the cube root of both negative and positive numbers? Hint: think about changing low to ensure that the answer lies within the region being searched.
x =float(input("Enter a number: "))epsilon =0.01low =min(x, -1)high =max(1, x)ans = (high + low) /2num_guesses =1print(f"{low =}, {high =}, {ans =}")whileabs(ans**3- x) >= epsilon: num_guesses +=1if ans**3< x: low = anselse: high = ans ans = (high + low) /2print(f"{low =}, {high =}, {ans =}")print(f"{num_guesses =}")print(ans, "is close to cube root of", x)
low = -27.0, high = 1, ans = -13.0
low = -13.0, high = 1, ans = -6.0
low = -6.0, high = 1, ans = -2.5
low = -6.0, high = -2.5, ans = -4.25
low = -4.25, high = -2.5, ans = -3.375
low = -3.375, high = -2.5, ans = -2.9375
low = -3.375, high = -2.9375, ans = -3.15625
low = -3.15625, high = -2.9375, ans = -3.046875
low = -3.046875, high = -2.9375, ans = -2.9921875
low = -3.046875, high = -2.9921875, ans = -3.01953125
low = -3.01953125, high = -2.9921875, ans = -3.005859375
low = -3.005859375, high = -2.9921875, ans = -2.9990234375
low = -3.005859375, high = -2.9990234375, ans = -3.00244140625
low = -3.00244140625, high = -2.9990234375, ans = -3.000732421875
low = -3.000732421875, high = -2.9990234375, ans = -2.9998779296875
num_guesses = 15
-2.9998779296875 is close to cube root of -27.0
Figure 3-6 Using bisection search to estimate log base 2. (This is not a finger exercise, but I thought Figure 3-6 was wrong in that it doesn’t work for 0 < x < 1 and it is inefficient for huge x, so I wanted to improve on it. - I was right about 0 < x < 1, but it turned out for huge x it was actually efficient. It is because 2**ans very quickly reaches overflow error, so we should be very careful about low and high to ensure they are as tight as possible to prevent the overflow error.)
y =float(input("Enter a positive number: "))epsilon =0.01lower_bound =0if y <1: x =1/ yelse: x = ywhile2**lower_bound < x: lower_bound +=1low = lower_bound -1high = lower_bound +1ans = (high + low) /2print(f"{low =}, {high =}, {ans =}")whileabs(2**ans - x) >= epsilon:if2**ans < x: low = anselse: high = ans ans = (high + low) /2print(f"{low =}, {high =}, {ans =}")if y <1:print(-ans, "is close to the log base 2 of", y)else:print(ans, "is close to the log base 2 of", y)
low = 2, high = 4, ans = 3.0
-3.0 is close to the log base 2 of 0.125
3.4 Newton-Raphson
Add some code to the implementation of Newton-Raphson (in Figure 3-7) that keeps track of the number of iterations used to find the root. Use that code as part of a program that compares the efficiency of Newton-Raphson and bisection search. (You should discover that Newton-Raphson is far more efficient). Note: I also changed the code to find the cube root.
k =float(input("Enter a number: "))epsilon =0.01guess = k /2num_guesses =1whileabs(guess**3- k) >= epsilon: num_guesses +=1 guess -= (guess**3- k) / (3* guess**2)print(f"{num_guesses =}")print("Cube root of", k, "is about", guess)
num_guesses = 8
Cube root of -27.0 is about -3.000000081210202
Chapter 4: Functions, Scoping, and Abstraction
Use find to implement a function satisfying the specifiaction
def find_last(s, sub):"""s and sub are non-empty strings Returns the index of the last occurence of sub in s. Returns None if sub does not occur in s"""if s.find(sub) ==-1:returnNoneelse:returnlen(s) -len(sub) - s[::-1].find(sub[::-1])
Chapter 5: Structured Types and Mutability
Using encoder and encrypt as models, implement the functions decoder and decrypt. Use them to decrypt the message
book ="In a village of La Mancha, the name of which I have no desire to call to mind, there lived not long since one of those gentlemen that keep a lance in the lance-rack, an old buckler, a lean hack, and a greyhound for coursing."cipher_text ="22*13*33*137*59*11*23*11*1*57*6*13*1*2*6*57*2*6*1*22*13*33*137*59*11*23*11*1*57*6*173*7*11"
gen_decode_keys =lambda book, cipher_text: { s: book[int(s)] for s inset(cipher_text.split("*"))}
decoder =lambda decode_keys, cipher_text: "".join( [decode_keys[s] for s in cipher_text.split("*")])
Note: We could decipher the text without creating the decrypt function using the call decoder(gen_decode_keys(book, cipher_text), cipher_text). I mean, we have eveything we need to decrypt the message before we implement the decrypt function. I guess the function decrypt is created for convenience, so that to decipher a message, we have a function with a straightforward usage: One parameter is the book and one parameter is the cipher text and we don’t have to care about anything else. I think that is to honor the abstraction. We shouldn’t care about how the function works (i.e. that it uses decoder and gen_decode_keys functions) as long as it simply takes the book, the cipher text and produces the decoded the message.