use std::collections::HashMap;
use nalgebra::RealField;
#[allow(unused)]
pub fn binomial(n: usize, k: usize) -> f64 {
if k == 0 || k == n {
return 1.;
} else if n == 0 || k > n {
return 0.;
}
let k = k.min(n - k);
let mut r = 1.;
for i in 0..k {
r = r * (n - i) as f64 / (i + 1) as f64;
}
r
}
pub struct Binomial<T> {
memo: HashMap<usize, HashMap<usize, T>>,
}
impl<T: RealField + Copy> Default for Binomial<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: RealField + Copy> Binomial<T> {
pub fn new() -> Self {
Self {
memo: HashMap::new(),
}
}
pub fn get(&mut self, n: usize, k: usize) -> T {
if k == 0 || k == n {
return T::one();
} else if n == 0 || k > n {
return T::zero();
}
let k = k.min(n - k);
if let Some(memoized) = self.memo(n, k) {
return memoized;
}
let r = self.get(n - 1, k) + self.get(n - 1, k - 1);
self.memoize(n, k, r);
r
}
fn memo(&self, n: usize, k: usize) -> Option<T> {
if let Some(m) = self.memo.get(&n) {
if let Some(&v) = m.get(&k) {
return Some(v);
}
}
None
}
fn memoize(&mut self, n: usize, k: usize, v: T) {
if let Some(m) = self.memo.get_mut(&n) {
m.insert(k, v);
} else {
let mut m = HashMap::new();
m.insert(k, v);
self.memo.insert(n, m);
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_binomial() {
assert_eq!(super::binomial(5, 0), 1.);
assert_eq!(super::binomial(5, 1), 5.);
assert_eq!(super::binomial(5, 2), 10.);
assert_eq!(super::binomial(5, 3), 10.);
assert_eq!(super::binomial(5, 4), 5.);
assert_eq!(super::binomial(5, 5), 1.);
assert_eq!(super::binomial(5, 6), 0.);
}
#[test]
fn test_memoized_binomial() {
let mut binomial = super::Binomial::<f64>::new();
for n in 1..10 {
for k in 1..=n {
assert_eq!(binomial.get(n, k), crate::misc::binomial::binomial(n, k));
}
}
}
}