Skip to content

Commit bc94271

Browse files
authored
Add Permuted Congruential Generator (rust-lang#311)
1 parent 521f5f3 commit bc94271

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
* [Simpson's Rule](https://github.com/TheAlgorithms/Rust/blob/master/src/math/simpson_integration.rs)
7171
* [Fast Fourier Transform](https://github.com/TheAlgorithms/Rust/blob/master/src/math/fast_fourier_transform.rs)
7272
* [Armstrong Number](https://github.com/TheAlgorithms/Rust/blob/master/src/math/armstrong_number.rs)
73+
* [Permuted Congruential Random Number Generator](https://github.com/TheAlgorithms/Rust/blob/master/src/math/random.rs)
7374
* Searching
7475
* [Binary Search](https://github.com/TheAlgorithms/Rust/blob/master/src/searching/binary_search.rs)
7576
* [Binary Search Recursive](https://github.com/TheAlgorithms/Rust/blob/master/src/searching/binary_search_recursive.rs)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ These are for demonstration purposes only.
5555
- [x] [Simpson's Rule for Integration](./src/math/simpson_integration.rs)
5656
- [x] [Fast Fourier Transform](./src/math/fast_fourier_transform.rs)
5757
- [x] [Armstrong Number](./src/math/armstrong_number.rs)
58+
- [x] [Permuted Congruential Random Number Generator](./src/math/random.rs)
5859

5960
## [Dynamic Programming](./src/dynamic_programming)
6061

src/math/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ mod perfect_numbers;
1313
mod pollard_rho;
1414
mod prime_check;
1515
mod prime_numbers;
16+
mod random;
1617
mod sieve_of_eratosthenes;
1718
mod simpson_integration;
1819
mod square_root;
@@ -38,6 +39,7 @@ pub use self::perfect_numbers::perfect_numbers;
3839
pub use self::pollard_rho::{pollard_rho_factorize, pollard_rho_get_one_factor};
3940
pub use self::prime_check::prime_check;
4041
pub use self::prime_numbers::prime_numbers;
42+
pub use self::random::PCG32;
4143
pub use self::sieve_of_eratosthenes::sieve_of_eratosthenes;
4244
pub use self::simpson_integration::simpson_integration;
4345
pub use self::square_root::square_root;

src/math/random.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
Permuted Congruential Generator
3+
https://en.wikipedia.org/wiki/Permuted_congruential_generator
4+
5+
Note that this is _NOT_ intended for serious applications. Use this generator
6+
at your own risk and only use your own values instead of the default ones if
7+
you really know what you are doing.
8+
*/
9+
pub struct PCG32 {
10+
state: u64,
11+
multiplier: u64,
12+
increment: u64,
13+
}
14+
15+
pub const PCG32_MULTIPLIER: u64 = 6364136223846793005_u64;
16+
pub const PCG32_INCREMENT: u64 = 1442695040888963407_u64;
17+
18+
pub struct IterMut<'a> {
19+
pcg: &'a mut PCG32,
20+
}
21+
22+
impl PCG32 {
23+
/// `stream` should be less than 1 << 63
24+
pub fn new(seed: u64, multiplier: u64, stream: u64) -> Self {
25+
// We should make sure that increment is odd
26+
let increment = (stream << 1) | 1;
27+
let mut pcg = PCG32 {
28+
state: seed.wrapping_add(increment),
29+
multiplier,
30+
increment,
31+
};
32+
pcg.next();
33+
pcg
34+
}
35+
pub fn new_default(seed: u64) -> Self {
36+
let multiplier = PCG32_MULTIPLIER;
37+
let increment = PCG32_INCREMENT;
38+
let mut pcg = PCG32 {
39+
state: seed.wrapping_add(increment),
40+
multiplier,
41+
increment,
42+
};
43+
pcg.next();
44+
pcg
45+
}
46+
#[inline]
47+
pub fn next(&mut self) {
48+
self.state = self
49+
.state
50+
.wrapping_mul(self.multiplier)
51+
.wrapping_add(self.increment);
52+
}
53+
#[inline]
54+
/// Advance the PCG by `delta` steps in O(lg(`delta`)) time. By passing
55+
/// a negative i64 as u64, it can go back too.
56+
pub fn advance(&mut self, mut delta: u64) {
57+
let mut acc_mult = 1u64;
58+
let mut acc_incr = 0u64;
59+
let mut curr_mlt = self.multiplier;
60+
let mut curr_inc = self.increment;
61+
while delta > 0 {
62+
if delta & 1 != 0 {
63+
acc_mult = acc_mult.wrapping_mul(curr_mlt);
64+
acc_incr = acc_incr.wrapping_mul(curr_mlt).wrapping_add(curr_inc);
65+
}
66+
curr_inc = curr_mlt.wrapping_add(1).wrapping_mul(curr_inc);
67+
curr_mlt = curr_mlt.wrapping_mul(curr_mlt);
68+
delta >>= 1;
69+
}
70+
self.state = acc_mult.wrapping_mul(self.state).wrapping_add(acc_incr);
71+
}
72+
#[inline]
73+
pub fn get_u32(&mut self) -> u32 {
74+
let mut x = self.state;
75+
let count = (x >> 59) as u32;
76+
77+
self.next();
78+
79+
x ^= x >> 18;
80+
((x >> 27) as u32).rotate_right(count)
81+
}
82+
#[inline]
83+
pub fn get_u64(&mut self) -> u64 {
84+
self.get_u32() as u64 ^ ((self.get_u32() as u64) << 32)
85+
}
86+
#[inline]
87+
pub fn get_u16(&mut self) -> (u16, u16) {
88+
let res = self.get_u32();
89+
(res as u16, (res >> 16) as u16)
90+
}
91+
#[inline]
92+
pub fn get_u8(&mut self) -> (u8, u8, u8, u8) {
93+
let res = self.get_u32();
94+
(
95+
res as u8,
96+
(res >> 8) as u8,
97+
(res >> 16) as u8,
98+
(res >> 24) as u8,
99+
)
100+
}
101+
#[inline]
102+
pub fn get_state(&self) -> u64 {
103+
self.state
104+
}
105+
pub fn iter_mut(&mut self) -> IterMut {
106+
IterMut { pcg: self }
107+
}
108+
}
109+
110+
impl<'a> Iterator for IterMut<'a> {
111+
type Item = u32;
112+
fn next(&mut self) -> Option<Self::Item> {
113+
Some(self.pcg.get_u32())
114+
}
115+
}
116+
117+
#[cfg(test)]
118+
mod tests {
119+
use super::*;
120+
121+
#[test]
122+
fn no_birthday() {
123+
// If the distribution is not almost uniform, the probability of
124+
// birthday paradox increases. For n=2^32 and k=1e5, the probability
125+
// of not having a collision is about (1 - (k+1)/n) ^ (k/2) which is
126+
// 0.3121 for this (n, k).
127+
// So this test is a (dumb) test for distribution, and for speed. This
128+
// is only basic sanity checking, as the actual algorithm was
129+
// rigorously tested by others before.
130+
let numbers = 1e5 as usize;
131+
let mut pcg = PCG32::new_default(314159);
132+
let mut pcg2 = PCG32::new_default(314159);
133+
assert_eq!(pcg.get_u32(), pcg2.get_u32());
134+
let mut randoms: Vec<u32> = pcg.iter_mut().take(numbers).collect::<Vec<u32>>();
135+
pcg2.advance(1000);
136+
assert_eq!(pcg2.get_u32(), randoms[1000]);
137+
pcg2.advance((-1001_i64) as u64);
138+
assert_eq!(pcg2.get_u32(), randoms[0]);
139+
randoms.sort_unstable();
140+
randoms.dedup();
141+
assert_eq!(randoms.len(), numbers);
142+
}
143+
}

0 commit comments

Comments
 (0)