0

I am trying to right a program, with arrays, which gets random numbers between 0 - 24 but they can only occur once. I know how to generate random numbers, I am just stuck on how to do the check to see if the number already exists in the array. I tried generating a new rand() % 25 and comparing it with placeholders in the array and if it didn't exist put the new random number in there but it did not work.

 void MultiU (){
         int size = 5;
         int array[5];
        srand(time(0));

        for (int index = 0; index < size; index++){
           exists[index] = rand() %25;
          }
    }

I am new to programming with arrays and rand(). I hope someone can guide me in the right direction.

Csci319
  • 125
  • 8

6 Answers6

9

std::unordered_set is your friend. It does not allow/insert duplicates, and you can exploit this fact to obtain 5 different numbers. When the set is of size 5, it is guaranteed to contain 5 different elements.

std::unordered_set<int> s;

while (s.size() < 5) {
    s.insert(rand() % 25);
}
  • I do not think this is a good idea for random numbers. That numbers will be there in particular order, no matter in which order they were generated. – Slava Oct 29 '14 at 16:43
  • @Slava It was not specified as a requirement that the order of generation be retained. – The Paramagnetic Croissant Oct 29 '14 at 16:45
  • @aardvarkk you should not. Though there won't be obvious pattern like if you use `std::set` order of elements is determined. This is not good solution for random numbers. – Slava Oct 29 '14 at 16:46
  • @TheParamagneticCroissant it does not have to be, that obvious requirement for random numbers. Problem is not to retain order of generation, but that order of the same elements will be always the same unless you use special hashing function. – Slava Oct 29 '14 at 16:46
  • @Slava How do you *know* that it is not? You might be right and you may well be wrong with an equal probability... – The Paramagnetic Croissant Oct 29 '14 at 16:47
  • @Slava So, if the numbers don't have to be in a particular order, then what's wrong with this solution? – The Paramagnetic Croissant Oct 29 '14 at 16:47
  • 3
    @TheParamagneticCroissant whats wrong? it degrades quality of random numbers drastically. If you generate "1,2,3,4,5" or "5,4,3,2,1" or "2,1,5,4,3" does not matter you will get the same output from `unordered_set`. This is bad. It is obvious if you use regular `set` as numbers will be in ascending orderd. But `unordered_set` is not much better as order is easily predictable. – Slava Oct 29 '14 at 16:52
  • We have not covered this in class so I don't think I would be able to use this, but thanks because its good to know this – Csci319 Oct 29 '14 at 16:57
  • @Slava "If you generate "1,2,3,4,5" or "5,4,3,2,1" or "2,1,5,4,3" does not matter you will get the same output from unordered_set" - yes. **Why** is that bad? In a lot of cases, this is the exact behavior that's desired. – The Paramagnetic Croissant Oct 29 '14 at 16:58
  • @BS319 Alternatively, you can use `std::set` (but it has slightly worse performance characteristics for **large** sets of numbers, shouldn't matter for a few hundred or thousand elements though.) This is also solvable by treating a `std::vector` as a set, in which case you replace the `insert` with a `push_back()` which is executed conditionally: if and only if the new number is not in the vector (use `find()` to check that). – The Paramagnetic Croissant Oct 29 '14 at 17:00
  • we haven't covered vectors yet in my course. I am really new to programming. We were given and example on how to go about this. I can post it if you want – Csci319 Oct 29 '14 at 17:02
  • @BS319 So what are you allowed to use? How do you want to do this without a container? More importantly, how **they** (your teachers) want you to do it? – The Paramagnetic Croissant Oct 29 '14 at 17:03
  • @TheParamagneticCroissant "Why is that bad?". Because it drastically decreases number of possible outputs. Can you mention some of "a lot" of cases when this is desired behavior? – Slava Oct 29 '14 at 17:04
  • @Slava e. g. lottery (or any "select some winners of the base set" type game). Also, not only games - but any problem when entities are to be treated as set elements and not array elements (i. e. the order does not matter). Random sampling of some physical quantity for a simulation? Granted, order does not matter. – The Paramagnetic Croissant Oct 29 '14 at 17:06
  • In libstdc++, `hash(i) == i` and `unordered_set` starts with 23 buckets. So predictably, you know what buckets everything goes in. IF unordered_set iterated by bucket, you could reliably guess the range of the next random number - which is an utter fail of randomness. HOWEVER, it appears that unordered_set simply iterates in reverse order by insert (at least on my implementation). So this is OK. For `std::set` however, this would *not* be usable. – Barry Oct 29 '14 at 17:09
  • @Barry I understand that this decreases randomness when the order of numbers is significant. But why is that a problem when the order is not significant? If I want to generate a **set** of random numbers, I expect that any time I generate the same numbers in any order, I get identical behavior. Is that an impossible, infeasible or mathematically incorrect expectation? – The Paramagnetic Croissant Oct 29 '14 at 17:11
  • @TheParamagneticCroissant The problem doesn't stipulate that the order is insignificant. It just asks for 5 unique random numbers. Maybe he will use those 5 to shuffle a 5 card deck, who knows. – Barry Oct 29 '14 at 17:12
  • @Barry One could go in the other direction as well - it doesn't state that order is significant either. That's not what I am asking you, though. – The Paramagnetic Croissant Oct 29 '14 at 17:13
  • 3
    @TheParamagneticCroissant solution that keeps order of generation will work in either case. Your solution has restriction and you even did not mention that. – Slava Oct 29 '14 at 17:16
  • @TheParamagneticCroissant You're asking if, conditioned on order not mattering, order matters? No, it doesn't. – Barry Oct 29 '14 at 17:25
  • @Barry Exactly, and that's what I'm saying too. – The Paramagnetic Croissant Oct 29 '14 at 17:26
5

Here's a general approach to getting lists of random numbers with no repeats:

#include <algorithm>
#include <iostream>
#include <random>
#include <vector>

int main() {
    std::vector<int> vals;
    for (size_t i = 0; i < 25; ++i) {
        vals.push_back(i);
    }
    std::shuffle(vals.begin(), vals.end(), std::mt19937());
    for (auto v : vals) {
        std::cout << v << std::endl;
    }
}

Here's a working demo.

In this case, it would be inefficient to do this and then only extract the first five numbers. However, as the list of possible random numbers gets larger (and you want to select more of them) this method will be more efficient than the one from The Paramagnetic Croissant.

I should also add that you should not use rand(). Or try to use rand() with a modulus to get fixed-range random numbers. They will not be properly distributed!

aardvarkk
  • 14,955
  • 7
  • 67
  • 96
  • ...or `std::random_shuffle` for pre-C++11. – Captain Obvlious Oct 29 '14 at 16:52
  • 2
    `default_random_engine` may default to a bad engine. Better to explicitly use `mt19937` which is less typing anyway. –  Oct 29 '14 at 16:53
  • @remyabel Yes, I was trying to find a reference saying `std::random_shuffle` was bad. Good point about the default engine -- I'll edit the answer. – aardvarkk Oct 29 '14 at 16:55
  • @Captain Oops I didn't see that you said `pre-C++11`... –  Oct 29 '14 at 16:56
  • 2
    @remyabel `std::random_shuffle` takes a rng function as one of it's parameters and is deprecated as of C++14. – Captain Obvlious Oct 29 '14 at 16:56
  • I am surprised how fast this method is. Result for [this](http://coliru.stacked-crooked.com/a/83dec6a87bc6cbb4) code on my machine: 53.273 73.9812 Still slower than the set method, but not by much. (Ignore the timer Wall of Code, just pasted that from one of my projects.) – Baum mit Augen Oct 29 '14 at 17:23
  • @BaummitAugen The running time depends very much on what proportion of your allowable values you actually care to look at. In your example, you were only using 1/10th of all generated values. As you increase that proportion to be closer to 1, my approach is much faster than the "set" method. See a demo (using your code exactly, just with a changed "how many" and "max" value) here: http://ideone.com/70QYn5 – aardvarkk Oct 29 '14 at 18:55
  • @aardvarkk I did expect the vector to win when the set's chances to hit a good number were close to zero. What surprised me was that it was that close even when the set hat at least a 90% chance to find a "good" number in every iteration. Seems like I underestimated the cache again. :) – Baum mit Augen Oct 29 '14 at 19:13
2

Below is the Pseudo code for what you are trying to do:- 1) First generate a random number

int number = rand() %25;

2) Check if "number" exists in array.

