fn fill_buffer_with_random_data(buf: &mut [u8])
由于这基本上需要与一些物理设备通信,因此该任务由操作系统处理。不同的操作系统提供不同的api,这超出了本文的范围(以及我自己的知识范围)。
这是Rust标准库的一个主要缺陷,它没有公开该功能。获取加密安全的随机数据与获取当前时间或读取标准输入属于同一类操作系统服务。可以说,它甚至更重要,因为使用此功能的大多数应用程序都对安全性至关重要。
fn random_u32(state: &mut f64) -> u32
有很多不同的算法可以做到。fastrand crate实现了一些足够接近随机性的状态。
pub fn random_numbers(seed: u32) -> impl Iterator<Item = u32> {
let mut random = seed;
std::iter::repeat_with(move || {
random ^= random << 13;
random ^= random >> 17;
random ^= random << 5;
random
})
}
这段代码是从Rust的标准库(源代码)中提取的。
pub fn random_seed() -> u64 {
std::hash::Hasher::finish(&std::hash::BuildHasher::build_hasher(
&std::collections::hash_map::RandomState::new(),
))
}
好的PRNG会给你一个u32数字序列,其中每个数字都可能和其他数字一样。你可以使用random_u32() % 10将其转换为从0到10的数字。这对于大多数目的来说已经足够好了,但却无法通过严格的统计测试。因为232不能被10整除,0出现的频率会比9略高。有一种算法可以正确地做到这一点(如果random_u32()非常大,并且在232除以10后余数落入范围,则将其丢弃并重试)。
有时你想使用random_u32()来生成其他类型的随机事物,比如3D球体上的随机点,或者随机排列。
球面:在单位立方体中生成随机点;如果它也在单位球中,则将其投射到表面上,否则将其扔掉并重试。
排列:选择一个随机元素为第一,然后从其余元素中选择一个随机元素为第二,以此类推的简单算法。
有一些库提供了这样的算法集合。例如,fastrand包括最常见的,如生成范围内的数字,生成浮点数或变换切片。
use std::cell::Cell;
pub fn thread_local_random_u32() -> u32 {
thread_local! {
static STATE: Cell<u64> = Cell::new(random_seed())
}
STATE.with(|cell| {
let mut state = cell.get();
let result = random_u32(&mut state);
cell.set(state);
result
})
}
使用不可预测的数据生成随机数据
使用一致的随机算法生成随机数据