Random and Random Seed
随机数和随机种子
介绍
随机数生成在现代计算中起着至关重要的作用。它们被用于各种应用,从简单的游戏和模拟到复杂的密码学和金融模型。
伪随机数
在机器控制的算法或过程中,所产生的都是伪随机数。
所以我们可以这样说,用计算机是不可能生成随机数的。凡用计算机生成的数,肯定是用程序实现的,既然用程序实现,肯定不是随机的。因此用计算机生成的所谓随机数只能称为伪随机数。但是,伪随机数不是假随机数,这里的“伪”指的是随机是有规律,但一般周期极长,在一定条件下,可以替代真随机数。
这个世界上有真随机数吗?
这是一个哲学讨论,在一般意义上,我们认为真随机数是存在的,但真随机数的获取往往是大费周章,而且需要繁琐而高价的物理设备。
真随机数多数采用物理性随机数发生器,也就是使用物理现象产生,比如说,掷硬币、投骰子、转轮、使用电子元件的噪音或者核裂变等等。技术要求比较高,物理过程比较复杂。
随机种子(Random Seed)
与无序的随机性相比,随机种子提供了一种方法来控制随机数生成器的“随机性”,使其能够以确定的方式重现相同的随机序列。 简单来说,设置随机种子就是指定随机数生成器的初始状态。这个初始状态决定了随机数生成器的整个输出序列。一旦设置了随机种子,随后的随机数序列就会变得确定和可重复。
在计算机科学、数据科学和机器学习等领域,设置随机种子非常重要,因为它确保了实验和计算的可重复性。无论你是在分割数据集、初始化神经网络权重,还是进行蒙特卡洛模拟,随机种子都可以确保你的工作可以在不同的机器甚至不同的时间点精确复制。
当需要真正的随机性时,例如在密码学应用中,就应该避免使用固定的随机种子。
随机种子的取值
随机种子一般都会设置为不小于0的整数。
建议设置为一个大整数。
arxiv上有篇极其离谱又很有深意的文章告诉你,随机数应该设置为3407
。
真正的随机性
我们是在伪随机范围内来说“真正”随机性的,这是是相对而言的。随机种子的选取,对伪随机数的随机性至关重要,每次运行的随机种子一定要不同,所以很多时候都是用当前的时间作为随机种子的,这基本满足了大多数需求。
代码展示
示例使用的编程语言是Rust,随机数生成使用的是rand库。
use rand::{Rng, SeedableRng};
use rand_chacha;
示例生成的是随机的32位浮点数,使用的是设定的随机种子:
let mut vec = Vec::new();
for _ in 0..5 {
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(23456543245);
vec.push(rng.gen::<f32>());
}
println!("Random f32: {:?}", vec);
输出结果为:
Random f32: [0.75257635, 0.75257635, 0.75257635, 0.75257635, 0.75257635]
可以看到随机数都是一样的。那么,现在让我们把第3行移到循环外部:
let mut vec = Vec::new();
let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(23456543245);
for _ in 0..5 {
vec.push(rng.gen::<f32>());
}
println!("Random f32: {:?}", vec);
输出结果为:
Random f32: [0.75257635, 0.7354908, 0.028039217, 0.10340983, 0.017779648]
虽然这5个数看起来随机了,但是,如果你多次执行上述代码,你就会发现每次运行的结果,都是上面这5个数。这就是随机种子的作用:让随机数拥有确定性。
如果你想要真正的随机数,每次运行都是不可预期的,那么就需要让随机种子每次都产生变化。
let mut vec = Vec::new();
for _ in 0..5 {
let mut rng = rand::thread_rng();
vec.push(rng.gen::<f32>());
}
println!("Random f32: {:?}", vec);
上面的函数中,没有设定随机种子,每次都会使用一个不同的随机种子。这个随机种子的生成不同的算法选择不一样,就像有的就会选择上面提到的,使用系统当前时间作为随机种子。