Random numbers

closed account (SECMoG1T)
Hey everyone, I would appreciate your insights on this function, I have a set of random numbers that I would like to generate at each call to this function, let's say {2,3,4,5,8}, Note that each call generates a single number from the set.

I would like this function to generate the first three numbers{2,3,4} 90% of all calls and the last two numbers{5,8} on the remainder 10% of the calls, how would I control this random generation without splitting this into two functions...
If we break it down on a per-element probability, are you saying that you want the following chances:
• 2 - 30%
• 3 - 30%
• 4 - 30%
• 5 - 5%
• 8 - 5%
?

<FOR FUTURE READERS: Please see JLBorges/lastchance's code below mine>

If so, maybe something like? Perhaps there is a better solution.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <random>
#include <ctime>  

int main()
{
    std::random_device rd;
    std::mt19937 gen(time(nullptr)  /*rd()*/);
    std::uniform_real_distribution<> random_norm(0.0, 1.0);
    
    double r_in = random_norm(gen);
    
    int r_out;
    if (r_in < 0.3)
    {
        r_out = 2;
    }
    else if (r_in < 0.6)
    {
        r_out = 3;
    }
    else if (r_in < 0.9)
    {
        r_out = 4;
    }
    else if (r_in < 0.95)
    {
        r_out = 5;
    }
    else
    {
        r_out = 8;
    }
}


And here's another way... maybe it's a bit silly, and not generalizable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <random>
#include <ctime>  
#include <map>

int main()
{
    std::random_device rd;
    std::mt19937 gen(time(nullptr)  /*rd()*/);
    std::uniform_real_distribution<> random_norm(0.0, 1.0);
    
                   //   1,  2,  3,   4,  5,  6, // 6/20 = 30%
                   //   7,  8,  9,  10, 11, 12, // 6/20 = 30%
                   //   13, 14, 15, 16, 17, 18, // 6/20 = 30%
                   //   19,                     // 1/20 =  5%
                   //   20                      // 1/20 =  5%
                   //  
    int outputs[20] = { 2, 2, 2, 2, 2, 2,
                        3, 3, 3, 3, 3, 3,
                        4, 4, 4, 4, 4, 4,
                        5, 8 };
                        

    std::map<int, int> output_count;
    
    const int iterations = 10000;
    
    for (int i = 0; i < iterations; i++)
    {
        double r_in = random_norm(gen); // 20 * [0.0, 1.0) == [0.0, 20.0)
        int r_out = outputs[static_cast<int>(20 * r_in)];

        output_count[r_out]++;
    }
    
    for (auto& pair : output_count)
    {
        std::cout << pair.first  //  (key)
                  << ':'
                  << (static_cast<double>(pair.second) / iterations) * 100.0 // (value as frequency percentage)
                  << '\n';
    }
}

2:29.61
3:30.11
4:30.13
5:4.94
8:5.21


Edit: using uniform_int_distribution alterantive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <random>
#include <ctime>  
#include <map>

int main()
{
    std::random_device rd;
    std::mt19937 gen(time(nullptr)  /*rd()*/);
    
    const int arr_size = 20;
    std::uniform_int_distribution<> random_index(0, arr_size - 1); // [0, arr_size-1]
    
                   //   1,  2,  3,   4,  5,  6, // 6/20 = 30%
                   //   7,  8,  9,  10, 11, 12, // 6/20 = 30%
                   //   13, 14, 15, 16, 17, 18, // 6/20 = 30%
                   //   19,                     // 1/20 =  5%
                   //   20                      // 1/20 =  5%
                   //  
    int outputs[arr_size] = { 2, 2, 2, 2, 2, 2,
                              3, 3, 3, 3, 3, 3,
                              4, 4, 4, 4, 4, 4,
                              5, 8 };
                        

    std::map<int, int> output_count;
    
    const int iterations = 10000;
    
    for (int i = 0; i < iterations; i++)
    {
        int r_out = outputs[random_index(gen)];
        output_count[r_out]++;
    }
    
    for (auto& pair : output_count)
    {
        std::cout << pair.first  //  (key)
                  << " : "
                  << (static_cast<double>(pair.second) / iterations) * 100.0 // (value as frequency percentage)
                  << '\n';
    }
}


The extra std::map logic is just to make a histogram, the actual random call is line 32.
Last edited on
closed account (SECMoG1T)
Thank you @Ganado, let me go through your code.
There is a discrete distribution in the library.
https://en.cppreference.com/w/cpp/numeric/random/discrete_distribution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <random>
#include <iterator>

int main()
{
    // https://en.cppreference.com/w/cpp/numeric/random
    std::mt19937 rng( std::random_device{}() ) ;

    const int numbers[] {  2, 3, 4, 5, 8 } ;
    const int weights[ std::size(numbers) ] { 30, 30, 30, 5, 5 } ;
    // https://en.cppreference.com/w/cpp/numeric/random/discrete_distribution
    std::discrete_distribution<int> distrib( std::begin(weights), std::end(weights) ) ;

    const int N = 10'000'000 ;
    int counts[ std::size(numbers) ] {} ;
    for( int i = 0 ; i < N ; ++i )
    {
        const int pos_selected = distrib(rng) ;
        ++counts[pos_selected] ;

        /* if( i < 400 )
        {
            std::cout << numbers[pos_selected] << ' ' ;
            if( i%25 == 24 ) std::cout << '\n' ;
        }*/
    }

    for( unsigned int i = 0 ; i < std::size(numbers) ; ++i )
        std::cout << numbers[i] << " was picked " << counts[i] << " times.\n" ;
}

http://coliru.stacked-crooked.com/a/d6f865f62c67f529
https://rextester.com/IJVK68799
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <iomanip>
#include <vector>
#include <chrono>
#include <random>
using namespace std;


mt19937 gen( chrono::system_clock::now().time_since_epoch().count() );


int main()
{
   // Define desired distribution
   vector<int> S = { 2, 3, 4, 5, 8 };
   discrete_distribution<int> dist{ 30.0, 30.0, 30.0, 5.0, 5.0 };
   int N = S.size();

   // Sampling ...
   const int NSample = 1000000;
   vector<int> counts( N, 0 );
   for ( int i = 1; i <= NSample; i++ ) counts[dist(gen)]++;

   cout << "Percentages:\n";
   for ( int i = 0; i < N; i++ ) cout << S[i] << ":  " << fixed << setprecision( 1 ) << 100.0 * counts[i] / NSample << " %\n";
}


Percentages:
2:  30.0 %
3:  30.0 %
4:  30.0 %
5:  5.0 %
8:  5.0 %
Last edited on
Perfect, I knew there had to be a better solution.
closed account (SECMoG1T)
@Ganado,@JLBorges,@Lastchance thank you very much, I have a solution now.

all help greatly appreciated.
Topic archived. No new replies allowed.