More factorial/fibonacci optimizing
Some checks failed
timw4mail/rusty-numbers/pipeline/head There was a failure building this commit

This commit is contained in:
Timothy Warren 2020-02-21 13:15:05 -05:00
parent f49d1798e2
commit d4458bae08
2 changed files with 85 additions and 54 deletions

View File

@ -1,16 +1,16 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use rusty_numbers::{factorial, fibonacci, mem_fibonacci, mem_factorial}; use rusty_numbers::{factorial, fibonacci, it_factorial, mem_fibonacci, rec_fibonacci};
fn bench_factorial(c: &mut Criterion) { fn bench_factorial(c: &mut Criterion) {
let mut group = c.benchmark_group("Factorials"); let mut group = c.benchmark_group("Factorials");
for i in [0usize, 5, 10, 15, 20, 25, 30, 34].iter() { for i in [0usize, 5, 10, 15, 20, 25, 30, 34].iter() {
group.bench_with_input(BenchmarkId::new("Recursive memoized", i), i, |b, i| {
b.iter(|| mem_factorial(*i))
});
group.bench_with_input(BenchmarkId::new("Recursive naive", i), i, |b, i| { group.bench_with_input(BenchmarkId::new("Recursive naive", i), i, |b, i| {
b.iter(|| factorial(*i)) b.iter(|| factorial(*i))
}); });
group.bench_with_input(BenchmarkId::new("Iterative", i), i, |b, i| {
b.iter(|| it_factorial(*i))
});
} }
group.finish(); group.finish();
} }
@ -27,6 +27,16 @@ fn bench_fibonacci(c: &mut Criterion) {
b.iter(|| fibonacci(*i)) b.iter(|| fibonacci(*i))
}); });
} }
group.finish();
let mut group = c.benchmark_group("Recursive Fibonacci");
for i in [0usize, 5, 10, 15, 20, 25, 26, 27, 28, 29, 30].iter() {
group.bench_with_input(BenchmarkId::new("Naive Recursive", i), i, |b, i| {
b.iter(|| rec_fibonacci(*i))
});
}
group.finish();
} }
criterion_group!(benches, bench_factorial, bench_fibonacci); criterion_group!(benches, bench_factorial, bench_fibonacci);

View File

@ -37,6 +37,11 @@ pub fn mem_fibonacci(n: usize) -> Option<u128> {
/// Actual calculating function for `fibonacci` /// Actual calculating function for `fibonacci`
#[inline] #[inline]
fn _mem_fibonacci(n: usize, table: &mut [u128]) -> Option<u128> { fn _mem_fibonacci(n: usize, table: &mut [u128]) -> Option<u128> {
if n < 2 {
// The first values are predefined.
return Some(table[n]);
}
if n > 186 { if n > 186 {
return None; return None;
} }
@ -51,8 +56,8 @@ fn _mem_fibonacci(n: usize, table: &mut [u128]) -> Option<u128> {
table[n] = a + b; table[n] = a + b;
Some(table[n]) Some(table[n])
}, }
x => Some(x) x => Some(x),
} }
} }
@ -101,8 +106,7 @@ pub fn fibonacci(n: usize) -> Option<u128> {
} }
} }
/// Calculate the value of a factorial, /// Calculate the value of a factorial iteratively
/// using a lookup table for better worst-case performance.
/// ///
/// Can calculate up to 34! using native unsigned 128 bit integers. /// Can calculate up to 34! using native unsigned 128 bit integers.
/// ///
@ -117,40 +121,22 @@ pub fn fibonacci(n: usize) -> Option<u128> {
/// # assert!(invalid.is_none()); /// # assert!(invalid.is_none());
/// ``` /// ```
#[inline] #[inline]
pub fn mem_factorial(n: usize) -> Option<u128> { pub fn it_factorial(n: usize) -> Option<u128> {
let mut table = [0u128; 35]; let mut total: u128 = 1;
table[0] = 1;
table[1] = 1;
table[2] = 2;
match n {
_mem_factorial(n, &mut table) 0 | 1 => Some(1u128),
} _ => {
for x in 1..=n {
/// Actual Calculation function for factoral total = total.checked_mul(x as u128)?;
#[inline]
fn _mem_factorial(n: usize, table: &mut [u128]) -> Option<u128> {
if n > 34 {
return None
} }
match table[n] { Some(total)
// Empty spaces in the lookup array are zero-filled }
0 => {
// The ? suffix passes along the Option value
// to be handled later
// See: https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator
let prev = _mem_factorial(n - 1, table)?;
table[n] = (n as u128) * prev;
Some(table[n])
},
x => Some(x),
} }
} }
/// Calculate the value of a factorial, /// Calculate the value of a factorial recrursively
/// ///
/// Can calculate up to 34! using native unsigned 128 bit integers. /// Can calculate up to 34! using native unsigned 128 bit integers.
/// ///
@ -184,36 +170,71 @@ mod tests {
#[test] #[test]
fn test_factorial() { fn test_factorial() {
assert_eq!(1, factorial(0).unwrap()); for pair in [[1usize, 0], [1, 1], [2, 2], [6, 3]].iter() {
assert_eq!(1, factorial(1).unwrap()); assert_eq!(
assert_eq!(6, factorial(3).unwrap()); Some(pair[0] as u128),
factorial(pair[1]),
"{}! should be {}",
pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
it_factorial(pair[1]),
"{}! should be {}",
pair[1],
pair[0]
);
}
// Verify each implementation returns the same results
let res = factorial(34); let res = factorial(34);
let res2 = mem_factorial(34); let res2 = it_factorial(34);
assert!(res.is_some()); assert!(res.is_some());
assert_eq!(res.unwrap(), res2.unwrap()); assert!(res2.is_some());
assert_eq!(res, res2);
let res = factorial(35); // Bounds checks
assert!(res.is_none()); assert!(factorial(35).is_none());
assert!(it_factorial(35).is_none());
} }
#[test] #[test]
fn test_fibonacci() { fn test_fibonacci() {
assert_eq!(0, fibonacci(0).unwrap()); // Sanity checking
assert_eq!(1, fibonacci(1).unwrap()); for pair in [[0usize, 0], [1, 1], [1, 2], [2, 3]].iter() {
assert_eq!(1, fibonacci(2).unwrap()); assert_eq!(
Some(pair[0] as u128),
let res = fibonacci(20); fibonacci(pair[1]),
let res2 = rec_fibonacci(20); "fibonacci({}) should be {}",
assert_eq!(res, res2); pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
mem_fibonacci(pair[1]),
"fibonacci({}) should be {}",
pair[1],
pair[0]
);
assert_eq!(
Some(pair[0] as u128),
rec_fibonacci(pair[1]),
"fibonacci({}) should be {}",
pair[1],
pair[0]
);
}
// Verify each implementation returns the same results
let res = fibonacci(186); let res = fibonacci(186);
let res2 = mem_fibonacci(186); let res2 = mem_fibonacci(186);
assert!(res.is_some()); assert!(res.is_some());
assert!(res2.is_some()); assert!(res2.is_some());
assert_eq!(res.unwrap(), res2.unwrap()); assert_eq!(res, res2);
let res = fibonacci(187); // Bounds checks
assert!(res.is_none()); assert!(fibonacci(187).is_none());
assert!(mem_fibonacci(187).is_none());
} }
} }