随机数据

#![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 , 这把随机值限制在 ¼ 的区间大小上。 重复这样的做法,直到选择一个 01 之间的、达到我们想要的精度的随机值。 尽管我们应该意识到,我们没有选择一个“准确的”值,而是选择一个小区间。

上面所定义(或者没能定义)的就是均匀的随机数分布,或者简单说 均匀分布 (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 就生成了这样的一列随机字节。

如果具有正确长度的无倾向性随机字节序列被解读成一个整数,比如 u32u64, 那么这个结果就是无倾向性的整数。 由于这种类型转换是不重要的, RngCore::next_u32RngCore::next_u64 从某种解读看是同一张 trait 。 实际上,这种类型转换是与之有关的另一个话题: 算法生成器通常与整数打交道,从而可以转换为你所需要的任何类型的随机数据。