Write a Python code that calculates Fibonacci numbers
What happens if you calculate the 50th Fibonacci numbers? What is the running time of the above code?
Optimization technique used to speed up programs by caching the results of expensive function calls and returning them when the same inputs are encountered again.
Write a memoized version of fibonacci(n)
What's the time complexity?
In practice, there is a decorator that does all the work for you.
With the memoized function, the space complexity in O(n). Can you write an algorithm with O(1) space complexity?
What is dynamic programming?
Two ways:
What is the highest revenue we can get by cutting an n feet rod and selling the pieces?
Length | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
Price | 1 | 5 | 8 | 9 | 10 | 17 | 17 | 20 | 24 |
What would the runtime be if we brute-force this problem and try all rod-cutting combinations?
We can decompose our problem as consisting of a first piece of length i and a remainder of length n−i. Write a top down implementation without worrying about memoization.
What's the time complexity? What if we memoize?
Write a bottom-up version of cut_rod
What are the time and space complexities?
Fibonacci | Rod cutting | |
---|---|---|
Subproblems | ||
Relate | ||
Original | ||
Order | ||
Base cases | ||
Running time |
Given two sequences A and B, find the longest sequence L that's a subsequence of both A and B.
A LCS of "hieroglyphology" and "michaelangelo" is "hello".
Determine an LCS of
10010101010110110What is the time complexity of the naive brute-force algorithm?
C[i,j]
denotes the length of LCS of X[:i]
and X[:j]
C[0,j] = C[i, 0] = 0
Write a top-down implementation of LCS
T | H | E | I | R | ||
---|---|---|---|---|---|---|
0 | ||||||
H | ||||||
A | ||||||
B | ||||||
I | ||||||
T |
B | D | C | A | B | A | ||
---|---|---|---|---|---|---|---|
0 | |||||||
A | |||||||
B | |||||||
C | |||||||
B | |||||||
D | |||||||
A | |||||||
B |
Below is a bottom-up implementation of LCS. What's its time complexity?
We'll use parent pointers to relate a subproblem to its parent.
T | H | E | I | R | ||
---|---|---|---|---|---|---|
0 | ||||||
H | ||||||
A | ||||||
B | ||||||
I | ||||||
T |
Find the LCS using parent pointers.
B | D | C | A | B | A | ||
---|---|---|---|---|---|---|---|
0 | |||||||
A | |||||||
B | |||||||
C | |||||||
B | |||||||
D | |||||||
A | |||||||
B |
Change the implementation so it actually computes the LCS and not only its length.
Given a sequence A, find the longest subsequence L of A consisting of increasing numbers.
An LIS of "carbohydrate" would be "abort"
Longest increasing subsequence | |
---|---|
Subproblems | |
Relate | |
Original | |
Order | |
Base cases | |
Running time |
Implement the LIS algorithm in Python.
# | value | weight |
---|---|---|
1 | 1 | 1 |
2 | 6 | 2 |
3 | 18 | 5 |
4 | 22 | 6 |
5 | 28 | 7 |
If W=11, the optimal choice is to take items 3 and 4.
Knapsack | |
---|---|
Guess | |
Subproblems | |
Relate | |
Original | |
Order | |
Base cases | |
Running time |
Write a top-down implementation of the Knapsack problem
Use the idea of parent pointers to reconstruct the solution.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
{} | ||||||||||||
{1} | ||||||||||||
{1, 2} | ||||||||||||
{1, 2, 3} | ||||||||||||
{1, 2, 3, 4} | ||||||||||||
{1, 2, 3, 4, 5} |
# | value | weight |
---|---|---|
1 | 1 | 1 |
2 | 6 | 2 |
3 | 18 | 5 |
4 | 22 | 6 |
5 | 28 | 7 |
Write a bottom-up implementation of Knapsack. Use parent pointers to construct the original solution.
Storing W in memory requires O(log2W) bit. Knapsack is therefore exponential in the input size, but polynomial in the input. We say it's a pseudo-polynomial algorithm.