【问题标题】:Why does vc++ compiler cause this statistical pattern?为什么 vc++ 编译器会导致这种统计模式?
【发布时间】:2018-10-21 07:17:55
【问题描述】:

我正在运行以下程序:

#include <iostream>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <chrono>
using namespace std;

const int N = 200;          // Number of tests.
const int M = 2000000;      // Number of pseudo-random values generated per test.
const int VALS = 2;         // Number of possible values (values from 0 to VALS-1).
const int ESP = M / VALS;   // Expected number of appearances of each value per test.

int main() {
    for (int i = 0; i < N; ++i) {
        unsigned seed = chrono::system_clock::now().time_since_epoch().count();
        srand(seed);
        vector<int> hist(VALS, 0);
        for (int j = 0; j < M; ++j) ++hist[rand() % VALS];
        int Y = 0;
        for (int j = 0; j < VALS; ++j) Y += abs(hist[j] - ESP);
        cout << Y << endl;
    }
}

此程序执行 N 次测试。在每个测试中,我们生成介于 0 和 VALS-1 之间的 M 个数字,同时我们不断计算它们在直方图中的出现次数。最后,我们在 Y 中累积误差,这些误差对应于直方图的每个值与期望值之间的差异。由于这些数字是随机生成的,因此理想情况下,每个测试都会出现 M/VALS 次。

运行我的程序后,我分析了结果数据(即 Y 的 200 个值),我意识到发生了一些我无法解释的事情。我看到,如果程序用 vc++ 编译并给出一些 N 和 VALS(在这种情况下 N = 200 和 VALS = 2),对于不同的 M 值,我们会得到不同的数据模式。对于某些测试,结果数据遵循正常分布,而对于某些测试,它没有。此外,这种类型的结果似乎会随着 M(每次测试中生成的伪随机值的数量)的增加而变化:

  • M = 10K,数据不正常:

  • M = 100K,数据正常:

  • 等等:

如您所见,根据 M 的值,生成的数据服从正态分布或服从非正态分布(双峰、狗粮或均匀分布),其中 Y 的更极端值具有更大的存在。

如果我们使用其他 C++ 编译器(gcc 和 clang)编译程序,则不会出现这种结果的多样性。在这种情况下,看起来我们总是获得 Y 值的半正态分布:

您对此有何看法?解释是什么?

我通过这个在线编译器进行了测试:http://rextester.com/l/cpp_online_compiler_visual

【问题讨论】:

标签: c++ visual-c++ random statistics


【解决方案1】:

程序会生成分布不均的随机数(不均匀、独立)。

  1. 函数rand 是出了名的糟糕。
  2. 使用余数运算符 % 将数字带入范围有效地丢弃了除低位之外的所有位。
  3. 每次循环都会重新播种 RNG。

[编辑] 我刚刚注意到const int ESP = M / VALS;。您需要一个浮点数。

尝试下面的代码并报告。使用新的 &LT;random> 有点乏味。很多人写一些小的库代码来简化它的使用。

#include <iostream>
#include <vector>
#include <cmath>
#include <random>
#include <chrono>
using namespace std;

const int N = 200;          // Number of tests.
const int M = 2000000;      // Number of pseudo-random values generated per test.
const int VALS = 2;         // Number of possible values (values from 0 to VALS-1).
const double ESP = (1.0*M)/VALS; // Expected number of appearances of each value per test.

static std::default_random_engine engine;

static void seed() {
    std::random_device rd;
    engine.seed(rd());
}
static int rand_int(int lo, int hi) {
    std::uniform_int_distribution<int> dist (lo, hi - 1);
    return dist(engine);
}
int main() {
    seed();
    for (int i = 0; i < N; ++i) {
        vector<int> hist(VALS, 0);
        for (int j = 0; j < M; ++j) ++hist[rand_int(0, VALS)];
        int Y = 0;
        for (int j = 0; j < VALS; ++j) Y += abs(hist[j] - ESP);
        cout << Y << endl;
    }
}

【讨论】:

  • 你还在我们身边吗?
  • 感谢您的评论和代码。 uniform_int_distribution 缺少其数据类型声明。您的程序生成的数据遵循某种半正态分布(如预期的那样)。我的问题仍然没有答案。即使 rand() 的缺陷很重要,了解 1. 为什么它们在这种情况下很重要以及它们如何影响数据以及 2. 为什么输出数据取决于编译器仍然会很有趣。
  • 我确实回答了这个问题,“您对此有何看法,解释是什么?”解释分三部分。至于为什么不同的编译器会给出不同的结果,可以归结为rand 的不同实现。 VC++ 的实现可能比大多数都差。
  • 为什么 rand() 中的缺陷很重要:垃圾输入,垃圾输出。深入的解释太复杂了,无法进入这里。请参阅:scribd.com/document/33641002/… uniform_int_distribution 的默认类型是 int。拿礼物马。
  • 顺便说一句,Marsaglia 的论文更能证明关于软件工程的一切都是在 1968 年发现的。
猜你喜欢
  • 1970-01-01
  • 2020-04-26
  • 2012-01-16
  • 2014-05-23
  • 1970-01-01
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
相关资源
最近更新 更多