第一个随机数总是小于其余的
First random number is always smaller than rest
我偶然注意到,在 C++ 中,使用 std rand() 方法调用的第一个随机数在大多数情况下明显小于第二个随机数。关于 Qt 实现,第一个几乎总是小几个数量级。
qsrand(QTime::currentTime().msec());
qDebug() << "qt1: " << qrand();
qDebug() << "qt2: " << qrand();
srand((unsigned int) time(0));
std::cout << "std1: " << rand() << std::endl;
std::cout << "std2: " << rand() << std::endl;
输出:
qt1: 7109361
qt2: 1375429742
std1: 871649082
std2: 1820164987
这是故意的,因为播种错误或错误?
此外,虽然 qrand() 输出变化很大,但第一个 rand() 输出似乎随时间线性变化。只是想知道为什么。
当前的 Qt 和 C 标准 运行-time 都没有质量随机发生器,您的测试显示。 Qt 似乎为此使用 C 运行-time(这很容易检查,但为什么)。如果 C++ 11 在您的项目中可用,请使用更好、更可靠的方法:
#include <random>
#include <chrono>
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::uniform_int_distribution<uint> distribution;
uint randomUint = distribution(generator);
有很好的 video 涵盖了主题。正如评论者 user2357112 所指出的,我们可以应用不同的随机引擎,然后应用不同的分布,但对于我的特定用途,上述方法非常有效。
我不确定这是否可以 class 确定为错误,但它有一个解释。让我们来看看情况:
看rand's implementation。您会看到它只是使用最后生成的值进行的计算。
您正在使用 QTime::currentTime().msec() 进行播种,它本质上受限于较小的值范围 0..999,但是 qsrand 接受范围 0.. 上的 uint 变量4294967295.
结合这两个因素,您就有了一个模式。
出于好奇: 尝试使用 QTime::currentTime().msec() + 100000000
进行播种
现在大多数时候第一个值可能会比第二个大。
我不会太担心。 "pattern" 似乎只发生在前两个生成的值上。在那之后,一切似乎都恢复了正常。
编辑:
为了使事情更清楚,请尝试 运行 下面的代码。它将使用所有可能的毫秒值(范围:0..999)作为种子来比较前两个生成的值,看看哪个更小:
int totalCalls, leftIsSmaller = 0;
for (totalCalls = 0; totalCalls < 1000; totalCalls++)
{
qsrand(totalCalls);
if (qrand() < qrand())
leftIsSmaller++;
}
qDebug() << (100.0 * leftIsSmaller) / totalCalls;
它将打印 94.8,这意味着 94.8% 的时间第一个值将小于第二个。
结论: 当使用当前毫秒作为种子时,您会看到前两个值的模式。我在这里做了一些测试,在生成第二个值后,模式似乎消失了。我的建议:找到一个 "good" 值来调用 qsrand (显然应该只调用一次,在程序的开头)。一个好的值应该跨越 uintclass 的整个范围。看看这个其他问题以获得一些想法:
- Recommended way to initialize srand?
另外,看看这个:
考虑到根据少量样本对统计现象做出判断可能会产生误导,我决定 运行 进行一个小实验。我运行下面的代码:
int main()
{
int i = 0;
int j = 0;
while (i < RAND_MAX)
{
srand(time(NULL));
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
}
}
基本上打印了第一个生成的数字小于第二个的次数的百分比。下面是该比率的图表:
如您所见,在实际新种子少于 50 个后,它实际上接近 0.5。
正如评论中所建议的,我们可以修改代码以在每次迭代中使用连续的种子并加快收敛速度:
int main()
{
int i = 0;
int j = 0;
int t = time(NULL);
while (i < RAND_MAX)
{
srand(t);
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
++t;
}
}
这给了我们:
也非常接近 0.5。
虽然 rand
肯定不是最好的伪随机数生成器,它经常在第一个 运行 期间生成较小数字的说法似乎没有根据。
我偶然注意到,在 C++ 中,使用 std rand() 方法调用的第一个随机数在大多数情况下明显小于第二个随机数。关于 Qt 实现,第一个几乎总是小几个数量级。
qsrand(QTime::currentTime().msec());
qDebug() << "qt1: " << qrand();
qDebug() << "qt2: " << qrand();
srand((unsigned int) time(0));
std::cout << "std1: " << rand() << std::endl;
std::cout << "std2: " << rand() << std::endl;
输出:
qt1: 7109361
qt2: 1375429742
std1: 871649082
std2: 1820164987
这是故意的,因为播种错误或错误? 此外,虽然 qrand() 输出变化很大,但第一个 rand() 输出似乎随时间线性变化。只是想知道为什么。
当前的 Qt 和 C 标准 运行-time 都没有质量随机发生器,您的测试显示。 Qt 似乎为此使用 C 运行-time(这很容易检查,但为什么)。如果 C++ 11 在您的项目中可用,请使用更好、更可靠的方法:
#include <random>
#include <chrono>
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::default_random_engine generator(seed);
std::uniform_int_distribution<uint> distribution;
uint randomUint = distribution(generator);
有很好的 video 涵盖了主题。正如评论者 user2357112 所指出的,我们可以应用不同的随机引擎,然后应用不同的分布,但对于我的特定用途,上述方法非常有效。
我不确定这是否可以 class 确定为错误,但它有一个解释。让我们来看看情况:
看rand's implementation。您会看到它只是使用最后生成的值进行的计算。
您正在使用 QTime::currentTime().msec() 进行播种,它本质上受限于较小的值范围 0..999,但是 qsrand 接受范围 0.. 上的 uint 变量4294967295.
结合这两个因素,您就有了一个模式。
出于好奇: 尝试使用 QTime::currentTime().msec() + 100000000
进行播种现在大多数时候第一个值可能会比第二个大。
我不会太担心。 "pattern" 似乎只发生在前两个生成的值上。在那之后,一切似乎都恢复了正常。
编辑:
为了使事情更清楚,请尝试 运行 下面的代码。它将使用所有可能的毫秒值(范围:0..999)作为种子来比较前两个生成的值,看看哪个更小:
int totalCalls, leftIsSmaller = 0;
for (totalCalls = 0; totalCalls < 1000; totalCalls++)
{
qsrand(totalCalls);
if (qrand() < qrand())
leftIsSmaller++;
}
qDebug() << (100.0 * leftIsSmaller) / totalCalls;
它将打印 94.8,这意味着 94.8% 的时间第一个值将小于第二个。
结论: 当使用当前毫秒作为种子时,您会看到前两个值的模式。我在这里做了一些测试,在生成第二个值后,模式似乎消失了。我的建议:找到一个 "good" 值来调用 qsrand (显然应该只调用一次,在程序的开头)。一个好的值应该跨越 uintclass 的整个范围。看看这个其他问题以获得一些想法:
- Recommended way to initialize srand?
另外,看看这个:
考虑到根据少量样本对统计现象做出判断可能会产生误导,我决定 运行 进行一个小实验。我运行下面的代码:
int main()
{
int i = 0;
int j = 0;
while (i < RAND_MAX)
{
srand(time(NULL));
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
}
}
基本上打印了第一个生成的数字小于第二个的次数的百分比。下面是该比率的图表:
如您所见,在实际新种子少于 50 个后,它实际上接近 0.5。
正如评论中所建议的,我们可以修改代码以在每次迭代中使用连续的种子并加快收敛速度:
int main()
{
int i = 0;
int j = 0;
int t = time(NULL);
while (i < RAND_MAX)
{
srand(t);
int r1 = rand();
int r2 = rand();
if (r1 < r2)
++j;
++i;
if (i%10000 == 0) {
printf("%g\n", (float)j / (float)i);
}
++t;
}
}
这给了我们:
也非常接近 0.5。
虽然 rand
肯定不是最好的伪随机数生成器,它经常在第一个 运行 期间生成较小数字的说法似乎没有根据。