21

How do I generate a vector of 100 64-bit integer values in the range from 1 to 20, allowing duplicates?

3
  • 1
    Generate one and keep on going. /halfserious Commented Jan 12, 2018 at 2:04
  • Like ' let secret_number = rand::thread_rng().gen_range(1, 101);'? I could make a closure out of it and map it to a vector for 1..100? Commented Jan 12, 2018 at 2:22
  • Yeah, pretty much. Commented Jan 12, 2018 at 2:24

3 Answers 3

33

There are a few main pieces that you need here. First, how to create a vector of 100 calculated items? The easiest way is to create a range of 100 and map over those items. For instance you could do:

let vals: Vec<u64> = (0..100).map(|v| v + 1000).collect();
// [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, ...

Splitting this up:

  1. 0..100 creates an iterator for 0 through 99
  2. .map processes each item in the iterator when the iterator is processed.
  3. .collect() takes an iterator and converts it into any type that implements FromIterator which in your case is Vec.

Expanding on this for your random values, you can adjust the .map function to generate a random value from 0 to 20 using the rand crate's gen_range function to create a numeric value within a given range.

use rand::Rng; // 0.6.5

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

    let vals: Vec<u64> = (0..100).map(|_| rng.gen_range(0, 20)).collect();

    println!("{:?}", vals);
}

(On the Playground)

You should also consider using the rand::distributions::Uniform type to create the range up front, which is is more efficient than calling gen_range multiple times, then pull samples from it 100 times:

use rand::{distributions::Uniform, Rng}; // 0.6.5

fn main() {
    let mut rng = rand::thread_rng();
    let range = Uniform::new(0, 20);

    let vals: Vec<u64> = (0..100).map(|_| rng.sample(&range)).collect();

    println!("{:?}", vals);
}

(On the Playground)

Sign up to request clarification or add additional context in comments.

Comments

17

TL;DR:

use rand::{distributions::Uniform, Rng}; // 0.8.0

fn main() {
    let range = Uniform::from(0..20);
    let values: Vec<u64> = rand::thread_rng().sample_iter(&range).take(100).collect();
    println!("{:?}", values);
}

It's important to use rand::distributions::uniform::Uniform instead of simply performing the modulo of a uniform random number. See Why do people say there is modulo bias when using a random number generator? for more details.

Since we are generating multiple numbers from a range, it's more performant to create the Uniform once and reuse it. Creating the Uniform does some computation to avoid sampling bias.

We can use Rng::sample_iter to create an iterator of random values and then take some number of them, collecting into a Vec. collect will even make use of Iterator::size_hint to allocate exactly the right number of elements.


If you only needed a single random number in the range, you could use the shortcut Rng::gen_range:

use rand::Rng; // 0.8.0

fn main() {
    let mut rng = rand::thread_rng();
    let value: u64 = rng.gen_range(0..20);
}

If you needed a vector of random values without limiting to a range, you can use the Standard distribution:

use rand::{distributions::Standard, Rng}; // 0.8.0

fn main() {
    let values: Vec<u64> = rand::thread_rng().sample_iter(Standard).take(100).collect();
    println!("{:?}", values);
}

5 Comments

Could be rng.gen_iter::<u64>().take(100).map(|n| n % 20 + 1).collect(); with only std.
@Stargateur You might at least note that % 20 is not the same as gen_range(0, 20) - and I'd really advise against "cheating" on this level, even if for specific use cases it might not matter.
@Stefan What do you mean, I'm not cheating, this is how all ranges are implemented, doc.rust-lang.org/rand/src/rand/distributions/range.rs.html#127. This is very classic.
@Stargateur Just read the comment and the accept_zone condition above the line you just linked to see the difference.
The accept_zone calculation is the reason why you should allocate Range once and reuse it, instead of calling gen_range in a loop.
0

With just standard library

use std::error::Error;
use std::hash::{BuildHasher, Hasher, RandomState};

fn main() -> Result<(), Box<dyn Error>> {
    // Example: Get a random integer value in the range 1 to 20:
    let value: u64 = get_random_integer(1, 20)?;

    println!("integer: {:?}", value);

    // Generate a vector of 100 64-bit integer values in the range from 1 to 20,
    // allowing duplicates:

    let integers: Vec<u64> = (0..100)
        .map(|_| get_random_integer(1, 20))
        .collect::<Result<Vec<u64>, _>>()?;

    println!("integers: {:?}", integers);

    Ok(())
}

Generate a random integer value in the given range (min, max), inclusive.

Return error if min > max.

fn get_random_integer(min: u64, max: u64) -> Result<u64, Box<dyn Error>> {
    if min > max {
        Err(format!(
            "Error: min ({}) must be less than or equal to max ({})",
            min, max
        )
        .into())
    } else {
        // The remainder (`%`) after division is always less than the divisor.
        Ok(min + rand() % (max - min + 1))
    }
}

Generate random numbers without external dependencies

pub fn rand() -> u64 {
    RandomState::new().build_hasher().finish()
}

See the Rust Playground

Without Result<u64, Box<dyn Error>>

If it is guaranteed that always min <= max in the range (min, max), then the code can be simplified to:

use std::hash::{BuildHasher, Hasher, RandomState};

fn main() {
    // Generate a vector of 100 64-bit integer values in the range from 1 to 20,
    // allowing duplicates:

    let integers: Vec<u64> = (0..100)
        .map(|_| get_random_integer(1, 20))
        .collect();

    println!("integers: {:?}", integers);
}
/// Generate a random integer value in the given range (min, max) inclusive.
fn get_random_integer(min: u64, max: u64) -> u64 {
    min + rand() % (max - min + 1)
}

/// Generate random numbers without external dependencies
pub fn rand() -> u64 {
    RandomState::new().build_hasher().finish()
}

See the Rust Playground

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.