Homework 2 (Due 10/11/2024 at 11:59pm)

Contents

Homework 2 (Due 10/11/2024 at 11:59pm)#

Name:#

ID:#

Submission instruction:

  • Download the file as .ipynb (see top right corner on the webpage).

  • Write your name and ID in the field above.

  • Answer the questions in the .ipynb file in either markdown or code cells.

  • Before submission, make sure to rerun all cells by clicking Kernel -> Restart & Run All and check all the outputs.

  • Upload the .ipynb file to Gradescope.

Q1 Fibonacci sequence

(1). Create a markdown cell below, write down the recurrence relation for the Fibonacci sequence

\(F_0 = 0\), \(F_1 = 1\), \(F_n = F_{n-1} + F_{n-2}\) for \(n >= 2\)

(2) Write a function, myfibo(n), using a loop, that returns the n-th Fibonacci number. Show your solution is correct by output the first 10 Fibonacci numbers.

# code here

def myfibo(n):
    if n == 0:
        return 0
    if n == 1:
        return 1
    else:
        a = 0
        b = 1
        for i in range(1, n):
            c = a + b
            a = b
            b = c
        return c

fibo_nums = [myfibo(i) for i in range(10)]
print(fibo_nums)

(3) Write a function, myfibo_rec(n), using recursion, that returns the n-th Fibonacci number.

# code here
def myfibo_rec(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return myfibo_rec(n-1) + myfibo_rec(n-2)

fibo_nums = [myfibo_rec(i) for i in range(10)]
print(fibo_nums)

(4) [Extra, not graded] Compare the time it takes to run myfibo(20) and myfibo_rec(20). Which one is faster? Why?

%%timeit
# run the following code to check the time, %%timeit will run the code multiple times and give you the average time it took to run the code 
myfibo(20)
%%timeit 
myfibo_rec(20)

Answer: myfibo is faster. myfibo_rec is slower because it makes many redundant calculations: for example, myfibo_rec(5) = myfibo_rec(4) + myfibo_rec(3), but myfibo_rec(4) = myfibo_rec(3) + myfibo_rec(2), notice that myfibo_rec(3) is calculated twice.

Q2 Working with list

(1) Define a function my_cumulative_sum(x), which take a list of numbers x, and modify x, so that the modified list is the list of cumulative sum of the original list:

If the original list is [\(x_0\), \(x_1\), \(x_2\), …], then the modified list is [\(x_0\), \(x_0+x_1\), \(x_0+x_1+x_2\), …].

Your function should only use a single for-loop.

For example

x = [1,1,1]

my_cumulative_sum(x)

print(x)

produce [1,2,3]

Test your function with input [1,2,3,4]

# code here
def my_cumulative_sum(x):
    for i in range(1, len(x)):
        x[i] = x[i] + x[i-1]

x = [1,2,3,4]
my_cumulative_sum(x)
print(x)

(2)

Generate a list of numbers between 1 and 100 that exclude (1) multiples of 7 and (2) numbers that ends with 7

For example, 7, 14, 17, 21, 27, 28, .. are excluded.

Do not use “outer for-loop”. you can use list comprehension.

Hint: you might need to convert between integer and string.

Hint: you can define a helper function.

# code here

def should_exclude(n):
    if n % 7 == 0:
        return True
    str_n = str(n)
    if str_n[-1] == '7':
        return True

    return False

x = [i for i in range(1, 101) if not should_exclude(i)]
print(x)

(3)

Sort the previous list in “increasing order”, in term of the absolute distance to the number 50. No need to break tie. For example, if the list is [48,50,51], the sorted list should be [50,51,48]. Do it in one-line.

Hint: use list comprehension, and the built-in function sorted or sort, and lambda function.

# Code here
sorted(x, key=lambda x: abs(x-50))

Q3 Implement a Rectangle class with the following attributes and methods:

  • Attributes: width, height

  • Methods:

    • __init__(self, width, height): initialize the width and height of the rectangle

    • area(self): return the area of the rectangle

    • smaller_than(self, rec): return True if the area of the rectangle is smaller than the area of another rectange object rec, False otherwise

    • resize(self, c): resize the rectangle, where c is a scaling factor. For example, if c=2, the width and height of the rectangle will be doubled. If c=0.5, the width and height of the rectangle will be halved.

    • If rec is a Rectangle object, print(rec) should print the (width,height) of the rectangle.

    • (Challenge, not graded) overload the ‘<’ operator so that rec1 < rec2 returns True if the area of rec1 is less than the area of rec2, False otherwise.

(1) Define your class here

# code here

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def smaller_than(self, other):
        return self.area() < other.area()   
    
    def __repr__(self):
        return f"({self.width}, {self.height})"

    def resize(self, c):
        self.width = self.width * c
        self.height = self.height * c
    
    def __lt__(self, other):
        return self.area() < other.area()

(2) Use examples to demonstrate that the methods in your class work as expected.

# code here

r1 = Rectangle(1, 2)
r2 = Rectangle(2, 3)

# should print 2
print(r1.area())

# should print (1, 2)
print(r1)

# should print True
print(r1.smaller_than(r2))

r1.resize(2)
print(r1.area()) # should print 8

# should print False
print(r1.smaller_than(r2))

# should print True
print(r2 < r1)

Q4 [Extra, not graded] Two sum problem

The problem is from Leetcode, which is a website for practicing coding problems.

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. You may assume that each input would have exactly one solution, and you may not use the same element twice. You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9

Output: [0,1]

Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6

Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6

Output: [0,1]

def twoSumDict(nums,target):
    dict_num_idx = {} # dictionary {num : index}
    for i in range(len(nums)):
        # nums[i] is my current num
        # we want the index of the complement
        complement = target - nums[i]
        
        # check if complement is already in my dictionary
        if complement in dict_num_idx:
            # already there, then get the index
            other_idx = dict_num_idx[complement]
            return [i, other_idx]
        else:
            # if not there, we keep in our dictionary
            # might use it in the future
            dict_num_idx[nums[i]] = i

twoSumDict([2,7,11,15], 9) # should return [0, 1]