在C++编程中,生成随机数是一项常见的任务,特别是在模拟、游戏开发或测试等领域。本文将深入探讨如何在C++中生成0到N-1之间的等概率随机数,并提供两种不同的实现方法。
让我们来看一种常见的做法,即使用`srand`和`rand`函数。`srand`用于设置随机数种子,通常使用当前时间来确保每次程序运行时产生不同的序列。`rand()`函数则返回一个在0到RAND_MAX之间(不包括RAND_MAX)的随机整数。然而,当需要在特定范围内生成随机数时,直接使用`rand() % N`并不总是理想的解决方案,因为它可能导致某些数字出现的概率高于其他数字。这主要是由于RAND_MAX可能不是N的整数倍,从而造成分布不均。
为了解决这个问题,我们可以采用以下策略:
1. **去除尾数**:
如果N小于RAND_MAX+1,我们可以计算出RAND_MAX对N取模的结果R,然后生成新的随机数t,直到它小于R。我们用t对N取模,得到符合要求的随机数result。这种方法确保了所有0到N-1的数字出现概率相等。
```cpp
long myrandom1(long n) {
long R = RAND_MAX - (RAND_MAX + 1) % n;
long t = rand();
while (t > R) {
t = rand();
}
return t % n;
}
```
2. **分段抽样**:
当N大于RAND_MAX时,我们可以将区间[0, N-1]分成[N/(RAND_MAX+1)]段,先随机选择一个段,然后再在该段内生成随机数。这个过程中,我们需要处理余数段的问题,即如果N不能被RAND_MAX+1整除,我们先以概率r/N选到余数段,然后在余数段内生成随机数,否则按照分段进行抽取。
```cpp
long myrandom2(long n) {
long r = n % (RAND_MAX + 1);
if (happened((double)r / n)) {
return n - r + myrandom(r);
} else {
return rand() + myrandom(n / (RAND_MAX + 1)) * (RAND_MAX + 1);
}
}
```
其中,`happened`函数用于模拟概率事件,`myrandom1`或`myrandom2`可以在需要时作为子函数调用。
此外,C++11引入了 `<random>` 头文件,提供了更为强大且灵活的随机数生成器。我们可以使用`std::mt19937`(Mersenne Twister算法)或其他随机数引擎,以及`std::uniform_int_distribution`分布来生成指定范围内的随机数。这种方法更高效且易于控制随机数的生成过程。
```cpp
#include <random>
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, N - 1);
long myrandom3(long N) {
return dis(gen);
}
```
另一种使用标准库的方法是`std::random_shuffle`,它可以打乱一个容器的顺序。在本例中,我们创建一个包含0到N-1的向量,然后使用`random_shuffle`对其进行随机排序。取向量的第一个元素作为随机数。
```cpp
#include <algorithm>
#include <vector>
long myrandom4(long N) {
std::vector<long> vl(N);
for (long i = 0; i < N; ++i) {
vl[i] = i;
}
std::random_shuffle(vl.begin(), vl.end());
return vl.front();
}
```
总结来说,生成C++中的等概率随机数可以通过多种方式实现,包括经典的`rand()`方法、去除尾数和分段抽样,以及C++11引入的`<random>`库。每种方法都有其适用场景,开发者可以根据项目需求和性能考虑选择合适的方法。在实际应用中,应优先考虑使用C++11及更高版本提供的随机数生成工具,以获得更好的随机性和效率。