Дискретное распределение не симметрично - PullRequest
0 голосов
/ 02 июля 2018

Я пытаюсь сэмплировать дискретное распределение с помощью функции std::discrete_distribution. Вот мве:

// 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:

Обратите внимание на асимметрию, особенно на нули в конце. Я не вижу, что я сделал не так. Что-то не так с кодом или происходит какое-то округление, которое я не вижу. Спасибо.

1 Ответ

0 голосов
/ 02 июля 2018

Кажется, проблема в математике с плавающей точкой. В частности, кажется вероятным, что распределение сохраняет промежуточный итог при нормализации его весов, что приводит к потере крошечной вероятности в конце. В простом примере предположим, что double может хранить только 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, в отличие от последнего. Видя, что только те значения, которые меньше, чем один ULP около 1, печатаются как ноль, я поддерживаю отмену как проблему.

...