0

I am making a random number generator. It asks how many digits the user wants to be in the number. for example it they enter 2 it will generate random numbers between 10 and 99. I have made the generator but my issue is that the numbers are not unique.

Here is my code. I am not sure why it is not generating unique number. I thought srand(time(null)) would do it.

void TargetGen::randomNumberGen()
{
srand (time(NULL));
if (intLength == 1)
{

    for (int i = 0; i< intQuantity; i++)
    {
        int min = 1;
        int max = 9;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";

    }

}
else if (intLength == 2)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 10;
        int max = 90;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";


    }

}

if (intLength == 3)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 100;
        int max = 900;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}
else if (intLength == 4)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 1000;
        int max = 9000;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}

if (intLength == 5)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 10000;
        int max = 90000;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}
else if (intLength == 6)
{

    for (int i = 0; i<intQuantity; i++)
    {
        int min = 100000;
        int max = 900000;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";


    }

}

if (intLength == 7)
{
    for (int i = 0; i<intQuantity; i++)
    {
        int min = 1000000;
        int max = 9000000;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}
else if (intLength == 8)
{
    for (int i = 0; i <intQuantity; i++)
    {
        int min = 10000000;
        int max = 89999999;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}

if (intLength == 9)
{
    for (int i = 0; i < intQuantity; i++)
    {
        int min = 100000000;
        int max = 900000000;
        int number1 = rand();

        if (intQuantity > max)
        {
            intQuantity = max;
        }

        cout << number1 % max + min << "\t";
    }

}
}

Okay so I thought I figured out a way to do this without arrays but It isn't working before I switch to the fisher yates method. Can someone tell me why this isn't working? It is supposed to essentially take the random number put that into variable numGen. Then in variable b = to numgen. Just to hold what numGen used to be so when the loop goes through and generates another random number it will compare it to what the old number is and if it is not equal to it, then it will output it. If it is equal to the old number than rather than outputting it, it will deincrement i so that it will run through the loop without skipping over the number entirely. However, when I do this is infinitely loops. And I am not sure why.

if (intLength == 1) {

    for (int i = 0; i< intQuantity; ++i)
    {

        int min = 1;
        int max = 9;
        int number1 = rand();
        int numGen = number1 % max + min;


        if (intQuantity > max)
        {
            intQuantity = max;
        }

        for (int k = 0; k < 1; k++)
        {
            cout << numGen << "\t";
            int b = numGen;
        }
        int b = numGen;
        if (b != numGen )
        {
            cout << numGen << "\t";
        }
        else
        {
            i--;
        }

    }

}
user3530147
  • 1
  • 1
  • 3

6 Answers6

2

Everyone has interesting expectations for random numbers -- apparently, you expect random numbers to be unique! If you use any good random number generator, your random numbers will never be guaranteed to be unique.

To make this most obvious, if you wanted to generate random numbers in the range [1, 2], and you were to generate two numbers, you would (normally expect to) get one of the following four possibilities with equal probability:

1, 2
2, 1
1, 1
2, 2

It does not make sense to ask a good random number generator to generate the first two, but not the last two.

Now, take a second to think what to expect if you asked to generate three numbers in the same range... 1, 2, then what??

Uniqueness, therefore, is not, and will not be a property of a random number generator.

Your specific problem may require uniqueness, though. In this case, you need to do some additional work to ensure uniqueness.

One way is to keep a tab on which numbers are already picked. You can keep them in a set, and re-pick if you get one you got earlier. However, this is effective only if you pick a small set of numbers compared to your range; if you pick most of the range, the end of the process gets ineffective.

If the number count you are going to pick corresponds to most of the range, then using an array of the range, and the using a good shuffling algorithm to shuffle the numbers around is a better solution. (The Fisher-Yates shuffle should do the trick.)

safkan
  • 121
  • 3
2

Hint 0: Use Quadratic residue from number theory; an integer q is called a quadratic residue modulo p if it is congruent to a perfect square modulo p; i.e., if there exists an integer x such that:

x2 ≡ q (mod p)

Hint 1: Theorem: Assuming p is a prime number, the quadratic residue of x is unique as long as 2x < p. For example:

02 ≡ 0 (mod 13)

12 ≡ 1 (mod 13)

22 ≡ 4 (mod 13)

32 ≡ 9 (mod 13)

42 ≡ 3 (mod 13)

52 ≡ 12 (mod 13)

62 ≡ 10 (mod 13)

Hint 2: Theorem: Assuming p is a prime number such that p ≡ 3 (mod 4), not only x2%p (i.e the quadratic residue) is unique for 2x < p but p - x2%p is also unique for 2x>p. For example:

02%11 = 0

12%11 = 1

22%11 = 4

32%11 = 9

42%11 = 5

52%11 = 3

11 - 62%11 = 8

11 - 72%11 = 6

11 - 82%11 = 2

11 - 92%11 = 7

11 - 102%11 = 10

Thus, this method provides us with a perfect 1-to-1 permutation on the integers less than p, where p can be any prime such that p ≡ 3 (mod 4).

Hint 3:

unsigned int UniqueRandomMapping(unsigned int x)
{
    const unsigned int p = 11; //any prime number satisfying p ≡ 3 (mod 4)
    unsigned int r = ((unsigned long long) x * x) % p;
    if (x <= p / 2) return r;
    else return p - r;
}

I didn't worry about the bad input numbers (e.g. out of the range).

Remarks

  • For 32-bit integers, you may choose the largest prime number such that p ≡ 3 (mod 4) which is less than 232 which is 4294967291.
  • Even though, this method gives you a 1-to-1 mapping for generating random number, it suffers from the clustering issue.
  • To improve the randomness of the aforementioned method, combine it with other unique random mapping methods such as XOR operator.
1

I'll assume you can come up with a way to figure out how many numbers you want to use. It's pretty simple, since a user input of 2 goes to 10-99, 3 is 100-999, etc.

If you want to come up with your own implementation of unique, randomly generated numbers, check out these links.

http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

Here is a very similar implementation: https://stackoverflow.com/a/196065/2142219

In essence, you're creating an array of X integers, all set to the value of their index. You randomly select an index between 0 and MAX, taking the value at this index and swapping it with the max value. MAX is then decremented by 1 and you can repeat it by randomly selecting an index between 0 and MAX - 1.

This gives you a random array of 0-999 integers with no duplicates.

Community
  • 1
  • 1
Mdev
  • 2,440
  • 2
  • 17
  • 25
0

Here are two possible approaches to generating unique random numbers in a range.

  1. Keep track of which numbers you have already generated using std::set, and throw away and regenerate numbers as long as they are already in the set. This approach is not recommended if you want to generate a large number of random numbers, due to the birthday paradox.
  2. Generate all numbers in your given range, take a random permutation of them, and output however many the user wants.
merlin2011
  • 71,677
  • 44
  • 195
  • 329
0

Standard random generators would never generate unique numbers, in this case they would Not be independent.

To generate unique numbers you have to:

  • Save all number generated and compare new one with old ones, if there is coincidence - regenerate.

or

klm123
  • 12,105
  • 14
  • 57
  • 95
0

Firstly, srand()/rand() commonly have a period of 2^32, which means that after calling srand(), rand() will internally iterate over distinct integers during the first 2^32 calls to rand(). Still, rand() may well return a result with less than 32 bits: such as an int between 0 and RAND_MAX where RAND_MAX is 2^31-1 or 2^15-1, so you may see repeated results as the caller of rand(). You probably read about the period though, or somebody's comment made with awareness of that, and somehow it's been mistaken as uniqueness....

Secondly, given any call to rand() generates a number far larger than you want, and you're doing this...

number1 % max

The result of "number1 % max" is in the range 0 <= N <= max, but the random number itself may have been any multiple of max greater than that. In other words, two distinct random numbers that differ by a multiple of max still produce the same result for number1 % max in your program.


To get distinct random numbers within a range, you could prepopulate a std::vector with all the numbers, then std::shuffle them.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252