rust rand包

官方文档

注意rand从0.10.0开始是把use rand::Rng替换为了use rand::RngExt
以下案例可以使用rand的0.9.0

数字

随机数字

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    // 随机数
    let a: u8 = rng.random();        
    let b: i32 = rng.random();       
    let c: f64 = rng.random();      
    println!("{}", a);  // 0..=255
    println!("{}", b);  // 全范围 i32
    println!("{}", c);  // 0.0 <= x < 1.0
}

随机数字(范围)

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    // 范围: 1 <= x  < 100
    let n: u8 = rng.random_range(1..100);
    println!("{}", n);
    // 包含右边界(推荐写法): 1 <= x <= 100
    let n: u8 = rng.random_range(1..=100);
    println!("{}", n);
    // 浮点数: 0.0 <= x < 10.0
    let f: f64 = rng.random_range(0.0..10.0);
    println!("{}", f);
}

bool

随机bool值

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    let d: bool = rng.random();
    println!("{}", d);
}

概率随机bool值

random_bool

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    // 概率为 0.9
    let res = rng.random_bool(0.9);
    println!("{}", res);
}

random_ratio

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    // 概率为 2/3
    let res = rng.random_ratio(2, 3);
    println!("{}", res);
}

random_bool 与random_ratio区别

方法 参数类型 概率计算方式
random_bool(p) f64 true 的概率 = p
random_ratio(n,d) u32/u64 true 的概率 = n/d
rng.random_bool(0.5)  ≈ rng.random_ratio(1, 2)
rng.random_bool(0.25) ≈ rng.random_ratio(1, 4)
rng.random_bool(0.75) ≈ rng.random_ratio(3, 4)
  • 浮点概率 → 用 random_bool(p),语义清楚

  • 分数概率(整数比例) → 用 random_ratio(n, d),避免浮点误差

其他

rng.random

rng.random() 方法能够生成数组和元组(生成的元祖最多 12 个元素,大于 12 个需要自己手动实现trait),只要可以生成所有元素类型

use rand::Rng;

fn main() {
    let mut rng = rand::rng();

    let arr: [u8; 4] = rng.random();
    println!("{:?}", arr);
    let arr: [u8; 34] = rng.random();
    println!("{:?}", arr);
}

rng.fill

用途类型 解释方式 示例用途
🔐 密钥 / 加密随机值 直接用作密钥字节数组 AES key, HMAC key, JWT secret
🧂 盐值(salt) 加密存储密码时混入 bcrypt / PBKDF2
🆔 标识符 / Token 转换成 base64 / hex API token, session id
📦 随机初始化 随机化结构体或数据初始状态 游戏随机数、随机颜色
🎲 随机数字生成基础 转换为 u64/u32 或浮点 通用随机逻辑
🔊 噪声 / 数据扰动 用于加噪算法或测试 图像噪声、测试数据
use rand::{rng, Rng};

fn main() {
    let mut rng = rng();
  	// 8 个随机字节(u8)组成的数组,每个字节的取值范围是 `0–255`,完全随机且不可预测。
    let mut buf = [0u8; 8]; // 8字节的缓冲区

    rng.fill(&mut buf); // 🔥 直接填满随机字节
    println!("{:?}", buf);
}

转成十六进制(可读 token)

用于session token、API key、JWT secret、随机标识符。

需要 cargo add hex

use rand::{rng, Rng};
use hex;

fn main() {
    let mut rng = rng();
    let mut key = [0u8; 8];

    rng.fill(&mut key);
    let token = hex::encode(key);
    println!("Token: {}", token);
}
Token: 26e9af71d7dbe284

转成 u64 随机整数

use rand::{rng, Rng};

fn main() {
    let mut rng = rng();
    let mut key = [0u8; 8];

    rng.fill(&mut key);
    println!("{:?}", key);
    let num = u64::from_le_bytes(key);
    println!("Random u64: {}", num);
}
[10, 27, 113, 211, 121, 42, 104, 29]
Random u64: 2118990327404632842
公式
u8转u64 = u8::MAX + 1 = 256
u16转u64 = u16::MAX + 1
u32转u64 = u32::MAX + 1
num = 10 * 256^0
    + 27 * 256^1
    + 113 * 256^2
    + 211 * 256^3
    + 121 * 256^4
    + 42 * 256^5
    + 104 * 256^6
    + 29 * 256^7

