5.1.2. 生成范围内随机数

rand-badge cat-science-badge

问题:

你想从一个自定义标量区间内生成随机数。

解决方案:

我们继续使用 rand crate 来解决此问题。rand crate 中有一个产生范围区间的方法 Rng::gen_range,可以在半开放的 [低位,高位] 范围内生成一个随机值,随机值包含低位,不包含高位。比如我们在 [0, 10) 范围内生成一个随机值。

以下实例代码引用自开源书籍项目《Cookin’ with Rust》,笔者在其基础上稍作修改。

use rand::Rng;

fn main() {
    let mut rng = rand::thread_rng();
    
    println!("  整数:   {}", rng.gen_range(0, 10)); // 半开放范围取随机值,即包括`低位`而不包括`高位`
    println!("  浮点数: {}", rng.gen_range(0.0, 10.0));
}

代码第 1 行,我们使用 use 关键字将 rand::Rng 引入作用域。rand::Rng 是在 RngCore trait 上自动实现的扩展 trait,它实现了高层次的泛型方法。

代码第 4 行,由系统创建本地线程,作用为延迟初始化的随机数生成器。

代码第 6,7 行,分别从半开放范围取整型(0,10)和浮点型(0.0,10.0)随机值,即包括低位而不包括高位

构建并运行后,结果大抵如下所示。

  整数:   8
  浮点数: 5.352798350588619

注:你的运行结果值和笔者运行结果不一定相同。

讨论:

Rng::gen_range 方法在 [低位,高位] 范围内生成一个随机值。此范围为半开放范围,即包括低位而不包括高位。此函数针对给定范围内仅生成一个样本值的情况进行了优化。另外,此函数为均匀分布类型,如果从相同的范围重复抽样取值,执行速度将会更快。

Uniform 是一个结构体,表示样本值均匀地分布在两个界限之间。Uniform::newUniform::new_inclusive 构造给定范围内的均匀分布采样;这些函数可能会预先做一些额外的工作,以便更快地对多个值进行采样。必须特别注意:确保四舍五入不会导致采样值小于(<)低位或者大于等于(>=)高位

均匀分布:在概率论和统计学中,均匀分布也叫矩形分布,它是对称概率分布,在相同长度间隔的分布概率是可能相等的。

使用 Uniform 模块可以得到均匀分布的值。下述代码和上述代码具有相同的效果,但在相同范围内重复生成数字时,下述代码性能可能会更好。

以下实例代码引用自开源书籍项目《Cookin’ with Rust》,笔者在其基础上稍作修改。

use rand::distributions::{Distribution, Uniform};

fn main() {
    let mut rng = rand::thread_rng();
    let die = Uniform::from(1..7);

    loop {
        let throw = die.sample(&mut rng);

        println!("  丢一次骰子: {}", throw);
        
        if throw == 6 {
            break;
        }
    }
}

本实例模拟丢骰子的程序。

代码第 1 行,我们使用 use 关键字将均匀分布模块 rand::distributions::{Distribution, Uniform}; 引入作用域。其中 Distribution trait 必须引入,其将自动实现随机值的取值方法。若不引入,代码第 8 行的 sample 方法将不能使用,控制会输出如下错误:

error[E0599]: no method named `sample` found for struct `Uniform<{integer}>` in the current scope
 --> examples/rand-range-uniform.rs:8:25
  |
8 |         let throw = die.sample(&mut rng);
  |                         ^^^^^^ method not found in `Uniform<{integer}>`
  |
  = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
  |
1 | use rand::distributions::Distribution;
  |

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `randomness`

To learn more, run the command again with --verbose.

代码第 4 行,由系统创建本地线程,作用为延迟初始化的随机数生成器。

代码第 5 行,制定了均匀分布取随机值的半开放范围(1,7),包括 1 而不包括 7

代码第 8 行,使用 Distribution trait 实现的 sample 方法进行随机值的半开放范围取值。

构建并运行后,结果大抵如下所示。

  丢一次骰子: 4
  丢一次骰子: 1
  丢一次骰子: 5
  丢一次骰子: 4
  丢一次骰子: 2
  丢一次骰子: 6

注:你的运行结果值,包括丢骰子的次数和骰子面值,都和笔者运行结果不一定相同。