Will this give me proper random numbers based on t

2019-04-08 11:11发布

Code:

int random = (rand() % 7 + 1)
if (random == 1) { } // num 1
else if (random == 2) { } // num 2
else if (random == 3 || random == 4) { } // num 3
else if (random == 5 || random == 6) { } // num 4
else if (random == 7) { } // num 5

Basically I want each of these numbers with each of these probabilities: 1: 1/7 2: 1/7 3: 2/7 4: 2/7 5: 1/7

Will this code give me proper results? I.e. if this is run infinite times, will I get the proper frequencies? Is there a less-lengthy way of doing this?

5条回答
Explosion°爆炸
2楼-- · 2019-04-08 11:36

rand returns pseudo-random integral number:

Notice though that this modulo operation does not generate a truly uniformly distributed random number in the span (since in most cases lower numbers are slightly more likely), but it is generally a good approximation for short spans.


Now, regarding the less-lengthy way, you can use switch-case construction, or a series of conditional operators ?: (which will make your code short and unreadable:).

查看更多
Viruses.
3楼-- · 2019-04-08 11:40

Not, it's actually slightly off, due to the way rand() works. In particular, rand returns values in the range [0,RAND_MAX]. Hypothetically, assume RAND_MAX were ten. Then rand() would give 0…10, and they'd be mapped (by modulus) to:

0  → 0
1  → 1
2  → 2
3  → 3
4  → 4
5  → 5
6  → 6
7  → 0
8  → 1
9  → 2
10 → 3

Note how 0–3 are more common than 4–6; this is bias in your random number generation. (You're adding 1 as well, but that just shifts it over).

RAND_MAX of course isn't 10, but it's probably not a multiple of 7 (minus 1), either. Most likely its a power of two. So you'll have some bias.

I suggest using the Boost Random Number Library which can give you a random number generator that yields 1–7 without bias. Look also at bames53's answer using C++11, which is the right way to do this if your code only needs to target C++11 platforms.

查看更多
Anthone
4楼-- · 2019-04-08 11:42

Assuming rand() is good then your code will work with only a very small bias to the lower X numbers, where X is RAND_MAX % 7. It's much more likely that you won't get the desired odds due to the quality of the implementation of rand(). If you find that to be the case then you'll want to use an alternative random number generator.

C++11 introduces the header <random> which includes several quality RNGs. Here's an example:

#include <random>
#include <functional>

auto rand = std::bind(std::uniform_int_distribution<int>(1,7),std::mt19937());

Given this, when you call rand() you will get a number from 1 to 7 each with equal probability. (And you can choose different engines if for different quality and speed characteristics.) You can then use this to implement the if-else conditions your example currently uses with std::rand(). However <random> allows you to do even better using one of their non-uniform distributions. In this case what you want is discrete_distribution. This distribution allows you to explicitly state the weights for each value from 0 to n.

// the random number generator
auto _rand = std::bind(std::discrete_distribution<int>{1./7.,1./7.,2./7.,2./7.,1./7.},std::mt19937());
// convert results of RNG from the range [0-4] to [1-5]
auto rand = [&_rand]() { return _rand() +1; };
查看更多
欢心
5楼-- · 2019-04-08 11:50
int toohigh = RAND_MAX - RAND_MAX%7;
int random;
do { 
    random = rand();
while (random >= toohigh); //should happen ~0.03% of the time
static const int results[7] = {1, 2, 3, 3, 4, 4, 5};
random = results[random%7];

This should give numbers with a distribution as even as rand can handle, and without the big if switch.

Note this does have a theoretically possible infinite loop, but the statistical odds of it staying in the loop for even are minuscule. The odds of it staying in the loop twice is quite close to the odds of winning the California Super Lotto Jackpot. Even if every person on the planet got five random numbers, it probably wouldn't stay in the loop three times. (Assuming a perfect RNG.)

查看更多
Rolldiameter
6楼-- · 2019-04-08 11:55

Just another way:

float probs[5] = {1/7.0f, 1/7.0f, 2/7.0f, 2/7.0f, 1/7.0f};
float sum = 0;
for (int i = 0; i < 5; i++)
  sum += probs[i]; /* edit */
int rand_M() {
  float f = (rand()*sum)/RAND_MAX; /* edit */
  for (int i = 0; i < 5; i++) {
    if (f <= probs[i]) return i;
    f -= probs[i];
  }
  return 4;
}
查看更多
登录 后发表回答