5

I'd like to make a number generator that does not repeat the number it has given out already (C++).

All I know is:

int randomgenerator(){
  int random;
  srand(time(0));
  random = rand()%11;
  return(random);
} // Added this on edition

That function gives me redundant numbers.

I'm trying to create a questionnaire program that gives out 10 questions in a random order and I don't want any of the questions to reappear.

Does anyone know the syntax?

Mahesh
  • 34,573
  • 20
  • 89
  • 115
Jpeh Noynay
  • 173
  • 3
  • 6
  • 12
  • 1
    If your random number generator can't repeat, it loses an important aspect of randomness. If someone knows the period, their accuracy of guesses increases as it reaches toward the end of the period, with 100% certainty on the last guess. – corsiKa Mar 21 '11 at 18:44
  • 7
    srand() should be run **ONCE** and only once at program start-up. – Martin York Mar 21 '11 at 18:48
  • You could simply store as an int the last value, and if the new value is equal to the last one, add a random number between 1 and the maximum value you want minus the number you got. You can do this simply with modulus and it is kind of more random-y than simply using rand() :) – Anonymous Pi Sep 04 '13 at 18:45
  • @corsiKa Well, the pseudo-random generator in C++ and C sucks. – Anonymous Pi Sep 04 '13 at 18:47
  • 2
    @AnonymousPi No matter how bad you think the RNG in "C and C++" is, trying to make your own is guaranteed to suck more. -gguuaarraanntteeeedd – corsiKa Sep 04 '13 at 19:53
  • language agnostic: http://stackoverflow.com/questions/196017/unique-non-repeating-random-numbers-in-o1 – Ciro Santilli OurBigBook.com Feb 27 '17 at 11:20

7 Answers7

20

What I would do:

  • Generate a vector of length N and fill it with values 1,2,...N.
  • Use std::random_shuffle.
  • If you have say 30 elements and only want 10, use the first 10 out the vector.

EDIT: I have no idea how the questions are being stored, so.. :)

I am assuming the questions are being stored in a vector or somesuch with random access. Now I have generated 10 random numbers which don't repeat: 7, 4, 12, 17, 1, 13, 9, 2, 3, 10.

I would use those as indices for the vector of questions:

