sciforge_lib/maths/probability/
sampling.rs1pub fn latin_hypercube(n_samples: usize, n_dims: usize, seed: u64) -> Vec<Vec<f64>> {
2 let mut rng = SampRng::new(seed);
3 let mut result = vec![vec![0.0; n_dims]; n_samples];
4 for d in 0..n_dims {
5 let mut perm: Vec<usize> = (0..n_samples).collect();
6 for i in (1..n_samples).rev() {
7 let j = rng.next_usize(i + 1);
8 perm.swap(i, j);
9 }
10 for (ri, &pi) in result.iter_mut().zip(&perm) {
11 ri[d] = (pi as f64 + rng.next_f64()) / n_samples as f64;
12 }
13 }
14 result
15}
16
17pub fn stratified_sampling(n_strata: usize, seed: u64) -> Vec<f64> {
18 let mut rng = SampRng::new(seed);
19 (0..n_strata)
20 .map(|i| (i as f64 + rng.next_f64()) / n_strata as f64)
21 .collect()
22}
23
24pub fn sobol_sequence_1d(n: usize) -> Vec<f64> {
25 let mut result = vec![0.0; n];
26 for i in 1..n {
27 let mut c = 0u32;
28 let mut v = i;
29 while v & 1 == 0 {
30 c += 1;
31 v >>= 1;
32 }
33 let direction = 1u64 << (63 - c);
34 let prev_bits = (result[i - 1] * (1u64 << 63) as f64) as u64;
35 result[i] = (prev_bits ^ direction) as f64 / (1u64 << 63) as f64;
36 }
37 result
38}
39
40pub fn halton_sequence(n: usize, base: u64) -> Vec<f64> {
41 (0..n).map(|i| halton_element(i as u64, base)).collect()
42}
43
44fn halton_element(mut index: u64, base: u64) -> f64 {
45 let mut result = 0.0;
46 let mut f = 1.0 / base as f64;
47 index += 1;
48 while index > 0 {
49 result += f * (index % base) as f64;
50 index /= base;
51 f /= base as f64;
52 }
53 result
54}
55
56pub fn inverse_transform_exponential(u: f64, lambda: f64) -> f64 {
57 -(1.0 - u).ln() / lambda
58}
59
60pub fn box_muller(u1: f64, u2: f64) -> (f64, f64) {
61 let r = (-2.0 * u1.ln()).sqrt();
62 let theta = 2.0 * std::f64::consts::PI * u2;
63 (r * theta.cos(), r * theta.sin())
64}
65
66pub fn reservoir_sampling(stream: &[f64], k: usize, seed: u64) -> Vec<f64> {
67 let mut rng = SampRng::new(seed);
68 let mut reservoir = stream[..k.min(stream.len())].to_vec();
69 for (i, &si) in stream.iter().enumerate().skip(k) {
70 let j = rng.next_usize(i + 1);
71 if j < k {
72 reservoir[j] = si;
73 }
74 }
75 reservoir
76}
77
78struct SampRng {
79 state: u64,
80}
81impl SampRng {
82 fn new(seed: u64) -> Self {
83 Self { state: seed }
84 }
85 fn next_u64(&mut self) -> u64 {
86 self.state = self
87 .state
88 .wrapping_mul(6364136223846793005)
89 .wrapping_add(1442695040888963407);
90 self.state
91 }
92 fn next_f64(&mut self) -> f64 {
93 (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
94 }
95 fn next_usize(&mut self, max: usize) -> usize {
96 (self.next_f64() * max as f64) as usize % max
97 }
98}
99
100pub fn alias_table_build(probs: &[f64]) -> (Vec<f64>, Vec<usize>) {
101 let n = probs.len();
102 let avg = 1.0 / n as f64;
103 let mut prob = vec![0.0; n];
104 let mut alias = vec![0; n];
105 let mut small = Vec::new();
106 let mut large = Vec::new();
107 let mut scaled: Vec<f64> = probs.iter().map(|p| p * n as f64).collect();
108 for (i, &si) in scaled.iter().enumerate() {
109 if si < 1.0 {
110 small.push(i);
111 } else {
112 large.push(i);
113 }
114 }
115 while let (Some(s), Some(l)) = (small.pop(), large.pop()) {
116 prob[s] = scaled[s];
117 alias[s] = l;
118 scaled[l] = scaled[l] + scaled[s] - 1.0;
119 if scaled[l] < 1.0 {
120 small.push(l);
121 } else {
122 large.push(l);
123 }
124 }
125 for &l in &large {
126 prob[l] = 1.0;
127 }
128 for &s in &small {
129 prob[s] = 1.0;
130 }
131 let _ = avg;
132 (prob, alias)
133}
134
135pub fn alias_sample(prob: &[f64], alias: &[usize], seed: u64) -> usize {
136 let mut rng = SampRng::new(seed);
137 let n = prob.len();
138 let i = rng.next_usize(n);
139 if rng.next_f64() < prob[i] {
140 i
141 } else {
142 alias[i]
143 }
144}
145
146pub fn systematic_sampling(n_samples: usize, seed: u64) -> Vec<f64> {
147 let mut rng = SampRng::new(seed);
148 let u0 = rng.next_f64() / n_samples as f64;
149 (0..n_samples)
150 .map(|i| u0 + i as f64 / n_samples as f64)
151 .collect()
152}
153
154pub fn importance_resampling(weights: &[f64], n_samples: usize, seed: u64) -> Vec<usize> {
155 let mut rng = SampRng::new(seed);
156 let total: f64 = weights.iter().sum();
157 let normalized: Vec<f64> = weights.iter().map(|w| w / total).collect();
158 let mut cumul = vec![0.0; normalized.len()];
159 cumul[0] = normalized[0];
160 for i in 1..normalized.len() {
161 cumul[i] = cumul[i - 1] + normalized[i];
162 }
163 let mut indices = Vec::with_capacity(n_samples);
164 for _ in 0..n_samples {
165 let u = rng.next_f64();
166 let idx = cumul.partition_point(|&c| c < u);
167 indices.push(idx.min(weights.len() - 1));
168 }
169 indices
170}
171
172pub fn van_der_corput(n: usize, base: u64) -> Vec<f64> {
173 (0..n)
174 .map(|i| {
175 let mut result = 0.0;
176 let mut f = 1.0 / base as f64;
177 let mut idx = (i + 1) as u64;
178 while idx > 0 {
179 result += f * (idx % base) as f64;
180 idx /= base;
181 f /= base as f64;
182 }
183 result
184 })
185 .collect()
186}
187
188pub fn hammersley_sequence(n: usize, base: u64) -> Vec<(f64, f64)> {
189 let vdc = van_der_corput(n, base);
190 (0..n).map(|i| (i as f64 / n as f64, vdc[i])).collect()
191}
192
193pub fn weighted_sampling(items: &[f64], weights: &[f64], n_samples: usize, seed: u64) -> Vec<f64> {
194 let mut rng = SampRng::new(seed);
195 let total: f64 = weights.iter().sum();
196 let mut cumul = Vec::with_capacity(weights.len());
197 let mut s = 0.0;
198 for w in weights {
199 s += w / total;
200 cumul.push(s);
201 }
202 let mut samples = Vec::with_capacity(n_samples);
203 for _ in 0..n_samples {
204 let u = rng.next_f64();
205 let idx = cumul.partition_point(|&c| c < u).min(items.len() - 1);
206 samples.push(items[idx]);
207 }
208 samples
209}
210
211pub fn poisson_disk_sampling_1d(
212 domain_min: f64,
213 domain_max: f64,
214 min_dist: f64,
215 seed: u64,
216) -> Vec<f64> {
217 let mut rng = SampRng::new(seed);
218 let mut points = Vec::new();
219 let mut candidate = domain_min + rng.next_f64() * (domain_max - domain_min);
220 points.push(candidate);
221 let max_attempts = 30;
222 let mut active = vec![0usize];
223 while !active.is_empty() {
224 let idx = rng.next_usize(active.len());
225 let center = points[active[idx]];
226 let mut found = false;
227 for _ in 0..max_attempts {
228 let offset = min_dist + rng.next_f64() * min_dist;
229 candidate = if rng.next_f64() < 0.5 {
230 center + offset
231 } else {
232 center - offset
233 };
234 if candidate < domain_min || candidate > domain_max {
235 continue;
236 }
237 let ok = points.iter().all(|&p| (p - candidate).abs() >= min_dist);
238 if ok {
239 active.push(points.len());
240 points.push(candidate);
241 found = true;
242 break;
243 }
244 }
245 if !found {
246 active.swap_remove(idx);
247 }
248 }
249 points
250}