count = 0
def fib(n):
global count
count += 1
if n <= 1:
return 1
f = fib(n-1) + fib(n-2)
return f
n=30 #40, 50? takes a while
print("fib at {}th position = {}".format(n, fib(n)))
print("fib function count = {}".format(count))
fib at 30th position = 1346269 fib function count = 2692537
Time Complexity: $T(n)$ = time to calculate Fib(n-1) + Fib(n-2) + time to add them: O(1)
using Big-Oh (O) notation for upper-bound:
precisely
$T(n) = O(1.6)^n$
Space Complexity = $O(n)$ due to call stack
#print(globals())
import timeit
print(timeit.timeit('fib(30)', number=1, globals=globals()))
# big difference between 30 and 40
0.35596761399983734
count = 0
def MemoizedFib(memo, n):
global count
count += 1
if n <= 1:
return 1
if n in memo:
return memo[n]
memo[n] = MemoizedFib(memo, n-1) + MemoizedFib(memo, n-2)
return memo[n]
memo = {}
n=1000 #try 40, 50, 60, 100, 500, 10000, ...
print("fib at {}th position = {}".format(n, MemoizedFib(memo, n)))
print("fib function called {} times.".format(count))
fib at 1000th position = 70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501 fib function called 2118 times.
import timeit
memo = {}
n=1000
print(timeit.timeit('MemoizedFib(memo, n)', number=1, globals=globals()))
0.0009976609999284847
mod = 1000000007
def MemoizedModFib(memo, n):
if n <= 1:
return 1
if n in memo:
return memo[n]
memo[n] = (MemoizedFib(memo, n-1)%mod + MemoizedFib(memo, n-2)%mod)%mod
return memo[n]
memo = {}
n=1000 #try 40, 50, 60, 100, 500, 10000, ...
print("fib at {}th position = {}".format(n, MemoizedModFib(memo, n)))
fib at 1000th position = 107579939
def iterativeFib(n):
# fib array/list
fib = [1]*(n+1) # initialize 0..n list with 1
for i in range(2, n+1):
fib[i] = fib[i-1] + fib[i-2]
return fib[i]
n=1000
print(timeit.timeit('iterativeFib(n)', number=1, globals=globals()))
# is faster than recursive counterpart
0.0001866370002971962
def countWays(coins, N):
# use ways table to store the results
# ways[i] will store the number of solutions for value i
ways = [0]*(N+1) # initialize all values 0-12 as 0
# base case
ways[0] = 1
# pick all coins one by one
# update the ways starting from the value of the picked coin
print('values:', list(range(N+1)))
for coin in coins:
for i in range(coin, N+1):
ways[i] += ways[i-coin]
print('ways: ', ways, coin)
return ways[N]
coins = [1, 5, 10]
N = 8
print('Number of Ways to get {} = {}'.format(N, countWays(coins, N)))
values: [0, 1, 2, 3, 4, 5, 6, 7, 8] ways: [1, 1, 1, 1, 1, 1, 1, 1, 1] 1 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2] 5 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2] 10 Number of Ways to get 8 = 2
N = 10
print('Number of Ways to get {} = {}'.format(N, countWays(coins, N)))
values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ways: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 1 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3] 5 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4] 10 Number of Ways to get 10 = 4
N = 10
print('Number of Ways to get {} = {}'.format(N, countWays(coins, 12)))
values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] ways: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 1 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3] 5 ways: [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 4, 4, 4] 10 Number of Ways to get 10 = 4
import math
# DP solution for min coin count to make the change N
def minCoins(coins, N):
# count list stores the minimum number of coins required for i value
# all values 0-N are initialized to infinity
count = [math.inf]*(N+1)
# base case
# no. of coin required to make 0 value is 0
count[0] = 0
# computer min coins for all values from 1 to N
for i in range(1, N+1):
for coin in coins:
# for every coin smaller than value i
if coin <= i:
if count[i-coin]+1 < count[i]:
count[i] = count[i-coin]+1
return count[N]
coins = [1, 3, 4]
N = 6
print('min coins required to give total of {} change = {}'.format(N, minCoins(coins, N)))
min coins required to give total of 6 change = 2