diff --git a/04-graph.md b/04-graph.md index fc22da6..574d3a6 100644 --- a/04-graph.md +++ b/04-graph.md @@ -98,6 +98,6 @@ b --> c 5. Finally, return the maximum distance found. **Complexity**: -- Time: O(E) where E is the number of edges in the graph. -- Space: O(N) where N is the number of nodes in the graph. +- Time: $O(E)$ where $E$ is the number of edges in the graph. +- Space: $O(N)$ where $N$ is the number of nodes in the graph. diff --git a/05-dynamic-programming.md b/05-dynamic-programming.md index 459f05d..cbcfda3 100644 --- a/05-dynamic-programming.md +++ b/05-dynamic-programming.md @@ -1,2 +1,36 @@ ## Dynamic Programming +### Fibonacci sequence problem + +**Statement:** Given a number ```n``` $\in\N$, find the its Fibonacci number. + +$$ +\begin{align*} +F_0 &= 0, \\ +F_1 &= 1, \\ +F_n &= F_{n-1} + F_{n-2} +\end{align*} +$$ + +**Approach**: +It is possible to solve this problem with brute force recursion. + +```python +def fib(n): + if n == 0: + return 0 + if n == 1: + return 1 + return fib(n-1) + fib(n-2) +``` + +Nevertheless this leads to repeated work. For example, the call to ```fib(3)``` will call ```fib(2)``` and ```fib(1)```, and the call to ```fib(2)``` will call ```fib(1)``` and ```fib(0)```. This means that the call to ```fib(1)``` is repeated. +Leading to a complexity of $O(2^n)$. + +A better approach is to use **dynamic programming** and store the results of the subproblems. + +**Complexity**: +- Time: $O(n)$ +- Space: $O(n)$ + + diff --git a/dynamic_programming/fib.py b/dynamic_programming/fib.py index e69de29..9487ef1 100644 --- a/dynamic_programming/fib.py +++ b/dynamic_programming/fib.py @@ -0,0 +1,32 @@ +""" +fib +Write a function fib that takes in a number argument, n, and returns +the n-th number of the Fibonacci sequence. + +The 0-th number of the sequence is 0. + +The 1-st number of the sequence is 1. + +To generate further numbers of the sequence, calculate the sum of +previous two numbers. + +Solve this recursively. +""" + +def fib(n): + calculated = dict() + calculated[0] = 0 + calculated[1] = 1 + return _fib(n, calculated) + + +def _fib(n, calculated): + + if n in calculated: + return calculated[n] + + calculated[n] = _fib(n-1, calculated) + _fib(n-2, calculated) + return calculated[n] + + +print(fib(35)) # -> 9227465 \ No newline at end of file diff --git a/dynamic_programming/sum_possible.py b/dynamic_programming/sum_possible.py new file mode 100644 index 0000000..ab0181e --- /dev/null +++ b/dynamic_programming/sum_possible.py @@ -0,0 +1,70 @@ +""" +sum possible +Write a function sum_possible that takes in an amount and a list of +positive numbers. The function should return a boolean indicating whether +or not it is possible to create the amount by summing numbers of the list. +You may reuse numbers of the list as many times as necessary. + +You may assume that the target amount is non-negative. +""" + +def sum_possible(amount, numbers): + for n in numbers: + possible = recur(amount, n, numbers) + if possible: + return True + + return False + +def recur(remainder, subtrahend, numbers): + + if subtrahend > remainder: + return + + remainder = remainder - subtrahend + + if remainder == 0: + return True + + + + return False + + + + + + +print(sum_possible(4, [1, 2, 3,])) # -> True, 4 + 4 + +############################### +# solution with n! complexity # +############################### + +#def sum_possible(amount, numbers): +# for j in range(len(numbers)): +# possible =_mod(amount, numbers.copy(), j) +# if possible: +# return True +# return False +# +#def _mod(amount, numbers, i): +# # i need a way to keep track of the numbers i already used for that +# # branch of the recursive call +# +# # i need to try modding simultaneously with all values in the array +# if i == len(numbers): +# return +# +# #n = numbers[i] +# n = numbers.pop(i) +# +# if n > amount: +# return +# +# remainder = amount % n +# if remainder == 0: +# return True +# +# return _mod(remainder, numbers, 0) + \ No newline at end of file diff --git a/dynamic_programming/tribonacci.py b/dynamic_programming/tribonacci.py new file mode 100644 index 0000000..0d4b7a2 --- /dev/null +++ b/dynamic_programming/tribonacci.py @@ -0,0 +1,33 @@ +""" +tribonacci +Write a function tribonacci that takes in a number argument, n, and +returns the n-th number of the Tribonacci sequence. + +The 0-th and 1-st numbers of the sequence are both 0. + +The 2-nd number of the sequence is 1. + +To generate further numbers of the sequence, calculate the sum of +previous three numbers. + +Solve this recursively. +""" + +def tribonacci(n): + memo = dict() + return _trib(n, memo) + + +def _trib(n, memo): + if n == 0 or n==1: + return 0 + if n ==2: + return 1 + + if n in memo: + return memo[n] + + memo[n] = _trib(n-1, memo) + _trib(n-2, memo) + _trib(n-3, memo) + return memo[n] + +print(tribonacci(20)) # -> 35890