rng.fill_bytes

专用字节填充(仅 u8)

uuid

use rand::{rng, RngCore};
use uuid::Uuid;


fn main() {
    let mut rng = rng();

    let mut bytes = [0u8; 16];
    rng.fill_bytes(&mut bytes);
    let id = Uuid::from_bytes(bytes);
    println!("UUID: {}", id);
}

fill 和fill_bytes区别

虽然 fill 也能填充 &mut [u8](因为 u8 实现了 Distribution),但两者有本质区别

  1. fill_bytes 更适合直接生成原始随机字节,语义明确。
  2. fill 是高层泛型接口,功能更广,但对固定字节数组来说稍显抽象。

随机变量的迭代器

use rand::{rngs::SmallRng, Rng, SeedableRng};

fn main() {
    let rng = SmallRng::seed_from_u64(0);
    let v: Vec<i32> = rng.random_iter().take(5).collect();
    println!("{:?}", v);
}

随机洗牌

shuffle()choose()choose_multiple()partial_shuffle() 等都是 rand::seq::SliceRandom 提供的扩展方法

use rand::{self, seq::SliceRandom, prelude::IndexedRandom};
  
fn main() {
    let mut rng = rand::rng();
    let mut arr = [1, 2, 3, 4, 5];

    // 打乱,shuffle方法来自SliceRandom trait
    arr.shuffle(&mut rng);
    println!("shuffled: {:?}", arr);

    // 从切片中随机选一个
    if let Some(&val) = arr.choose(&mut rng) {
        println!("chosen one: {}", val);
    }

    // 随机选多个(比如 2 个不同元素)
    let choices: Vec<&i32> = arr.choose_multiple(&mut rng, 2).collect();
    println!("two chosen: {:?}", choices);
}

RNG 类型和来源

rand::rng() 实际上是快捷方式,但了解底层 RNG 更好:

  • thread_rng() → 线程本地 RNG,默认推荐
    • rand::rng()只是对thread_rng()做了封装
  • OsRng → 系统随机数源,适合加密用途
  • SmallRng, StdRng → 可种子化、可复制、用于测试和可重复随机

OsRng

use rand::rngs::OsRng;
use rand::TryRngCore; // ⚠️ 必须导入 trait

fn main() {
    let mut key = [0u8; 16];
    let mut rng = OsRng::default();

    rng.try_fill_bytes(&mut key).expect("生成随机字节失败");
    
    println!("生成的安全随机密钥:{:x?}", key);
}
生成的安全随机密钥:[32, 4f, df, 7f, 22, db, 48, e5, 71, e8, f9, 82, 33, 1c, ba, b5]

0u8和 u8 区别

let x: u8 = 10;  // x 是 u8 类型
let x = u8; // ❌ 编译错误:expected value, found type `u8`

// 0u8 是具体的值,同时带上了类型标注
let x: u8 = 0;

SmallRng

seed_from_u64 值一样可重复

use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};

fn main() {
    // 用固定种子创建可重复的 RNG
    let mut rng = SmallRng::seed_from_u64(42);

    // 生成随机数
    let x: u32 = rng.random();
    let y: f64 = rng.random();
    println!("x = {}, y = {}", x, y);
}

重复案例

SmallRng和StdRng 效果一样的

use rand::rngs::SmallRng;
use rand::{Rng, SeedableRng};

fn main() {
    let mut rng1 = SmallRng::seed_from_u64(42);
    let mut rng2 = SmallRng::seed_from_u64(42);
    println!("{:?}", rng1);
    println!("{:?}", rng2);
}
SmallRng(Xoshiro256PlusPlus { s: [13679457532755275413, 2949826092126892291, 5139283748462763858, 6349198060258255764] })
SmallRng(Xoshiro256PlusPlus { s: [13679457532755275413, 2949826092126892291, 5139283748462763858, 6349198060258255764] })

StdRng

seed_from_u64 值一样可重复

use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};

