11'''
2- Nth Fibonacci Number
2+ Find Nth Fibonacci Number
33
44The Fibonacci numbers are the numbers in the following integer sequence.
550, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...
1515The simplest recursive solution.
1616 Time Complexity: O(2^N) , actually ~1.6^N, the golden ratio
1717 Space Complexity: O(N) , because of the recursion stack
18- Recursion with memoizing.
18+ Recursion with memoizing. Much faster, 2^N-N less operations.
1919 Time Complexity: O(N)
2020 Space Complexity: O(N)
2121Dynamic programming.
2424Space optimized dynamic programming. (don't need a whole array, only 2 variables are needed)
2525 Time Complexity: O(N)
2626 Space Complexity: O(1)
27- Matrix multiplication. (matrix power)
27+ Matrix power.
2828start matrix: | 1 1 |
2929 | 1 0 |
3030
4141 | Fn+1 Fn |
4242 Time Complexity: O(N)
4343 Space Complexity: O(1)
44- Optimized matrix multiplication . (faster power function, using divide and conquer approach)
44+ Time optimized matrix power . (using recursive divide and conquer approach)
4545 Time Complexity: O(LogN)
4646 Space Complexity: O(LogN) , because of the recursion stack
47+ Time and space optimized matrix multiplication. (without recursion, just looping)
48+ Time Complexity: O(LogN)
49+ Space Complexity: O(1)
4750'''
4851
4952
5457def fibonacci_1 (n ):
5558 if (n == 0 ) or (n == 1 ):
5659 return n
60+
5761 return fibonacci_1 (n - 1 ) + fibonacci_1 (n - 2 )
5862
5963
6064##############
6165# Solution 2 #
6266##############
6367
68+ # hashmap
6469fib = {0 : 0 , 1 : 1 }
6570
6671def fibonacci_2 (n ):
6772 if n in fib :
6873 return fib [n ]
74+
6975 fib [n ] = fibonacci_2 (n - 1 ) + fibonacci_2 (n - 2 )
76+
7077 return fib [n ]
7178
7279
@@ -76,7 +83,6 @@ def fibonacci_2(n):
7683
7784def fibonacci_3 (n ):
7885 dp = [0 for i in range (max (2 , n + 1 ))]
79- dp [0 ] = 0
8086 dp [1 ] = 1
8187
8288 for i in range (2 , n + 1 ):
@@ -90,13 +96,33 @@ def fibonacci_3(n):
9096##############
9197
9298def fibonacci_4 (n ):
93- a , b = 1 , 0
99+ dp0 , dp1 = 0 , 1
100+
101+ for i in range (n ):
102+ dp0 , dp1 = dp1 , dp0 + dp1
103+
104+ return dp0
94105
95- while n > 0 :
96- a , b = a + b , a
97- n -= 1
98106
99- return b
107+ #################################
108+ # Helper for the next solutions #
109+ #################################
110+
111+ def matrix_mult (a , b ):
112+ ''' a = a * b
113+ Matrices (2x2 matrix) Multiplication method used for the next solutions.
114+ The result of multiplication is saved in 'a' (because of that, the reference
115+ shouldn't be changed, only change the values after all computations are completed
116+ because 'b' could be the same reference/matrix as 'a').
117+ '''
118+ a00 = a [0 ][0 ]* b [0 ][0 ] + a [0 ][1 ]* b [1 ][0 ]
119+ a01 = a [0 ][0 ]* b [0 ][1 ] + a [0 ][1 ]* b [1 ][1 ]
120+ a10 = a [1 ][0 ]* b [0 ][0 ] + a [1 ][1 ]* b [1 ][0 ]
121+ a11 = a [1 ][0 ]* b [0 ][1 ] + a [1 ][1 ]* b [1 ][1 ]
122+ a [0 ][0 ] = a00
123+ a [0 ][1 ] = a01
124+ a [1 ][0 ] = a10
125+ a [1 ][1 ] = a11
100126
101127
102128##############
@@ -108,16 +134,7 @@ def fibonacci_5(n):
108134 res = [[1 , 1 ], [1 , 0 ]]
109135
110136 for i in range (n ):
111- res = [
112- [
113- res [0 ][0 ]* fib [0 ][0 ] + res [0 ][1 ]* fib [1 ][0 ],
114- res [0 ][0 ]* fib [0 ][1 ] + res [0 ][1 ]* fib [1 ][1 ]
115- ],
116- [
117- res [1 ][0 ]* fib [0 ][0 ] + res [1 ][1 ]* fib [1 ][0 ],
118- res [1 ][0 ]* fib [0 ][1 ] + res [1 ][1 ]* fib [1 ][1 ]
119- ]
120- ]
137+ matrix_mult (res , fib )
121138
122139 return res [1 ][1 ] # Fn-1 (or change the range(n-1) and use res[0][1] or res[1][0])
123140
@@ -127,39 +144,42 @@ def fibonacci_5(n):
127144##############
128145
129146def fibonacci_6 (n ):
130- fib = [[1 , 1 ], [1 , 0 ]]
147+ res = [[1 , 1 ], [1 , 0 ]]
131148
132- # send the matrix by reference
133- matrix_pow (fib , n + 1 )
149+ matrix_pow (res , n + 1 )
134150
135- return fib [1 ][1 ] # return Fn-1
151+ return res [1 ][1 ] # return Fn-1
136152
137- def matrix_pow (fib , n ):
153+ def matrix_pow (mat , n ):
138154 # divide and conquer power function
139155 if (n == 0 ) or (n == 1 ):
140156 return
141157
142158 # split on 2, because matrix^k = matrix^(k/2) * matrix^(k/2)
143- matrix_pow (fib , n // 2 )
159+ matrix_pow (mat , n // 2 )
144160
145- matrix_mult (fib , fib )
161+ matrix_mult (mat , mat )
146162 if n % 2 == 1 :
147163 # multiply by the start matrix if odd power
148- matrix_mult (fib , [[1 , 1 ], [1 , 0 ]])
164+ matrix_mult (mat , [[1 , 1 ], [1 , 0 ]])
149165
150- def matrix_mult (a , b ):
151- a0 = [
152- a [0 ][0 ]* b [0 ][0 ] + a [0 ][1 ]* b [1 ][0 ],
153- a [0 ][0 ]* b [0 ][1 ] + a [0 ][1 ]* b [1 ][1 ]
154- ]
155- a1 = [
156- a [1 ][0 ]* b [0 ][0 ] + a [1 ][1 ]* b [1 ][0 ],
157- a [1 ][0 ]* b [0 ][1 ] + a [1 ][1 ]* b [1 ][1 ]
158- ]
159- # don't lose the reference of 'a'
160- # and don't multiply directly because 'a' has same reference as 'b'
161- a [0 ] = a0
162- a [1 ] = a1
166+
167+ ##############
168+ # Solution 7 #
169+ ##############
170+
171+ def fibonacci_7 (n ):
172+ fib = [[1 , 1 ], [1 , 0 ]]
173+ res = [[1 , 1 ], [1 , 0 ]]
174+
175+ while n > 0 :
176+ if n % 2 == 1 :
177+ matrix_mult (res , fib )
178+
179+ n = n // 2
180+ matrix_mult (fib , fib )
181+
182+ return res [1 ][1 ]
163183
164184
165185###########
@@ -173,4 +193,16 @@ def matrix_mult(a, b):
173193print (fibonacci_3 (8 ))
174194print (fibonacci_4 (8 ))
175195print (fibonacci_5 (8 ))
176- print (fibonacci_6 (8 ))
196+ print (fibonacci_6 (8 ))
197+ print (fibonacci_7 (8 ))
198+
199+
200+ # Test 2
201+ # Correct result => 10946
202+ print (fibonacci_1 (21 ))
203+ print (fibonacci_2 (21 ))
204+ print (fibonacci_3 (21 ))
205+ print (fibonacci_4 (21 ))
206+ print (fibonacci_5 (21 ))
207+ print (fibonacci_6 (21 ))
208+ print (fibonacci_7 (21 ))
0 commit comments