在撰寫抽籤或抽獎程式的時候,我們可能會希望能夠依照籤或獎品的稀有程度來決定其被抽出來的機率。這樣的功能這要如何用Rust程式語言來實現呢?



假設有個抽獎活動共有5種獎項,愈前面的獎項價值愈高,所以抽到的機會也愈少。若第1種獎項抽到的機率為2%,第2種獎項抽到的機率為5%,第3種獎項抽到的機率為10%,第4種獎項抽到的機率為33%,第5種獎項抽到的機率為50%。若要從prizes這個字串切片的陣列中抽出獎項:

let prizes = ["頭獎", "二獎", "三獎", "四獎", "五獎"];

利用這篇文章介紹的隨機整數,程式實作方式如下:

let prize;

let i = random_integer::random_i32(0, 99);
if i >= 0 && i <= 1 {
    prize = prizes[0];
} else if i >= 2 && i <= 6 {
    prize = prizes[1];
} else if i >= 7 && i <= 16 {
    prize = prizes[2];
} else if i >= 17 && i <= 49 {
    prize = prizes[3];
} else {
    prize = prizes[4];
}

println!("{}", prize);

以上程式雖然可以達成需求,但程式碼並不是很不直覺。

Random Pick

「Random Pick」是筆者開發的套件,可以從一個或是多個陣列切片中,依權重隨機取得一個或是多個元素。

Crates.io

https://crates.io/crates/random-pick

Cargo.toml

random-pick = "*"

使用方法

「random-pick」這個crate,提供了「pick_from_slice」函數,可以從一個陣列中,依權重隨機取得一個元素。如本文開頭提到的例子,可以使用「Random Pick」套件改寫如下:

extern crate random_pick;

fn main() {
    let prizes = ["頭獎", "二獎", "三獎", "四獎", "五獎"];

    let weights = [2, 5, 10, 33, 50];

    let prize = random_pick::pick_from_slice(&prizes, &weights).unwrap();

    println!("{}", prize);
}

如果要抽出多個元素,可以使用「pick_from_multiple_slices」函數。程式如下:

extern crate random_pick;

fn main() {
    let prizes = ["頭獎", "二獎", "三獎", "四獎", "五獎"];

    let weights = [2, 5, 10, 33, 50];

    for prize in random_pick::pick_multiple_from_slice(&prizes, &weights, 5){
        println!("{}", prize);
    }
}

如果要從多個陣列切片中抽出一個元素,可以使用「pick_from_multiple_slices」函數。程式如下:

extern crate random_pick;

fn main() {
    let prizes = ["頭獎", "二獎", "三獎"];

    let extra_prizes = ["加開四獎", "加開五獎"];

    let weights = [2, 5, 10, 33, 50];

    let prize = random_pick::pick_from_multiple_slices(&[&prizes, &extra_prizes], &weights).unwrap();

    println!("{}", prize);
}

如果要從多個陣列切片中抽出多個元素,可以使用「pick_multiple_from_multiple_slices」函數。程式如下:

extern crate random_pick;

fn main() {
    let prizes = ["頭獎", "二獎", "三獎"];

    let extra_prizes = ["加開四獎", "加開五獎"];

    let weights = [2, 5, 10, 33, 50];

    for prize in random_pick::pick_multiple_from_multiple_slices(&[&prizes, &extra_prizes], &weights, 5) {
        println!("{}", prize);
    }
}

權重的陣列切片長度並不一定要等於獎項陣列切片的元素數量,但它通常會是元素數量的因數。舉例來說:

extern crate random_pick;

fn main() {
    let prizes = ["頭獎", "二獎", "三獎", "四獎"];

    let weights = [20, 80];

    let prize = random_pick::pick_from_slice(&prizes, &weights).unwrap();

    println!("{}", prize);
}

以上程式,抽出「頭獎」的機率為10%,「二獎」的機率為10%,「三獎」的機率為40%,「四獎」的機率為40%。