0

我正在尝试使用该std::discrete_distribution函数对离散分布进行采样。这是一个mwe:

// discrete_distribution
#include <iostream>
#include <random>

int main()
{
  const int nrolls = 10000; // number of experiments
  const int nstars = 100;   // maximum number of stars to distribute
  std::vector<double> weights;
  weights = {1.28503e-22, 1.67881e-17, 8.99861e-13, 1.70418e-08, 9.27031e-05,
    0.106935, 16.1967, 140.325, 16.1967, 0.106935, 9.27031e-05, 1.70418e-08,
    8.99861e-13, 1.67881e-17, 1.28503e-22};

  std::default_random_engine generator;
  std::discrete_distribution<int> distribution(weights.begin(), weights.end());

  for (double x:distribution.probabilities()) std::cout << x << " ";
  std::cout << std::endl;

  int p[15]={};

  for (int i=0; i<nrolls; ++i) {
    int number = distribution(generator);
    ++p[number];
  }

  std::cout << "a discrete_distribution:" << std::endl;
  for (int i=0; i<15; ++i)
    std::cout << i << ": " << std::string(p[i]*nstars/nrolls,'*') << std::endl;

  return 0;
}

这给出了:

7.43082e-25 9.70789e-20 5.20354e-15 9.8546e-11 5.36065e-07 
0.000618363 0.0936591 0.811444 0.0936591 0.000618363 5.36065e-07
9.85459e-11 5.10703e-15 0 0

a discrete_distribution:
0: 
1: 
2: 
3: 
4: 
5: 
6: *********
7: ********************************************************************************
8: *********
9: 
10: 
11: 
12: 
13: 
14:

注意不对称,尤其是最后的零。我看不出我做错了什么。代码是否有问题,或者发生了一些我看不到的舍入。谢谢。

4

1 回答 1

0

问题似乎是浮点数学。特别是,分布似乎很可能在对其权重进行归一化的同时保持运行总数,导致它在最后失去微小的概率。在一个简单的例子中,假设双精度数只能存储 2 个有效数字(实际情况更接近 16),并且您的权重分别为 0.001、1.0、1.0 和 0.001:

它将权重相加为 2.002(它只能表示为 2.00),然后继续对权重进行归一化。第一个变为 0.001/2.00 = 0.0005。然后下一个是 0.5,总计 0.5005(即 5.00)。第三个权重也是 0.5,所以总数已经是 1.00。与允许总和的差值为 0.00,因此它不能为最后一个事件赋予正权重。

我知道这不是一个完美的例子(因为权重仍然没有完全加起来),但我希望你明白这一点 - 你的标准库实现和/或你的浮点设置在这里弄乱了你的结果,因为消除。并不是说让你的概率为 1e-20 的事件发生在合理范围内,但你是对的,理论上它应该保持对称性。

对于那些说“没有足够的精度来打印它”的人:我不同意,因为理想情况下这些值应该仍然是对称的,并且第一个值不像最后一个那样打印为 0。看到只有那些小于 1 左右的 ULP 的值被打印为零,我支持取消作为问题。

于 2018-07-02T16:58:46.130 回答