Reputation: 7157
How you I generate a random dice roll in Rust?
I know I can use rand::random
, but that requires I want to generate a value of an integer type. Using rand::random<u8>() % 6
introduces a bias.
Upvotes: 5
Views: 5869
Reputation: 430673
Use Rng::gen_range
for a one-off value:
use rand::{self, Rng}; // 0.8.0
fn main() {
let mut rng = rand::thread_rng();
let die = rng.gen_range(1..=6);
println!("The die was: {}", die);
}
Under the hood, this creates a Uniform
struct. Create this struct yourself if you will be getting multiple random numbers:
use rand::{
self,
distributions::{Distribution, Uniform},
}; // 0.8.0
fn main() {
let mut rng = rand::thread_rng();
let die_range = Uniform::new_inclusive(1, 6);
let die = die_range.sample(&mut rng);
println!("{}", die);
}
Uniform
does some precomputation to figure out how to map the complete range of random values to your desired range without introducing bias. It translates and resizes your original range to most closely match the range of the random number generator, discards any random numbers that fall outside this new range, then resizes and translates back to the original range.
See also:
Upvotes: 9
Reputation: 5689
You're correct that a bias is introduced; whenever you want to map from set A to set B where the cardinality of set B is not a factor or multiple of set A, you will have bias.
In your case, 42*6=252
. So you can just throw away any u8
values of 252 or greater (and call random again).
Your output can then be safely mapped with the modulus operator. Finally add 1 to achieve the standard [1,6] dice output.
It might seem unclean to call random again but there is no way of mapping a set of 256 values to a set of 6 without introducing bias.
Edit: looks like the rand crate has something which takes bias into account: https://docs.rs/rand/latest/rand/distributions/uniform/struct.Uniform.html
Upvotes: 3