Search code examples
randomrustnormal-distribution

Generating random unsigned integers within a range biased toward the middle


I am trying to generate multiple random u32 integers in a very narrow range. My maximum and minimum both vary between 1 and 6 really. I would like to bias the generator towards the middle of the range.

I have tried using the Normal distribution from the rand_distr crate, but it seems to be for floats and unbounded beyond probability, i.e. I want values between 2 and 5 but I can potentially get a result like 0.81 or 6.92 even if they are rather rare. I wasn't able to find an integer version of the Normal distribution in the documentation. I am assuming it does not exist.

I would also like this to be efficient so I have a feeling the normal distribution for floats would not be very performant. I've also noticed a distribution called weighted indexes but this would require manual computation of weights with every iteration.

Perhaps the regular get_range for integer values can be biased towards the mean arithmetically somehow after the generator runs. Does anyone have any interesting solutions for this?


Solution

  • If you want to have a biased random distribution from a sample of values, you can use the rand crate's rand::distributions::weighted::WeightedIndex to have fine grain control over your biasness by defining weights of each item in the sample.

    use rand::prelude::*;
    use rand::distributions::WeightedIndex;
    
    fn main(){
    
        let mut rng = thread_rng();
        //item value and it's weight increasing till middle and then decreasing till end
        let sample_item = [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 3), ('f', 2), ('g', 1)];
    
    
        let weight_dist = WeightedIndex::new(sample_item.iter().map(|(_, weight)| weight)).unwrap();
    
        let mut pool = vec![];
    
        for _ in 1..100{
            let item = sample_item[weight_dist.sample(&mut rng)];
            pool.push(item.0);
        }
        println!("{:?}", pool.iter().filter(|x| **x == 'a').count());
        println!("{:?}", pool.iter().filter(|x| **x == 'b').count());
        println!("{:?}", pool.iter().filter(|x| **x == 'c').count());
        println!("{:?}", pool.iter().filter(|x| **x == 'd').count());
        println!("{:?}", pool.iter().filter(|x| **x == 'e').count());
        println!("{:?}", pool.iter().filter(|x| **x == 'f').count());
    }
    

    You can try out the code here