std::vector<std::string> questions;
//fill with questions
for(int i = 0; i < number_of_questions; i++)
{
    send_question_and_get_answer(questions[i]);
}
PrettyPrincessKitty FS
  • 6,117
  • 5
  • 36
  • 51
  • I like this one but I still have no idea how it will help me pick up the questions. – Jpeh Noynay Mar 21 '11 at 19:07
  • @Jpeh from your example, it looks like you want to have a generator that returns a number which you will then use as an index to your list of questions. The answer proposed here allows you to take a list of questions, randomize its order, and then just iterate through it. – csj Mar 21 '11 at 19:21
  • In C++11, you can use [std::iota](http://en.cppreference.com/w/cpp/algorithm/iota) to populate the vector. – Fred Larson May 07 '14 at 19:52
7

You are trying to solve the problem "the wrong way".

Try this instead (supposing you have a vector<int> with question ids, but the same idea will work with whatever you have):

  1. Get a random R from 0 to N-1 where N is the number of questions in the container
  2. Add question R to another collection of "selected" questions
  3. If the "selected questions" collection has enough items, you 're done
  4. Remove question R from your original container (now N has decreased by 1)
  5. Go to 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • hi, could you help me create the actual function? I'm new to C++ and I have no idea what vector is. D: – Jpeh Noynay Mar 21 '11 at 18:42
  • @Jpeh: How are your questions stored then? Also, is this homework? – Jon Mar 21 '11 at 18:43
  • I am using the function I wrote in the question to pick a random number between 0-10, and I return the value to my main program. Whichever that value is equivalent to a certain question (e.g. if random == 1, then i will call the function: "question 1". no, it is not a homework. I'm just a programming enthu here. :D – Jpeh Noynay Mar 21 '11 at 18:48
  • @Jpeh: Max Lybbert's answer has some example code you can use. But you also need to read some book that introduces you to the STL. – Jon Mar 21 '11 at 18:51
7

Sounds like you essentially want to shuffle a deck of cards (in this case, the "cards" being the questions, or question numbers).

In C++, I would do:

#include <vector>
#include <algorithms>

std::vector<int> question_numbers;
for (unsigned int i = 0; i < 10; ++i)
    question_numbers.push_back(i+1);
std::random_shuffle(question_numbers.begin(), question_numbers.end());

// now dole out the questions based on the shuffled numbers

You do not have to hand out all of the questions, any more than you have to deal out a whole deck of cards every time you play a game. You can, of course, but there's no such requirement.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
4

Create a vector of 10 elements (numbers 1-10), then shuffle it, with std::random_shuffle. Then just iterate through it.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 1
    @Benjamin: I think what the OP wants is to randomly select N out of M. – Jon Mar 21 '11 at 18:41
  • 2
    @Jon: Yeah? So you only iterate through N elements. – Benjamin Lindley Mar 21 '11 at 18:43
  • @Jon: that's essentially the same, but this approach gives you O(N+M) time while your gives O(NM). – Yakov Galka Mar 21 '11 at 18:44
  • @Benjamin: Obviously. What I 'm trying to say is that the wording "create a vector of 10 elements **(numbers 1-10)**" is misleading if not incorrect because the vector should be `0...M-1`. I 'd give +1 because this is algorithmically superior if the answer was written to address the OP's question. – Jon Mar 21 '11 at 18:45
  • I'm not sure I understand you @Jon, especially since you gave The Communist Duck +1 and he recommended the exact same thing. – Benjamin Lindley Mar 21 '11 at 18:54
  • @Benjamin: I can't believe I have to continue beating this horse, but you will notice that Duck's answer has "N" in it, while yours does not. – Jon Mar 21 '11 at 19:00
  • @Jon: Okay, don't get upset. I'm just trying to clarify what the problem is, so I know where I went wrong. Don't feel like you have to respond, but I don't see how your most current complaint relates to your previous complaint. First, you said "the vector should be 0...M-1". Now you're saying the problem is that I didn't have an N. I'm not trying to coax an upvote out of you, I'm just trying to understand the complaint. – Benjamin Lindley Mar 21 '11 at 19:09
1

Why not use some STL to perform the checks for you? The idea:

Create an (initially empty) set of 10 integers that will be the indices of the random questions (they will be distinct as a set forbids duplicate items). Keep pushing random numbers in [0, num_of_questions-1] in there until it grows to a size of 10 (duplicates will get rejected automatically). When you have that set ready, iterate over it and output the questions of the corresponding indexes:

std::vector<std::string> questions = /* I am assuming questions are stored in here */
std::set<int> random_indexes;

/* loop here until you get 10 distinct integers */
while (random_indexes.size() < 10) random_indexes.insert(rand() % questions.size());

for (auto index: random_indexes){
    std::cout << questions[index] <<std::endl;
}

I may be missing something, but it seems to me the answers that use shuffling of either questions or indexes perform more computations or use an unnecessary memory overhead.

kyriakosSt
  • 1,754
  • 2
  • 15
  • 33
1

Should look more like this: (Note: does not solve your original problem).

int randomgenerator(){
  int random;

  // I know this looks re-dunand compared to %11
  // But the bottom bits of rand() are less random than the top
  // bits do you get a better distribution like this.

  random = rand() / (RAND_MAX / 11);

  return random;
}

int main()
{
    // srand() goes here.
    srand(time(0));

    while(true)
    {
        std::cout << randomgenerator() << "\n";
    }
}

A better way to solve the original problem is to pre-generate the numbers so you know that each number will appear only once. Then shuffle the order randomly.

int main()
{
    int data[] =  { 0,1,2,3,4,5,6,7,8,9,10,11};
    int size   =  sizeof(data)/sizeof(data[0]);

    std::random_shuffle(data, data + size);

    for(int loop = 0; loop < size; ++loop)
    {
        std::cout << data[loop] << "\n";
    }
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
0
//non repeating random number generator
for (int derepeater = 0; derepeater < arraySize; derepeater++)
{
    for (int j = 0; j < arraySize; j++)
    {
        for (int i = arraySize; i > 0; i--)
        {
            if (Compare[j] == Compare[i] && j != i)
            {
                Compare[j] = rand() % upperlimit + 1;
            }
        }
    }
}