Skip to content

Commit 9cb06fc

Browse files
y5c4l3vil02
andauthored
fibonacci: add binary lifting version (TheAlgorithms#828)
Signed-off-by: y5c4l3 <y5c4l3@proton.me> Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com>
1 parent f5ab102 commit 9cb06fc

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

src/dynamic_programming/fibonacci.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,33 @@ fn matrix_multiply(multiplier: &[Vec<u128>], multiplicand: &[Vec<u128>]) -> Vec<
180180
result
181181
}
182182

183+
/// Binary lifting fibonacci
184+
///
185+
/// Following properties of F(n) could be deduced from the matrix formula above:
186+
///
187+
/// F(2n) = F(n) * (2F(n+1) - F(n))
188+
/// F(2n+1) = F(n+1)^2 + F(n)^2
189+
///
190+
/// Therefore F(n) and F(n+1) can be derived from F(n>>1) and F(n>>1 + 1), which
191+
/// has a smaller constant in both time and space compared to matrix fibonacci.
192+
pub fn binary_lifting_fibonacci(n: u32) -> u128 {
193+
// the state always stores F(k), F(k+1) for some k, initially F(0), F(1)
194+
let mut state = (0u128, 1u128);
195+
196+
for i in (0..u32::BITS - n.leading_zeros()).rev() {
197+
// compute F(2k), F(2k+1) from F(k), F(k+1)
198+
state = (
199+
state.0 * (2 * state.1 - state.0),
200+
state.0 * state.0 + state.1 * state.1,
201+
);
202+
if n & (1 << i) != 0 {
203+
state = (state.1, state.0 + state.1);
204+
}
205+
}
206+
207+
state.0
208+
}
209+
183210
/// nth_fibonacci_number_modulo_m(n, m) returns the nth fibonacci number modulo the specified m
184211
/// i.e. F(n) % m
185212
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 {
251278

252279
#[cfg(test)]
253280
mod tests {
281+
use super::binary_lifting_fibonacci;
254282
use super::classical_fibonacci;
255283
use super::fibonacci;
256284
use super::last_digit_of_the_sum_of_nth_fibonacci_number;
@@ -398,6 +426,24 @@ mod tests {
398426
);
399427
}
400428

429+
#[test]
430+
fn test_binary_lifting_fibonacci() {
431+
assert_eq!(binary_lifting_fibonacci(0), 0);
432+
assert_eq!(binary_lifting_fibonacci(1), 1);
433+
assert_eq!(binary_lifting_fibonacci(2), 1);
434+
assert_eq!(binary_lifting_fibonacci(3), 2);
435+
assert_eq!(binary_lifting_fibonacci(4), 3);
436+
assert_eq!(binary_lifting_fibonacci(5), 5);
437+
assert_eq!(binary_lifting_fibonacci(10), 55);
438+
assert_eq!(binary_lifting_fibonacci(20), 6765);
439+
assert_eq!(binary_lifting_fibonacci(21), 10946);
440+
assert_eq!(binary_lifting_fibonacci(100), 354224848179261915075);
441+
assert_eq!(
442+
binary_lifting_fibonacci(184),
443+
127127879743834334146972278486287885163
444+
);
445+
}
446+
401447
#[test]
402448
fn test_nth_fibonacci_number_modulo_m() {
403449
assert_eq!(nth_fibonacci_number_modulo_m(5, 10), 5);

src/dynamic_programming/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mod word_break;
2020

2121
pub use self::coin_change::coin_change;
2222
pub use self::egg_dropping::egg_drop;
23+
pub use self::fibonacci::binary_lifting_fibonacci;
2324
pub use self::fibonacci::classical_fibonacci;
2425
pub use self::fibonacci::fibonacci;
2526
pub use self::fibonacci::last_digit_of_the_sum_of_nth_fibonacci_number;

0 commit comments

Comments
 (0)