1
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

void randnum()
{
    int random;
    srand((unsigned int)time(0));
    for(int i=1;i<=5;i++)
    {
        random=(rand()%39)+1;
        cout<<random<<endl;
    }
}

int main()
{
    cout<<"Five random number is here"<<endl;
    randnum();
    system ("PAUSE");
    return 0;
}

I am randomly doing this to practice C++. I always get confused in setting the range of random generator (is my method correct? from 1-39). Also how can I prevent numbers from overlapping one another? That is, if I am outputting 5 different numbers from 1-39, it can always 5 different numbers like 4,5,2,7,12 instead of 4,5,2,4,12 (4 is used twice here)

jww
  • 97,681
  • 90
  • 411
  • 885
user3543568
  • 69
  • 1
  • 1
  • 13
  • 2
    If you are using `C++` use the Standard Library. See http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution – imreal Aug 29 '14 at 02:03
  • 2
    If you want to select 5 numbers from 1 to 39 it's probably best to add all of those numbers to a vector, shuffle it, and take the first 5. – Retired Ninja Aug 29 '14 at 02:03
  • 1
    And while you do, keep `std::iota` and `std::shuffle` in mind. – chris Aug 29 '14 at 02:03
  • @Ninja - I think this is a beginner C/C++ question that's related to counting elements in a range `[a,b]` and their permutations. I don't think the cited dup is equivalent in this case because its talking about things like asymptotic analysis and running times of an algorithm that does the same thing. (If the poster was more advanced, then it would be a different story). I'm voting to keep it open for him so he can get some simple answers about counting over the range `[a,b]`, permutations, and the absence of duplicates. (And there may be a more appropriate dup somewhere). – jww Aug 29 '14 at 03:28
  • [This answer](http://stackoverflow.com/questions/14720134/is-it-possible-to-random-shuffle-an-array-of-int-elements) shows how you can shuffle an array of numbers - then you can take the first 5 or whatever. Alternatively, it is faster to just compare each selection to the already-selected values if you're selecting a small fraction of the potential values. – Tony Delroy Aug 29 '14 at 03:37

4 Answers4

3

Yes, the method of getting a random number between 1 and 39 is correct.

To ensure non-overlapping numbers, two algorithms come to mind:

  1. keep a set of already-served numbers and skip when they are picked a second time, or
  2. create a list of all candidate numbers and randomly reorder them, then serve them in order
ash
  • 4,867
  • 1
  • 23
  • 33
1

The method is correct. To prevent numbers from overlapping one another, for me the best solution would be to create a vector of already generated numbers, and every time you generate a new random number, see if it is already in the vector. If it is, regenerate. If it isn't, then add it to the vector and move on.

toblerone_country
  • 577
  • 14
  • 26
  • 4
    This will get progressively slower as you generate more numbers, since it will have to retry more as the number of "bad" results goes up. It's probably okay for the specific case, but doesn't generalise well to larger numbers. – Craig McQueen Aug 29 '14 at 02:16
  • @CraigMcQueen: if that's an issue, the already-used values can be tracked in a `bitset`, `set` or `unordered_set`, but the general approach remains valid. +1 – Tony Delroy Aug 29 '14 at 03:39
  • @TonyD That would speed up checking if the generated number is already used. But you would still have the problem of having to retry the random generation more and more, as the number of "already-used" values goes up. – Craig McQueen Sep 01 '14 at 00:04
  • @CraigMcQueen: with the bitset of hash set approaches, the important thing is the ratio of already-used to unused numbers, and what is there in the question implying that both a) big-O efficiency is of any relevance, and b) the ratio of wanted to total numbers will ever increase beyond 5:39? Sometimes the best solution for a limited scenario isn't the best solution for the general case. – Tony Delroy Sep 01 '14 at 03:09
1

random=(rand()%39)+1;

This could lead to a duplicate.


I always get confused in setting the range of random generator (is my method correct from 1-39?)

The number of elements in a range [begin,end] (where the bracket means "inclusive") is:

 count = end - begin + 1

If you need one of 0-based count elements, then you perform:

rand() % count

Because the starting element may not be 0, you actually perform the following to get a value in the range:

rand() % count + begin

Also how can I prevent numbers from overlapping one another?

In this case, one of the easier solutions would be to use a vector. Its not as efficient as other answers (like the one @Retired Ninja suggested), but its easier to understand. Something like shown below.

The code below just dumps the result of the shuffle (which is not random because it repeats across runs based on the seed used). It should not be hard for you to adapt it to the first 5 elements (we can't give you all the answers).

ShuffledRange range (1, 39);
...

$ ./tt.exe 
29 33 8 37 9 32 38 24 16 14 36 7 10 31 34 39 27 11 6 4 35 1 19 20 18 15 5 12 22
21 3 30 17 25 2 28 23 26 13 

If you specify a seed (the default is 0), then you will get a different sequence:

ShuffledRange range (1, 39, 2);
...

$ ./tt.exe 
12 20 28 6 7 15 32 17 35 11 18 31 27 4 23 36 25 24 22 1 33 2 37 39 21 9 38 13 5 3
14 10 8 34 16 19 29 26 30

The code below needs C++ 11 because of random_shuffle. Visual Studio 2012 should be fine with C++ 11. I'm not sure about Visual Studio 2010.

GCC will need:

$ g++ -Wall -Wextra -std=c++11 tt.cpp -o tt.exe

And Mac OS X:

$ g++ -Wall -Wextra -std=c++11 -stdlib=libc++ tt.cpp -o tt.exe

class ShuffledRange
{
public:

    explicit ShuffledRange(unsigned int low, unsigned int high, int seed=0)
      : m_numbers(move(create_numbers(low,high,seed))), m_it(m_numbers.begin()) { }

    unsigned int GetCount() const {
        return static_cast<unsigned int>(m_numbers.size());
    }

    bool HasNext() const {
        return m_it != m_numbers.end();
    }

    unsigned int GetNext()
    {
        if(!HasNext())
            throw std::runtime_error("No numbers left");

        unsigned int temp = *m_it++;
        return temp;
    }

protected:                

    vector<unsigned int> create_numbers(unsigned int low, unsigned int high, int seed)
    {
        if(high < low)
            throw std::runtime_error("Bad range of elements");

        vector<unsigned int> temp;
        temp.reserve(high - low + 1);

        for(unsigned int i = low; i <= high; i++)
            temp.push_back(i);

        srand(seed);
        random_shuffle(temp.begin(), temp.end());

        return temp;
    }

private:

    vector<unsigned int> m_numbers;
    vector<unsigned int>::iterator m_it;
};

int main(int argc, char* argv[])
{
    ShuffledRange range(1, 39);

    while(range.HasNext())
        cout << range.GetNext() << " ";

    cout << endl;

    return 0;
}

A hint....

int main()
{
    cout<<"Five random number is here"<<endl;
    randnum();
    system ("PAUSE");
    return 0;
}

If you place a breakpoint (F9) on main's closing brace (i.e., the }), then you won't need the system ("PAUSE");. Visual Studio will break and wait for you. Once you've inspected the values, then press F5 to finish the program.

jww
  • 97,681
  • 90
  • 411
  • 885
1

Try create a vector containing the numbers 1 -39, shuffle them, and pick the first 5. Then you have 5 non-repeating random numbers.

random_shuffle() function is implemented in the C++ library. Check here: http://www.cplusplus.com/reference/algorithm/random_shuffle/

Alfred Huang
  • 433
  • 6
  • 13