fn main() {
    let mut rng = StdRng::seed_from_u64(42);

    let x: i32 = rng.random();
    let y: f64 = rng.random();
    println!("x = {}, y = {}", x, y);
}
RNG 类型 加密安全 可种子化 性能 内存占用 典型用途
OsRng 中等 密钥、token、nonce
SmallRng 游戏、模拟、测试
StdRng 中高 通用 RNG、可重复序列

安全随机 vs 普通随机

  • fill_bytes / rng.random() 使用的是 普通随机数(PRNG)
  • 对于加密用途,推荐使用 加密安全 RNGOsRng
    • AES key、JWT secret、session token 等必须用加密安全 RNG
use rand::rngs::OsRng;
use rand::TryRngCore;

fn main() {
    let mut key = [0u8; 16];
    let _ = OsRng.try_fill_bytes(&mut key); 
    println!("{:?}", key);
}

生成更多复杂随机结构

  • 可以生成 自定义结构体的随机值,前提是每个字段都实现了 Distribution 或通过 rng.fill 填充
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

let mut p = Point { x: 0, y: 0 };
rng.fill(&mut p); // 每个字段随机

权重概率函数WeightedIndex

  • 除了 random_bool / random_ratiorand 还可以生成 自定义离散概率分布
    • WeightedIndex → 按权重随机选择索引
    • Uniform → 自定义范围浮点或整数
use rand::SeedableRng;
use rand::rngs::StdRng;
use rand::distr::{Distribution, weighted::WeightedIndex};

fn main() {
    let mut rng = StdRng::seed_from_u64(42);
    let equipment = ["普通装备", "稀有装备", "史诗装备"];
    let weights = [80, 15, 5];

    let dist = WeightedIndex::new(&weights).unwrap();
    for _ in 0..5 {
      	// 本质随机获取weights下表,会更具权重分布
        let idx = dist.sample(&mut rng);
        println!("掉落:{}", equipment[idx]);
    }
}

equipment 和weights 对应

use rand::SeedableRng;
use rand::rngs::StdRng;
use rand::distr::{Distribution, weighted::WeightedIndex};


#[derive(Debug)]
struct WeightedOption<'a> {
    name: &'a str,  // 选项内容(如装备名)
    weight: u32,    // 对应权重(非负)
}


fn main() {
    let mut rng = StdRng::seed_from_u64(42);
    
    // 1. 定义所有选项(数量再多也不怕,结构化关联不会乱)
    let weighted_equipments = [
        WeightedOption { name: "普通武器", weight: 50 },
        WeightedOption { name: "稀有武器", weight: 30 },
        WeightedOption { name: "史诗武器", weight: 15 },
        WeightedOption { name: "传说武器", weight: 5 },
        WeightedOption { name: "神级武器", weight: 1 }, // 1/101 ≈ 0.99% 概率
        // 后续增减选项,直接在这里添加/删除,权重自动对应
    ];

    // 2. 自动拆分出 equipment 列表和 weights 列表(确保长度一致)
    let equipments: Vec<&str> = weighted_equipments.iter().map(|x| x.name).collect();
    let weights: Vec<u32> = weighted_equipments.iter().map(|x| x.weight).collect();

    let dist = WeightedIndex::new(&weights).unwrap();
    for _ in 0..5 {
        let idx = dist.sample(&mut rng);
        println!("掉落:{}", equipments[idx]);
    }
}

武器分类(细化)

use std::array;

use rand::SeedableRng;
use rand::rngs::StdRng;
use rand::distr::{Distribution, weighted::WeightedIndex};


#[derive(Debug)]
struct WeightedOption<'a> {
    name: &'a str,  // 选项内容(如装备名)
    weight: u32,    // 对应权重(非负)
    mode: &'a [ModeOption<'a>],
}

#[derive(Debug)]
struct ModeOption<'a> {
    name: &'a str,  // 选项内容(如装备名)
    weight: u32,    // 对应权重(非负)
}


