#[derive(Debug, Clone)]
pub struct SplitMix64 {
state: u64,
}
impl SplitMix64 {
#[must_use]
pub fn new(seed: u64) -> Self {
SplitMix64 { state: seed }
}
pub fn next_u64(&mut self) -> u64 {
self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.state;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn below(&mut self, n: u64) -> u64 {
self.next_u64() % n
}
#[must_use]
pub fn bit(&mut self) -> bool {
self.next_u64() & 1 == 1
}
}
#[must_use]
#[allow(
clippy::cast_possible_truncation,
reason = "j ∈ [0,i] ≤ len ≤ usize::MAX,回写 usize 不会截断"
)]
pub fn shuffle(len: usize, seed: u64) -> Vec<usize> {
let mut a: Vec<usize> = (0..len).collect();
let mut rng = SplitMix64::new(seed);
for i in (1..len).rev() {
let j = rng.below(i as u64 + 1) as usize;
a.swap(i, j);
}
a
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splitmix64_matches_the_published_reference_vectors() {
let take5 = |seed: u64| -> [u64; 5] {
let mut r = SplitMix64::new(seed);
[r.next_u64(), r.next_u64(), r.next_u64(), r.next_u64(), r.next_u64()]
};
assert_eq!(
take5(0),
[
0xE220_A839_7B1D_CDAF,
0x6E78_9E6A_A1B9_65F4,
0x06C4_5D18_8009_454F,
0xF88B_B8A8_724C_81EC,
0x1B39_896A_51A8_749B,
],
"seed 0 的前五个输出与 Vigna 参考实现不符"
);
assert_eq!(
take5(1),
[
0x910A_2DEC_8902_5CC1,
0xBEEB_8DA1_658E_EC67,
0xF893_A2EE_FB32_555E,
0x71C1_8690_EE42_C90B,
0x71BB_54D8_D101_B5B9,
],
"seed 1 的前五个输出与 Vigna 参考实现不符"
);
assert_eq!(
take5(0xDEAD_BEEF),
[
0x4ADF_B90F_68C9_EB9B,
0xDE58_6A31_41A1_0922,
0x021F_BC2F_8E1C_FC1D,
0x7466_CE73_7BE1_6790,
0x3BFA_8764_F685_BD1C,
],
"seed 0xDEADBEEF 的前五个输出与 Vigna 参考实现不符"
);
assert_eq!(
take5(1_234_567),
[
6_457_827_717_110_365_317,
3_203_168_211_198_807_973,
9_817_491_932_198_370_423,
4_593_380_528_125_082_431,
16_408_922_859_458_223_821,
],
"seed 1234567 的前五个输出与 Rosetta Code 公布的期望值不符"
);
}
#[test]
fn deterministic_given_seed() {
assert_eq!(shuffle(78, 42), shuffle(78, 42)); }
#[test]
fn shuffle_is_permutation() {
let mut s = shuffle(78, 12345); s.sort_unstable();
assert_eq!(s, (0..78).collect::<Vec<_>>()); }
#[test]
fn different_seeds_differ() {
assert_ne!(shuffle(78, 1), shuffle(78, 2));
}
#[test]
fn bit_and_below_cover() {
let mut rng = SplitMix64::new(7);
let mut seen_t = false;
let mut seen_f = false;
for _ in 0..64 {
if rng.bit() {
seen_t = true;
} else {
seen_f = true;
}
}
assert!(seen_t && seen_f);
let mut rng2 = SplitMix64::new(7);
for _ in 0..100 {
assert!(rng2.below(6) < 6);
}
}
#[test]
fn shuffle_len_one_and_zero() {
assert_eq!(shuffle(1, 9), vec![0]);
assert!(shuffle(0, 9).is_empty());
}
use proptest::prelude::*;
proptest! {
#[test]
fn prop_splitmix_deterministic(seed in any::<u64>()) {
let (mut a, mut b) = (SplitMix64::new(seed), SplitMix64::new(seed));
for _ in 0..8 {
prop_assert_eq!(a.next_u64(), b.next_u64());
}
}
#[test]
fn prop_below_in_range(seed in any::<u64>(), n in 1u64..1_000_000) {
let mut r = SplitMix64::new(seed);
for _ in 0..16 {
prop_assert!(r.below(n) < n);
}
}
#[test]
fn prop_shuffle_is_permutation(len in 0usize..256, seed in any::<u64>()) {
let mut p = shuffle(len, seed);
prop_assert_eq!(p.len(), len);
p.sort_unstable();
prop_assert!(p.iter().copied().eq(0..len));
}
}
}