diff --git a/src/dynamic_programming/fibonacci.rs b/src/dynamic_programming/fibonacci.rs index 2ea0c672bd5..a7db9f9562c 100644 --- a/src/dynamic_programming/fibonacci.rs +++ b/src/dynamic_programming/fibonacci.rs @@ -180,6 +180,33 @@ fn matrix_multiply(multiplier: &[Vec], multiplicand: &[Vec]) -> Vec< result } +/// Binary lifting fibonacci +/// +/// Following properties of F(n) could be deduced from the matrix formula above: +/// +/// F(2n) = F(n) * (2F(n+1) - F(n)) +/// F(2n+1) = F(n+1)^2 + F(n)^2 +/// +/// Therefore F(n) and F(n+1) can be derived from F(n>>1) and F(n>>1 + 1), which +/// has a smaller constant in both time and space compared to matrix fibonacci. +pub fn binary_lifting_fibonacci(n: u32) -> u128 { + // the state always stores F(k), F(k+1) for some k, initially F(0), F(1) + let mut state = (0u128, 1u128); + + for i in (0..u32::BITS - n.leading_zeros()).rev() { + // compute F(2k), F(2k+1) from F(k), F(k+1) + state = ( + state.0 * (2 * state.1 - state.0), + state.0 * state.0 + state.1 * state.1, + ); + if n & (1 << i) != 0 { + state = (state.1, state.0 + state.1); + } + } + + state.0 +} + /// nth_fibonacci_number_modulo_m(n, m) returns the nth fibonacci number modulo the specified m /// i.e. F(n) % m pub fn nth_fibonacci_number_modulo_m(n: i64, m: i64) -> i128 { @@ -251,6 +278,7 @@ pub fn last_digit_of_the_sum_of_nth_fibonacci_number(n: i64) -> i64 { #[cfg(test)] mod tests { + use super::binary_lifting_fibonacci; use super::classical_fibonacci; use super::fibonacci; use super::last_digit_of_the_sum_of_nth_fibonacci_number; @@ -398,6 +426,24 @@ mod tests { ); } + #[test] + fn test_binary_lifting_fibonacci() { + assert_eq!(binary_lifting_fibonacci(0), 0); + assert_eq!(binary_lifting_fibonacci(1), 1); + assert_eq!(binary_lifting_fibonacci(2), 1); + assert_eq!(binary_lifting_fibonacci(3), 2); + assert_eq!(binary_lifting_fibonacci(4), 3); + assert_eq!(binary_lifting_fibonacci(5), 5); + assert_eq!(binary_lifting_fibonacci(10), 55); + assert_eq!(binary_lifting_fibonacci(20), 6765); + assert_eq!(binary_lifting_fibonacci(21), 10946); + assert_eq!(binary_lifting_fibonacci(100), 354224848179261915075); + assert_eq!( + binary_lifting_fibonacci(184), + 127127879743834334146972278486287885163 + ); + } + #[test] fn test_nth_fibonacci_number_modulo_m() { assert_eq!(nth_fibonacci_number_modulo_m(5, 10), 5); diff --git a/src/dynamic_programming/mod.rs b/src/dynamic_programming/mod.rs index 76059465899..f28fc7c615c 100644 --- a/src/dynamic_programming/mod.rs +++ b/src/dynamic_programming/mod.rs @@ -20,6 +20,7 @@ mod word_break; pub use self::coin_change::coin_change; pub use self::egg_dropping::egg_drop; +pub use self::fibonacci::binary_lifting_fibonacci; pub use self::fibonacci::classical_fibonacci; pub use self::fibonacci::fibonacci; pub use self::fibonacci::last_digit_of_the_sum_of_nth_fibonacci_number;