Random in Rust

| 分类 Rust  | 标签 Rust 

前言

本文介绍Rust中的随机数产生方法。Rust提供了rand crate来产生随机数。

产生标准类型的随机数

常见的类型:

  • i8
  • i32
  • i64
  • u8
  • u32
  • u64
  • bool
  • float

如何产生这些类型的随机数:

random方法

rand crate提供了一个快捷的方式产生随机数

use rand::random;

这个函数可以产生出各种类型的随机数:

extern crate rand ;
use rand::{random};

fn main() {

    let r_u8  :u8   = random();
    let r_u32 :u32  = random();
    let r_u64 :u64  = random();
    let r_f64 :f64  = random();

    println!("r_u8 {} r_u32 {} r_u64 {} r_f64 {}", r_u8, r_u32, r_u64, r_f64);

    let r_i8  = random::<i8>();
    let r_i32 = random::<i32>();
    let r_i64 = random::<i64>();
    let r_f32 = random::<f32>();

    println!("r_i8 {} r_i32 {} r_i64 {} r_f32 {}", r_i8, r_i32, r_i64, r_f32);

    if random()
    {
        println!("This print sometimes appears ,sometimes cannot be seen");
    }
}

输出结果如下:

manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
r_u8 108 r_u32 2741032365 r_u64 1387371133752336062 r_f64 0.46364384148921434
r_i8 -27 r_i32 -100204817 r_i64 -5883903312845635387 r_f32 0.24733853

manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
r_u8 13 r_u32 2994006999 r_u64 10822813997525460261 r_f64 0.8190726733233058
r_i8 99 r_i32 -674518030 r_i64 8391467083991742077 r_f32 0.6915216
This print sometimes appears ,sometimes cannot be seen

我们可以看到:

  • 每次执行,结果各不相同,即达到随机的效果。
  • random()函数可以产生出各种类型的随机值,包括bool型
  • random和random()产生出[0,1)范围内的浮点数

使用thread_rng函数

rand这个crate实现了Rng这个trait,这个trait的Rng的意思为 Random Number Generator,即随机数字生成器。该trait有如下重大的API:

  • gen
  • gen_range
  • sample
  • same_iter

我们可以利用gen函数来产生出我们需要的随机数。

extern crate rand ;
use rand::{Rng};

fn main() {

    let mut rng = rand::thread_rng();
    let r_u8  :u8   = rng.gen();
    let r_u32 :u32  = rng.gen();
    let r_u64 :u64  = rng.gen();
    let r_f64 :f64  = rng.gen();

    println!("r_u8 {} r_u32 {} r_u64 {} r_f64 {}", r_u8, r_u32, r_u64, r_f64);

    let r_i8  = rng.gen::<i8>();
    let r_i32 = rng.gen::<i32>();
    let r_i64 = rng.gen::<i64>();
    let r_f32 = rng.gen::<f32>();

    println!("r_i8 {} r_i32 {} r_i64 {} r_f32 {}", r_i8, r_i32, r_i64, r_f32);

    if rng.gen()
    {
        println!("This print sometimes appears ,sometimes cannot be seen");
    }
}

我们可以看到,和之前的random函数一样,通过Rng的gen函数,一样可以获取各种类型的随机数:

输出如下:
r_u8 80 r_u32 1687585909 r_u64 8352424927451894494 r_f64 0.8696927377548344
r_i8 -57 r_i32 -1884445722 r_i64 -4327157282855795895 r_f32 0.074106276

再跑一轮
r_u8 98 r_u32 2598551176 r_u64 16337455607562742807 r_f64 0.997604506929101
r_i8 114 r_i32 1535487133 r_i64 39920825847438157 r_f32 0.9148346
This print sometimes appears ,sometimes cannot be seen

Rng的gen函数不仅仅是可以产生出标准类型,还可以产生出tuple

let tuple: (u8, i32, char) = rng.gen(); 

如果在上面的代码最后加入如下两行:

    let tuple :(u8, i32, f64, bool) = rng.gen();
    println!("{:?}", tuple);

会多输入如下内容:

(242, -1061332895, 0.3170911846965502, false)

不仅可以产生tuple,数组也是一样:

    let array1 :[f32; 7] = rng.gen();
    println!("{:?}", array1)

上面代码产生7个f32类型的数组,数组中的数字是随机f32:

[0.07171655, 0.5189788, 0.68672246, 0.7315745, 0.1870473, 0.36728072, 0.66637975]

产生某范围的数字

上面章节是产生某类型的随机数字,有些时候需要产生某个范围内的某类型的随机数字。Rng提供了gen_range函数,负责此事。

使用gen_range

