随机数据
#![allow(unused)] fn main() { extern crate rand; use rand::RngCore; // get some random data: let mut data = [0u8; 32]; rand::thread_rng().fill_bytes(&mut data); }
什么是随机性
随机 意味着什么? 通俗地说,这个词可以简单理解成 “出乎意料的” 或者 “未知的” , 但是我们需要对这种描述精确化。 Wikipedia 向我们展示了一个更明确的定义:
Randomness is the lack of pattern or predictability in events.
随机性 就是事情发生的时候没有模式,或者不可预测。
“没有模式”意味着没有倾向性 (no bias) ,也就是说,所有可能的值都等可能。
为了理解 随机值 , 我们需要一个情景:随机值来自于一堆什么样的数字呢?
- 举一个小例子:想想骰子,它有 1,2,3,4,5,6 总共 6 个数字, 无倾向性地(公平地)投掷,就会让每个值以 ⅙ 的概率等可能出现。
- 现在思考一个无聊的例子:自然数(1,2,3,...)。
这些数字没有界限,如果你需要你一个等可能的无倾向性的随机自然数字,
比如 1,5,1000,1 百万,1 万亿,这意味着对任何自然数
k
, 数字1, 2, ..., k
都是所有自然数的无限小的一部分, 即从自然数中选一个无倾向性的值的概率等于1/∞ = 0
。 换言之,对于任何一个自然数,我们总是觉得这个无倾向性的随机值大一些。 而这是不可能的,所以自然数中不会有这样的无倾向性的值。 - 还有一个例子:0 到 1 之间的实数。
实数包括所有的分数、像 π 和 √2 的无理数,以及它们的倍数。
所以在
(0, 1)
这么小的范围内也有无数种可能。
从而简单地说成 “所有可能的结果是等可能的” 这句话还不够。 我们用另外一种方式来解释 “没有模式”: 每个等长的区间具有等可能性。 比如我们能把0,1
区间分成0,½
和½,1
, 然后投掷一枚硬币决定随机样本来自于哪个区间。 假设选择了½,1
,然后投掷另一枚硬币来选择½,¾
还是¾,1
, 这把随机值限制在¼
的区间大小上。 重复这样的做法,直到选择一个0
和1
之间的、达到我们想要的精度的随机值。 尽管我们应该意识到,我们没有选择一个“准确的”值,而是选择一个小区间。
上面所定义(或者没能定义)的就是均匀的随机数分布,或者简单说 均匀分布 (uniform distributions) 。之后我们也会看到非均匀分布。 值得注意的是,均匀分布不意味着样本会 有规律地 散布,比如你掷 6 枚骰子,你不太可能得到 1, 2, 3, 4, 5, 6 。
让我们回到计算上,现在可以定义在以下几个情况下定义一个均匀分布的随机值:
u32
:0 到u32::MAX
之间等可能的随机数BigInt
:因为这个类型没有上界,所以无法生成一个无倾向性的随机值。 这个类型的值可能会无穷大,使用无穷多的内存。f64
:将这个类型近似地看作实数,而且依照惯例,把范围限制在 0 到 1 内; 除非另外被指定。稍后会谈到使用的惯例有哪些。 现在,请注意这个类型生成 52-53 位精度的数字, 具体取决于使用哪种转换方式,输出结果的误差会落在ε
或ε/2
内, 其中1+ε
代表比1
大的最小的数字。
随机数据
正如上面讨论的, 随机数 如果脱离情景来谈 是没有意义的。 随机数据 通常指 一列随机字节,其中每个字节上,等可能地存在一个 256 种情况的值。
RngCore::fill_bytes
就生成了这样的一列随机字节。
如果具有正确长度的无倾向性随机字节序列被解读成一个整数,比如 u32
或 u64
,
那么这个结果就是无倾向性的整数。
由于这种类型转换是不重要的, RngCore::next_u32
和 RngCore::next_u64
从某种解读看是同一张 trait 。
实际上,这种类型转换是与之有关的另一个话题:
算法生成器通常与整数打交道,从而可以转换为你所需要的任何类型的随机数据。