fuzzytags-sim/src/probability/hypergeometric.rs

63 lines
2.7 KiB
Rust

use crate::probability::binomial;
use std::cmp::min;
/// choose exactly w items (of a max of m) given k trials and n total items for a process that can be characterized by the `hypergeometric distribution`:
/// That is:
/// * The result of each draw (the elements of the population being sampled) can be classified into one of two mutually exclusive categories (e.g. Red/Blue or Not Red/Red).
/// * The probability of a success changes on each draw, as each draw decreases the population (sampling without replacement from a finite population).
pub fn exactly_without_replacement(x: u64, m: u64, k: u64, n: u64) -> rug::Float {
// println!("Selecting exactly {} (out of {}) from {} samples of {}",x,m,k,n);
if x > k || k > n || m > n || m < x {
return rug::Float::with_val(64, 0.0);
}
// If the desired population is the same as the total population then the answer is trivial
if m == n {
return rug::Float::with_val(64, 1.0);
}
let mchoosen = binomial::nchoosek(m, x) * rug::Float::with_val(64, 1.0);
let nsubmchooseksubx = binomial::nchoosek(n - m, k - x) * rug::Float::with_val(64, 1.0);
let nchoosek = binomial::nchoosek(n, k) * rug::Float::with_val(64, 1.0);
(mchoosen * nsubmchooseksubx) / nchoosek
}
/// choose at least w items (of a max of m) given k trials and n total items for a process that can be characterized by the `hypergeometric distribution`:
/// That is:
/// * The result of each draw (the elements of the population being sampled) can be classified into one of two mutually exclusive categories (e.g. Red/Blue or Not Red/Red).
/// * The probability of a success changes on each draw, as each draw decreases the population (sampling without replacement from a finite population).
/// This implementation is based on summing over `exactly_without_replacement`.
pub fn at_least_without_replacement(w: u64, m: u64, k: u64, n: u64) -> rug::Float {
// println!("Selecting at least {} (out of {}) from {} samples of {}",w,m,k,n);
let mut prob_at_least = rug::Float::with_val(64, 0.0);
if m == k && k == n || m == n {
return rug::Float::with_val(64, 1.0);
}
if w == m {
return exactly_without_replacement(w, m, k, n);
}
for x in w..=min(m, k) {
let a = exactly_without_replacement(x, m, k, n);
prob_at_least += a;
}
prob_at_least
}
#[cfg(test)]
mod tests {
use crate::probability::hypergeometric::at_least_without_replacement;
#[test]
fn test_at_least_without_replacement() {
println!("{:.12}", at_least_without_replacement(2, 3, 3, 4).to_f64());
}
#[test]
fn test_at_least_without_replacement_2() {
println!("{:.12}", at_least_without_replacement(1, 1, 3, 9).to_f64());
}
}