@@ -123,11 +123,69 @@ fn _memoized_fibonacci(n: u32, cache: &mut HashMap<u32, u128>) -> u128 {
123123 * f
124124}
125125
126+ /// matrix_fibonacci(n) returns the nth fibonacci number
127+ /// This function uses the definition of Fibonacci where:
128+ /// F(0) = 0, F(1) = 1 and F(n+1) = F(n) + F(n-1) for n>0
129+ ///
130+ /// Matrix formula:
131+ /// [F(n + 2)] = [1, 1] * [F(n + 1)]
132+ /// [F(n + 1)] [1, 0] [F(n) ]
133+ ///
134+ /// Warning: This will overflow the 128-bit unsigned integer at n=186
135+ pub fn matrix_fibonacci ( n : u32 ) -> u128 {
136+ let multiplier: Vec < Vec < u128 > > = vec ! [ vec![ 1 , 1 ] , vec![ 1 , 0 ] ] ;
137+
138+ let multiplier = matrix_power ( & multiplier, n) ;
139+ let initial_fib_matrix: Vec < Vec < u128 > > = vec ! [ vec![ 1 ] , vec![ 0 ] ] ;
140+
141+ let res = matrix_multiply ( & multiplier, & initial_fib_matrix) ;
142+
143+ res[ 1 ] [ 0 ]
144+ }
145+
146+ fn matrix_power ( base : & Vec < Vec < u128 > > , power : u32 ) -> Vec < Vec < u128 > > {
147+ let identity_matrix: Vec < Vec < u128 > > = vec ! [ vec![ 1 , 0 ] , vec![ 0 , 1 ] ] ;
148+
149+ vec ! [ base; power as usize ]
150+ . iter ( )
151+ . fold ( identity_matrix, |acc, x| matrix_multiply ( & acc, x) )
152+ }
153+
154+ // Copied from matrix_ops since u128 is required instead of i32
155+ #[ allow( clippy:: needless_range_loop) ]
156+ fn matrix_multiply ( multiplier : & [ Vec < u128 > ] , multiplicand : & [ Vec < u128 > ] ) -> Vec < Vec < u128 > > {
157+ // Multiply two matching matrices. The multiplier needs to have the same amount
158+ // of columns as the multiplicand has rows.
159+ let mut result: Vec < Vec < u128 > > = vec ! [ ] ;
160+ let mut temp;
161+ // Using variable to compare lenghts of rows in multiplicand later
162+ let row_right_length = multiplicand[ 0 ] . len ( ) ;
163+ for row_left in 0 ..multiplier. len ( ) {
164+ if multiplier[ row_left] . len ( ) != multiplicand. len ( ) {
165+ panic ! ( "Matrix dimensions do not match" ) ;
166+ }
167+ result. push ( vec ! [ ] ) ;
168+ for column_right in 0 ..multiplicand[ 0 ] . len ( ) {
169+ temp = 0 ;
170+ for row_right in 0 ..multiplicand. len ( ) {
171+ if row_right_length != multiplicand[ row_right] . len ( ) {
172+ // If row is longer than a previous row cancel operation with error
173+ panic ! ( "Matrix dimensions do not match" ) ;
174+ }
175+ temp += multiplier[ row_left] [ row_right] * multiplicand[ row_right] [ column_right] ;
176+ }
177+ result[ row_left] . push ( temp) ;
178+ }
179+ }
180+ result
181+ }
182+
126183#[ cfg( test) ]
127184mod tests {
128185 use super :: classical_fibonacci;
129186 use super :: fibonacci;
130187 use super :: logarithmic_fibonacci;
188+ use super :: matrix_fibonacci;
131189 use super :: memoized_fibonacci;
132190 use super :: recursive_fibonacci;
133191
@@ -250,4 +308,22 @@ mod tests {
250308 127127879743834334146972278486287885163
251309 ) ;
252310 }
311+
312+ #[ test]
313+ fn test_matrix_fibonacci ( ) {
314+ assert_eq ! ( matrix_fibonacci( 0 ) , 0 ) ;
315+ assert_eq ! ( matrix_fibonacci( 1 ) , 1 ) ;
316+ assert_eq ! ( matrix_fibonacci( 2 ) , 1 ) ;
317+ assert_eq ! ( matrix_fibonacci( 3 ) , 2 ) ;
318+ assert_eq ! ( matrix_fibonacci( 4 ) , 3 ) ;
319+ assert_eq ! ( matrix_fibonacci( 5 ) , 5 ) ;
320+ assert_eq ! ( matrix_fibonacci( 10 ) , 55 ) ;
321+ assert_eq ! ( matrix_fibonacci( 20 ) , 6765 ) ;
322+ assert_eq ! ( matrix_fibonacci( 21 ) , 10946 ) ;
323+ assert_eq ! ( matrix_fibonacci( 100 ) , 354224848179261915075 ) ;
324+ assert_eq ! (
325+ matrix_fibonacci( 184 ) ,
326+ 127127879743834334146972278486287885163
327+ ) ;
328+ }
253329}
0 commit comments