-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathbell_numbers.rs
147 lines (115 loc) · 3.79 KB
/
bell_numbers.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use num_bigint::BigUint;
use num_traits::{One, Zero};
use std::sync::RwLock;
/// Returns the number of ways you can select r items given n options
fn n_choose_r(n: u32, r: u32) -> BigUint {
if r == n || r == 0 {
return One::one();
}
if r > n {
return Zero::zero();
}
// Any combination will only need to be computed once, thus giving no need to
// memoize this function
let product: BigUint = (0..r).fold(BigUint::one(), |acc, x| {
(acc * BigUint::from(n - x)) / BigUint::from(x + 1)
});
product
}
/// A memoization table for storing previous results
struct MemTable {
buffer: Vec<BigUint>,
}
impl MemTable {
const fn new() -> Self {
MemTable { buffer: Vec::new() }
}
fn get(&self, n: usize) -> Option<BigUint> {
if n == 0 || n == 1 {
Some(BigUint::one())
} else if let Some(entry) = self.buffer.get(n) {
if *entry == BigUint::zero() {
None
} else {
Some(entry.clone())
}
} else {
None
}
}
fn set(&mut self, n: usize, b: BigUint) {
self.buffer[n] = b;
}
#[inline]
fn capacity(&self) -> usize {
self.buffer.capacity()
}
#[inline]
fn resize(&mut self, new_size: usize) {
if new_size > self.buffer.len() {
self.buffer.resize(new_size, Zero::zero());
}
}
}
// Implemented with RwLock so it is accessible across threads
static LOOKUP_TABLE_LOCK: RwLock<MemTable> = RwLock::new(MemTable::new());
pub fn bell_number(n: u32) -> BigUint {
let needs_resize;
// Check if number is already in lookup table
{
let lookup_table = LOOKUP_TABLE_LOCK.read().unwrap();
if let Some(entry) = lookup_table.get(n as usize) {
return entry;
}
needs_resize = (n + 1) as usize > lookup_table.capacity();
}
// Resize table before recursion so that if more values need to be added during recursion the table isn't
// reallocated every single time
if needs_resize {
let mut lookup_table = LOOKUP_TABLE_LOCK.write().unwrap();
lookup_table.resize((n + 1) as usize);
}
let new_bell_number: BigUint = (0..n).map(|x| bell_number(x) * n_choose_r(n - 1, x)).sum();
// Add new number to lookup table
{
let mut lookup_table = LOOKUP_TABLE_LOCK.write().unwrap();
lookup_table.set(n as usize, new_bell_number.clone());
}
new_bell_number
}
#[cfg(test)]
pub mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_choose_zero() {
for i in 1..100 {
assert_eq!(n_choose_r(i, 0), One::one());
}
}
#[test]
fn test_combination() {
let five_choose_1 = BigUint::from(5u32);
assert_eq!(n_choose_r(5, 1), five_choose_1);
assert_eq!(n_choose_r(5, 4), five_choose_1);
let ten_choose_3 = BigUint::from(120u32);
assert_eq!(n_choose_r(10, 3), ten_choose_3);
assert_eq!(n_choose_r(10, 7), ten_choose_3);
let fourty_two_choose_thirty = BigUint::from_str("11058116888").unwrap();
assert_eq!(n_choose_r(42, 30), fourty_two_choose_thirty);
assert_eq!(n_choose_r(42, 12), fourty_two_choose_thirty);
}
#[test]
fn test_bell_numbers() {
let bell_one = BigUint::from(1u32);
assert_eq!(bell_number(1), bell_one);
let bell_three = BigUint::from(5u32);
assert_eq!(bell_number(3), bell_three);
let bell_eight = BigUint::from(4140u32);
assert_eq!(bell_number(8), bell_eight);
let bell_six = BigUint::from(203u32);
assert_eq!(bell_number(6), bell_six);
let bell_twenty_six = BigUint::from_str("49631246523618756274").unwrap();
assert_eq!(bell_number(26), bell_twenty_six);
}
}