How to generate a random number in C++?

后端 未结 11 1527
萌比男神i
萌比男神i 2020-11-22 11:14

I\'m trying to make a game with dice, and I need to have random numbers in it (to simulate the sides of the die. I know how to make it between 1 and 6). Using



        
相关标签:
11条回答
  • 2020-11-22 11:56

    Generate a different random number each time, not the same one six times in a row.

    Use case scenario

    I likened Predictability's problem to a bag of six bits of paper, each with a value from 0 to 5 written on it. A piece of paper is drawn from the bag each time a new value is required. If the bag is empty, then the numbers are put back into the bag.

    ...from this, I can create an algorithm of sorts.

    Algorithm

    A bag is usually a Collection. I chose a bool[] (otherwise known as a boolean array, bit plane or bit map) to take the role of the bag.

    The reason I chose a bool[] is because the index of each item is already the value of each piece of paper. If the papers required anything else written on them then I would have used a Dictionary<string, bool> in its place. The boolean value is used to keep track of whether the number has been drawn yet or not.

    A counter called RemainingNumberCount is initialised to 5 that counts down as a random number is chosen. This saves us from having to count how many pieces of paper are left each time we wish to draw a new number.

    To select the next random value I'm using a for..loop to scan through the bag of indexes, and a counter to count off when an index is false called NumberOfMoves.

    NumberOfMoves is used to choose the next available number. NumberOfMoves is first set to be a random value between 0 and 5, because there are 0..5 available steps we can make through the bag. On the next iteration NumberOfMoves is set to be a random value between 0 and 4, because there are now 0..4 steps we can make through the bag. As the numbers are used, the available numbers reduce so we instead use rand() % (RemainingNumberCount + 1) to calculate the next value for NumberOfMoves.

    When the NumberOfMoves counter reaches zero, the for..loop should as follows:

    1. Set the current Value to be the same as for..loop's index.
    2. Set all the numbers in the bag to false.
    3. Break from the for..loop.

    Code

    The code for the above solution is as follows:

    (put the following three blocks into the main .cpp file one after the other)

    #include "stdafx.h"
    #include <ctime> 
    #include <iostream>
    #include <string>
    
    class RandomBag {
    public:
        int Value = -1;
    
        RandomBag() {
            ResetBag();
    
        }
    
        void NextValue() {
            int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
    
            int NumberOfMoves = rand() % (RemainingNumberCount + 1);
    
            for (int i = 0; i < BagOfNumbersLength; i++)            
                if (BagOfNumbers[i] == 0) {
                    NumberOfMoves--;
    
                    if (NumberOfMoves == -1)
                    {
                        Value = i;
    
                        BagOfNumbers[i] = 1;
    
                        break;
    
                    }
    
                }
    
    
    
            if (RemainingNumberCount == 0) {
                RemainingNumberCount = 5;
    
                ResetBag();
    
            }
            else            
                RemainingNumberCount--; 
    
        }
    
        std::string ToString() {
            return std::to_string(Value);
    
        }
    
    private:
        bool BagOfNumbers[6]; 
    
        int RemainingNumberCount;
    
        int NumberOfMoves;
    
        void ResetBag() {
            RemainingNumberCount = 5;
    
            NumberOfMoves = rand() % 6;
    
            int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
    
            for (int i = 0; i < BagOfNumbersLength; i++)            
                BagOfNumbers[i] = 0;
    
        }
    
    };
    

    A Console class

    I create this Console class because it makes it easy to redirect output.

    Below in the code...

    Console::WriteLine("The next value is " + randomBag.ToString());
    

    ...can be replaced by...

    std::cout << "The next value is " + randomBag.ToString() << std::endl; 
    

    ...and then this Console class can be deleted if desired.

    class Console {
    public:
        static void WriteLine(std::string s) {
            std::cout << s << std::endl;
    
        }
    
    };
    

    Main method

    Example usage as follows:

    int main() {
        srand((unsigned)time(0)); // Initialise random seed based on current time
    
        RandomBag randomBag;
    
        Console::WriteLine("First set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nSecond set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nThird set of six...\n");
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        randomBag.NextValue();
    
        Console::WriteLine("The next value is " + randomBag.ToString());
    
        Console::WriteLine("\nProcess complete.\n");
    
        system("pause");
    
    }
    

    Example output

    When I ran the program, I got the following output:

    First set of six...
    
    The next value is 2
    The next value is 3
    The next value is 4
    The next value is 5
    The next value is 0
    The next value is 1
    
    Second set of six...
    
    The next value is 3
    The next value is 4
    The next value is 2
    The next value is 0
    The next value is 1
    The next value is 5
    
    Third set of six...
    
    The next value is 4
    The next value is 5
    The next value is 2
    The next value is 0
    The next value is 3
    The next value is 1
    
    Process complete.
    
    Press any key to continue . . .
    

    Closing statement

    This program was written using Visual Studio 2017, and I chose to make it a Visual C++ Windows Console Application project using .Net 4.6.1.

    I'm not doing anything particularly special here, so the code should work on earlier versions of Visual Studio too.

    0 讨论(0)
  • 2020-11-22 12:02

    Using modulo may introduce bias into the random numbers, depending on the random number generator. See this question for more info. Of course, it's perfectly possible to get repeating numbers in a random sequence.

    Try some C++11 features for better distribution:

    #include <random>
    #include <iostream>
    
    int main()
    {
        std::random_device dev;
        std::mt19937 rng(dev());
        std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]
    
        std::cout << dist6(rng) << std::endl;
    }
    

    See this question/answer for more info on C++11 random numbers. The above isn't the only way to do this, but is one way.

    0 讨论(0)
  • 2020-11-22 12:02

    for random every RUN file

    size_t randomGenerator(size_t min, size_t max) {
        std::mt19937 rng;
        rng.seed(std::random_device()());
        //rng.seed(std::chrono::high_resolution_clock::now().time_since_epoch().count());
        std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
    
        return dist(rng);
    }
    
    0 讨论(0)
  • 2020-11-22 12:03

    Here is a simple random generator with approx. equal probability of generating positive and negative values around 0:

      int getNextRandom(const size_t lim) 
      {
            int nextRand = rand() % lim;
            int nextSign = rand() % lim;
            if (nextSign < lim / 2)
                return -nextRand;
            return nextRand;
      }
    
    
       int main()
       {
            srand(time(NULL));
            int r = getNextRandom(100);
            cout << r << endl;
            return 0;
       }
    
    0 讨论(0)
  • 2020-11-22 12:04

    Can get full Randomer class code for generating random numbers from here!

    If you need random numbers in different parts of the project you can create a separate class Randomer to incapsulate all the random stuff inside it.

    Something like that:

    class Randomer {
        // random seed by default
        std::mt19937 gen_;
        std::uniform_int_distribution<size_t> dist_;
    
    public:
        /*  ... some convenient ctors ... */ 
    
        Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
            : gen_{seed}, dist_{min, max} {
        }
    
        // if you want predictable numbers
        void SetSeed(unsigned int seed) {
            gen_.seed(seed);
        }
    
        size_t operator()() {
            return dist_(gen_);
        }
    };
    

    Such a class would be handy later on:

    int main() {
        Randomer randomer{0, 10};
        std::cout << randomer() << "\n";
    }
    

    You can check this link as an example how i use such Randomer class to generate random strings. You can also use Randomer if you wish.

    0 讨论(0)
提交回复
热议问题