Rng的gen_range函数,可以获取某个范围内的随机数字,该函数接受两个参数,(low,high),其中low包含,high不包含, including low , excluding high。

extern crate rand ;
use rand::{Rng};

fn main() {
    let mut rng = rand::thread_rng();
    let num = rng.gen_range(0,10);
    let f_num = rng.gen_range(0.0,10.0);

    println!("num {} f_num {}", num, f_num);
}

输出结果如下:(多轮次结果)


num 7 f_num 2.52105525700977

num 9 f_num 6.242366880456077

num 9 f_num 7.333549347293831

num 7 f_num 5.24803166200549

如果需要反复地获取某个范围内的随机数字,处于效率的考量,我们最好采用下面方法:

使用Uniform

struct Uniform可以获取均匀分布的数值,效率要比反复调用gen_range高一些:

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

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

    loop {
        let throw = uniform.sample(&mut rng);
        println!("Roll the die : {}", throw);

        if throw == 6
        {
            break;
        }
    }
}

输出结果如下:

manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
Roll the die : 1
Roll the die : 3
Roll the die : 6
manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
Roll the die : 4
Roll the die : 5
Roll the die : 1
Roll the die : 3
Roll the die : 6

Uniform表示均匀分布,我们使用了from函数,除此外,Uniform还提供了new和new_inclusive两个方法:

pub fn new<B1, B2>(low: B1, high: B2) -> Uniform<X>
pub fn new_inclusive<B1, B2>(low: B1, high: B2) -> Uniform<X>

接受两个参数,low和high,区别在于是否包含high。

  • new :[low, high)
  • new_inclusive : [low, high]

我们可以用这两个方法,改造下我们的代码:

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

fn main() {
    let mut rng = rand::thread_rng();
    let uniform = Uniform::new_inclusive(1,6);

    loop {
        let throw = uniform.sample(&mut rng);
        println!("Roll the die : {}", throw);

        if throw == 6
        {
            break;
        }
    }
}

输入结果如下:

manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
Roll the die : 4
Roll the die : 2
Roll the die : 3
Roll the die : 5
Roll the die : 2
Roll the die : 4
Roll the die : 3
Roll the die : 3
Roll the die : 2
Roll the die : 5
Roll the die : 5
Roll the die : 2
Roll the die : 6

关于 distribution

既然介绍到了随机,就不得不讲概率分布。概率分布是指,采样空间内的每一个值出现的概率。这个世界上并不是只有均匀分布(Uniform)一种,比如常见的正态分布。

标准分布:distributions::Standard

这个分布比较重要,实际上,Rng::gen就使用这个分布,这是产生很多类型随机数的默认分布:

  • integers : 均匀分布
  • char: 在整个Unicode内均匀分布
  • bool: 产生true or false,概率均为0.5
  • float:均匀分布在[0,1)范围内

Alphanumeric是一种特殊的Standard,它是在[a-zA-Z0-9]这个字符集上均匀分布。

extern crate rand ;
use rand::{Rng, thread_rng};
use rand::distributions::{Alphanumeric};

fn main() {
    let rand_string :String = thread_rng()
        .sample_iter(&Alphanumeric)
        .take(12)
        .collect();
    println!("rand_string : {}", rand_string)
}

产生出长度是12的随机字符:

rand_string : IZFJGQoCJFWc
rand_string : WYTW38x7lJ64

上面代码片段中,给出了sample_iter 。这是一个迭代器,如果我们想产生指定长度或者指定个数的随机数,我们可以使用这种方式:

均匀分布:Uniform

这种分布前面介绍很多了。我们需要记住,Standard分布和Uniform在很多场景下是一样的,而Standard分布并不是正态分布,切莫混淆即可。

伯努利分布 :Bernoulli

我们通过扔硬币,正反面出现的概率是相等的,即true or false 都是一半的概率。某些情况下,返回true和false的概率并不相等,比如我们希望1/3的概率返回True,2/3的概率返回true,rand提供了gen_bool函数:

extern crate rand ;
use rand::{Rng, thread_rng};

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

    for _ in 0..10{
        println!("{}", rng.gen_bool(1.0/4.0));
    }
}

输出结果如下:

manu-Inspiron-5748 CookBook/random ‹master*› » cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/random`
true
false
false
false
false
false
true
false
false
true

gen_bool函数,接受一个f64类型的参数:

fn gen_bool(&mut self, p: f64) -> bool

p的含义是,返回true的概率。该值的取值范围为[0,1]。

  • 当p = 0,表示永远返回false,
  • 当p = 1, 表示永远返回true
  • (0,1), p表示返回true的概率

上一篇     下一篇