Generate random numbers without duplicates (random sampling without replacement)

#1

Hey , does anyone knows how to generate number randomly without duplicates?

For example: i am trying to generate 5 numbers in between 0-30 and i want to make sure they all are different.
3 22 13 4 7 is correct
2 25 2 8 3 isn’t correct
i am trying to reset the function when its done

thanks!

#2

I’d use ofRandomize (which is essentially a shuffle) on a vector of ints:

vector < int > randomNums;
for (int i = 0; i < 100; i++){
    randomNums.push_back(i);
}
ofRandomize(randomNums);
for (int i = 0; i < 100; i++){
    cout << randomNums[i] << endl;
}
1 Like
#3

to reset, just keep track as you move through the array and when you get to the end, shuffle again

also, you can always grab the first element of the array and erase, ie:

 int whichVal;
if (randomNums.size() > 0){
    whichVal = randomNums[0];
    randomNums.erase(randomNums.begin());
} else {
    // make a new set of random numbers (ie, use same code as above) 
    whichVal =  randomNums[0];
    randomNums.erase(randomNums.begin());
}
2 Likes
#4

@zach 's way will work great. If you want a unique set of 5 from a sequence each time, you could also do something like this:

std::vector<int> getRandomInts(int rangeStart,
                               int rangeEnd,
                               std::size_t numToSample)
{
    // These asserts are a quick replacement for errors or exceptions.
    assert(rangeEnd > rangeStart);
    assert(std::abs(rangeEnd - rangeStart) >= numToSample);
    // Create an empty vector large enough to hold the range of numbers.
    std::vector<int> numbers(std::abs(rangeEnd - rangeStart));
    // Fill the vector with a sequence of numbers (this is a cool but a for-loop works too).
    std::iota(numbers.begin(), numbers.end(), rangeStart);
    // Shuffle the numbers.
    std::random_shuffle(numbers.begin(), numbers.end());
    // Throw away all but the number you want.
    numbers.resize(numToSample);
    // Return the ones you didn't throw away.
    return numbers;
}

To use it, you could do something like this.

// Select 5 numbers without replacement from [0, 30].

std::vector<int> ints = getRandomInts(0, 31, 5);

std::cout << ofToString(ints) << std::endl;

The main difference between this method and the one above is that each set of 5 numbers will not have repeats, but subsequent sets of 5 could have repeats. If you want to exhaustively select each of the numbers in the sequence from [0, 30] before repeats are allowed use @zach’s method or set numToSample = std::abs(rangeEnd - rangeStart);

1 Like
#5

@zach @bakercp thanks guys! both working prefect for what i needed!

1 Like