【问题标题】:discrete distribution returns integers out of bound离散分布返回整数越界
【发布时间】:2020-08-03 15:31:19
【问题描述】:

我写了下面的函数来采样而不用替换。它返回一个vector<int>,表示在一些点中选取的样本。作为输入,我有一个vector<double>,其中包含所需样本大小的概率和一个整数。对于函数,我使用离散分布:

http://www.cplusplus.com/reference/random/discrete_distribution/discrete_distribution/

vector<int> samplingwoutreplacement(vector<double> probs, int samplesize) {
    random_device rd;
    mt19937 generator(rd());
    vector<int> sample;
    sample.reserve(samplesize);
    for (int i = 0; i < samplesize; i++) {
        discrete_distribution<int> distribution(probs.begin(), probs.end());
        int currentpick = distribution(generator);
        if (currentpick >= probs.size()) {
            cout  << endl << "error: range overstepped; current pick is: " << currentpick << endl;
            cout << "probs.size = " << probs.size() << endl;
            for (int j = 0; j < probs.size(); j++) {
                cout << probs[j] << endl;
            }
        }
        probs[currentpick] = 0;
        sample.push_back(currentpick);
    }
    return sample;
}

在我的应用程序中,我多次使用这种采样,经过多次迭代后,离散分布返回的整数大于包含概率的向量的大小。 (更准确地说,我的向量大小为 178,我得到的返回值为 178,但应该得到介于 0 和 177 之间的整数。)这是怎么发生的?

【问题讨论】:

  • rdgenerator 应该是 staticdistribution 应该在循环之外。 rd 甚至不需要,真的。你可以去mt19937 generator(random_device{}());
  • 我没有收到那个错误。我尝试了 10M 的样本大小。进行我上面建议的调整后,我的程序也运行得更快了。
  • 我想重复选择数字,但消除重复的可能性。因此,我将所选数字的概率设置为 0,并使用更新的概率初始化分布。所以,在我看来,分发不应该在循环之外。
  • 对,明白了。在那部分上涂上光泽。将分布移回仍然不会触发错误。
  • 更明确地说:pastebin 链接中的权重总和为 INFINITY (6.46e307 + 1.29e308 > 1.9e308 > DBL_MAX),这很容易在库中测试一个未充分测试的极端情况实施。

标签: c++ language-lawyer distribution sampling


【解决方案1】:

在 C++ 标准中,我们可以在 26.6.8.6.1 [rand.dist.samp.discrete] 阅读有关 std::discrete_distribution 的信息(重点是我的)

一个discrete_­distribution随机数分布产生随机整数i,0≤i,按照离散概率函数分布 P(i | p0, ..., pn−1) = pi.

除非另有说明,否则分布参数计算为:pk = wk/S for k = 0, …, n−1, 其中wk,俗称权重应为非负、非NaN、非无穷大此外,以下关系应成立:0 0 +⋯ + wn−1

提问者使用的某些权重(如 1.29272e+308)太大以至于它们的总和超出了double 的范围,因此 S 的值(变为无穷大)并且下面的计算变得毫无意义。

我在这种极端情况下测试了 gcc、clang 和 MSVC 的行为,发现虽然 gcc 和 clang 产生的分布所有概率都为零,而 std::discrete_distribution::operator() 总是返回 n - 1 ,MSVC 也会产生一个所有概率为零的分布,返回的整数总是 n(一个超出范围的值,正如提问者所经历的那样)。 p>

根据我的解释,这不是一种符合标准的行为,即使值得称赞的是,OP 的输入参数导致违反先决条件。

【讨论】:

  • 正如这里所引用的,它不排除总和是无限的,只排除权重。
  • @halfflat 但是如果总和趋于无穷大,所有概率变为 0(例如,参见 sample)。
  • 当然,它几乎肯定没有用,但它仍然不应该给出超出允许范围的值。即使使用合理的权重,也不能保证 sum (w_i/S) 实际上会完全等于 1,因此实现必须在某种程度上处理此类问题。
  • @halfflat 我明白你的意思,即使它可以被视为违反先决条件,恕我直言,operator() 不应允许返回范围之外的值。我可以重现错误行为的唯一编译器是MSVC
  • @Bob__ 感谢您提供全面而有帮助的回答。有趣的是,gcc 和 clang 不会发生这种行为。我使用 MS Visual Studio 19。
猜你喜欢
  • 1970-01-01
  • 2019-10-22
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
  • 2021-04-06
  • 2013-11-17
  • 2012-07-08
  • 1970-01-01
相关资源
最近更新 更多