3) If not then insert else go to step 1;

Apart from that there are more lucrative options ( containers ) than plain array.

EDITED IN RESPONSE TO COMMENT#

You can define a function for this and call it in step 2 mentioned above:-

bool search ( int *p, int size, int element )
{
   for( int i = 0; i < size; i++ )
   {
     if ( *(p+i) == element )
     return false;
   }
   return true;
}

Hash-table would be very effective in this case if that's included in your class.

ravi
  • 10,994
  • 1
  • 18
  • 36
0

You may use Fisher–Yates_shuffle:

// Fisher–Yates_shuffle
std::vector<int> FisherYatesShuffle(std::size_t size, std::size_t max_size, std::mt19937& gen)
{
    assert(size < max_size);
    std::vector<int> res(size);

    for(std::size_t i = 0; i != max_size; ++i) {
        std::uniform_int_distribution<> dis(0, i);
        std::size_t j = dis(gen);
        if (j < res.size()) {
            if (i < res.size()) {
                res[i] = res[j];
            }
            res[j] = i;
        }
    }
    return res;
}

Live example

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Check this code:

int numbers[26], tempNum; //Array required to store the used values and tempNum for the random number you want to generate
bool x; //This will be required later (true or false)
srand(time(0));//I think you have this clear

for(int i = 0; i <= 24; i++){//Required in order to generate 24 random numbers
        do{
        tempNum = (rand()%25);//Generates the random number

        x = false;//This sets the value as not used before
        for(int j =0; j <= 24; j++){//Scans the array to find out whether the value is used before
                if(tempNum == numbers[j]){//Tests if the value is used before
                           x = true;// If so marks it as used
                           }
                }
                }while(x == true);//This makes it go back to generating a new number part

        numbers[i] = tempNum;//This stores the number in the array
        cout << tempNum << endl;//Prints the number on the screen
}
SilverS
  • 66
  • 3
  • thanks this worked. And thanks for explaining each part. It really helps me learn – Csci319 Oct 29 '14 at 17:31
  • thanks again for your help. I know how to make a 5x5 array, I was wondering what how you take this same problem and put in a 5x5 array, would you add another for-loop under the int j loop? – Csci319 Oct 31 '14 at 16:01
  • In that case you will need a for loop inside another for loop to be reading the values and also storing them. It would make think too complicated. – SilverS Nov 01 '14 at 08:30
0

The obvious solution is to create a vector with the acceptable values, do a random shuffle on it, and take the first n:

std::vector<int> values( 25 );
for ( int i = 0; i != 25; ++ i ) {
    values[i] = i;
}
std::random_shuffle( values.begin(), values.end() );
values.resize( size );

should do the trick.

James Kanze
  • 150,581
  • 18
  • 184
  • 329