fn main() {
    let mut rng = StdRng::seed_from_u64(42);

    // 普通武器
    let a = [
        ModeOption { name: "普通武器-1", weight: 54 },
        ModeOption { name: "普通武器-2", weight: 30 },
        ModeOption { name: "普通武器-3", weight: 10 },
        ModeOption { name: "普通武器-4", weight: 5 },
        ModeOption { name: "普通武器-5", weight: 1 },
    ];
    // 稀有武器
    let aa = [
        ModeOption { name: "稀有武器🌙🌙-1", weight: 54 },
        ModeOption { name: "稀有武器🌙🌙-2", weight: 30 },
        ModeOption { name: "稀有武器🌙🌙-3", weight: 10 },
        ModeOption { name: "稀有武器🌙🌙-4", weight: 5 },
        ModeOption { name: "稀有武器🌙🌙-5", weight: 1 },
    ];
    // 史诗武器
    let aaa = [
        ModeOption { name: "史诗武器⭐️-1", weight: 50 },
        ModeOption { name: "史诗武器⭐️-2", weight: 30 },
        ModeOption { name: "史诗武器⭐️-3", weight: 15 },
        ModeOption { name: "史诗武器⭐️-4", weight: 5 },
        ModeOption { name: "史诗武器⭐️-5", weight: 1 },
    ];
    // 传说武器
    let sss = [
        ModeOption { name: "传说武器⭐️⭐️⭐️-1", weight: 54 },
        ModeOption { name: "传说武器⭐️⭐️⭐️-2", weight: 30 },
        ModeOption { name: "传说武器⭐️⭐️⭐️-3", weight: 10 },
        ModeOption { name: "传说武器⭐️⭐️⭐️-4", weight: 5 },
        ModeOption { name: "传说武器⭐️⭐️⭐️-5", weight: 1 },
    ];
    // 神级武器
    let sssss = [
        ModeOption { name: "神级武器⭐️⭐️⭐️⭐️⭐️-1", weight: 54 },
        ModeOption { name: "神级武器⭐️⭐️⭐️⭐️⭐️-2", weight: 30 },
        ModeOption { name: "神级武器⭐️⭐️⭐️⭐️⭐️-3", weight: 10 },
        ModeOption { name: "神级武器⭐️⭐️⭐️⭐️⭐️-4", weight: 5 },
        ModeOption { name: "神级武器⭐️⭐️⭐️⭐️⭐️-5", weight: 1 },
    ];

    
    // 1. 定义所有选项(数量再多也不怕,结构化关联不会乱)
    let weighted_equipments = [
        WeightedOption { name: "普通武器", weight: 60 , mode: &a},
        WeightedOption { name: "稀有武器", weight: 30 , mode: &aa},
        WeightedOption { name: "史诗武器", weight: 20 , mode: &aaa},
        WeightedOption { name: "传说武器", weight: 10 , mode: &sss},
        WeightedOption { name: "神级武器", weight: 5 , mode: &sssss},
        // 后续增减选项,直接在这里添加/删除,权重自动对应
    ];

    // 2. 自动拆分出 equipment 列表和 weights 列表(确保长度一致)
    let equipments: Vec<&WeightedOption> = weighted_equipments.iter().map(|x: &WeightedOption| x).collect();
    let weights: Vec<u32> = weighted_equipments.iter().map(|x| x.weight).collect();

    let dist = WeightedIndex::new(&weights).unwrap();
    for _ in 0..10 {
        let idx = dist.sample(&mut rng);
        let ww:&WeightedOption = equipments[idx];
        
        let equipments11: Vec<&str> = ww.mode.iter().map(|x| x.name).collect();
        let weights22: Vec<u32> = ww.mode.iter().map(|x| x.weight).collect();
        let dist22 = WeightedIndex::new(&weights22).unwrap();

        let idx22 = dist22.sample(&mut rng);
        println!("掉落:{}", equipments11[idx22]);
    }
}
掉落:普通武器-1
掉落:普通武器-2
掉落:史诗武器⭐️-2
掉落:神级武器⭐️⭐️⭐️⭐️⭐️-1
掉落:神级武器⭐️⭐️⭐️⭐️⭐️-1
掉落:稀有武器🌙🌙-1
掉落:普通武器-2
掉落:普通武器-3
掉落:稀有武器🌙🌙-1
掉落:普通武器-1
posted @ 2026-03-05 22:49  lxd670  阅读(106)  评论(0)    收藏  举报