3.5 为 RNG 设置种子

原文链接: https://rust-random.github.io/book/guide-seeding.html

如前所述,伪随机数生成器(PRNG)的输出由其初始状态决定。

部分 PRNG 定义指定了如何从密钥生成初始状态,密码学生成器通常以字节序列为密钥,小型 PRNG 往往只需一个字。我们通过 SeedableRng trait 为所有生成器形式化这一过程。

注意:播种并不意味着结果可复现。为此需要使用具名 RNG 且算法固定(例如 ChaCha12Rng 而非 StdRng)。另请参阅可复现性。

Seed 类型

我们要求所有可播种 RNG 定义满足 AsMut<[u8]> + Default + Sized 的 Seed 类型(通常是固定 N 的 [u8; N])。 我们建议非密码学 PRNG 使用 [u8; 12] 或更大,密码学 PRNG 使用 [u8; 32]。

PRNG 可直接用该值通过 SeedableRng::from_seed 播种。

从……播种

新鲜熵

使用 rand::make_rng() 可轻松使用新鲜种子:

1
2
3
4
5
6
7
use rand::prelude::*;
use rand::rngs::ChaCha20Rng;

fn main() {
    let mut rng: ChaCha20Rng = rand::make_rng();
    println!("{}", rng.random_range(0..100));
}

注意这需要 rand_core 启用 getrandom 特性。

另一个 RNG

显然,可用另一个 RNG 填充种子。我们为此提供了便捷方法:

1
2
3
4
5
6
use rand::prelude::*;

fn main() {
    let mut rng = SmallRng::from_rng(&mut rand::rng());
    println!("{}", rng.random_range(0..100));
}

但若想保存密钥稍后使用,需要更明确一些:

1
2
3
4
5
6
7
8
9
use rand::prelude::*;
use rand::rngs::ChaCha8Rng;

fn main() {
    let mut seed: <ChaCha8Rng as SeedableRng>::Seed = Default::default();
    rand::rng().fill(&mut seed);
    let mut rng = ChaCha8Rng::from_seed(seed);
    println!("{}", rng.random_range(0..100));
}

必要警告:少数简单 PRNG(尤其是 XorShiftRng)从同类型生成器播种时表现很差(此处 Xorshift 会生成克隆)。对密码学 PRNG 这不是问题;其他情况建议从不同类型生成器播种。ChaCha8Rng 是确定性主生成器的优秀选择(密码学用途请优先使用 12 轮或更高变体)。

简单数字

对某些应用(尤其是模拟),你可能只需要一系列不同、固定的随机数种子,例如 1、2、3 等。

SeedableRng::seed_from_u64 正是为此设计。内部使用简单 PRNG 从输入数字填充种子位,并提供良好的位雪崩(使 0 和 1 等相近数字转化为非常不同的种子和独立的 RNG 序列)。

1
2
3
4
5
6
7
use rand::prelude::*;
use rand::rngs::ChaCha8Rng;

fn main() {
    let mut rng = ChaCha8Rng::seed_from_u64(2);
    println!("{}", rng.random_range(0..100));
}

注意 64 位或更少的数字不可能安全,因此不应用于密码学或赌博游戏等应用。

字符串或任何可哈希数据

假设让用户输入字符串为随机数生成器播种。理想情况下,字符串的每个部分都应影响生成器,对字符串做微小改动应产生完全独立的生成器序列。

可通过哈希函数将所有输入数据压缩为哈希结果,再用该结果为生成器播种。rand_seeder crate 正是为此设计。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
use rand::prelude::*;
use rand::rngs::Xoshiro256PlusPlus;
use rand_seeder::{Seeder, SipHasher};

fn main() {
    // 一行搞定:
    let mut rng: Xoshiro256PlusPlus = Seeder::from("stripy zebra").into_rng();
    println!("{}", rng.random::<char>());

    // 若要更明确,先创建 SipRng:
    let hasher = SipHasher::from("a sailboat");
    let mut hasher_rng = hasher.into_rng();
    // (注意:hasher_rng 是完整 RNG,可直接使用。)

    // 现在用 hasher_rng 创建种子:
    let mut seed: <Xoshiro256PlusPlus as SeedableRng>::Seed = Default::default();
    hasher_rng.fill(&mut seed);

    // 从该种子创建 RNG:
    let mut rng = Xoshiro256PlusPlus::from_seed(seed);
    println!("{}", rng.random::<char>());
}

注意 rand_seeder 不适用于密码学用途。 它不是密码哈希器;此类应用必须使用 Argon2 等密钥派生函数。

最后修改 August 23, 2026: 更新 (499855b16)