Advertisement
Guest User

Программа моделирования распределения явки на участках

a guest
Mar 31st, 2018
310
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 7.21 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * Моделирование N участков с случайным числом избирателей и случайно
  5.  * распределенной явкой, с целью проверить, как будет выглядеть
  6.  * график распределения числа участков с определенной явкой.
  7.  *
  8.  * Число избирателей на участке распределено равномерно.
  9.  *
  10.  * Явка распределена нормально.
  11.  *
  12.  * На выходе программа генерирует CSV-файл с двумя колонками:
  13.  *
  14.  * {процент в бине}, {число участков в бине}
  15.  *
  16.  * Например:
  17.  *
  18.  * 0,5
  19.  * 1,13
  20.  * 2,6
  21.  * ....
  22.  * 100,12
  23.  *
  24.  */
  25.  
  26. if (!empty($argv[1]) && $argv[1] == '--test-gen') {
  27.     // тестирование генератора нормального распределения
  28.     $bins = array_fill(0, 200, 0);
  29.     $num = 0;
  30.     $gen = generateMtRand();
  31.     foreach (generateNormallyDistributedNumber($gen, 100, 20) as $number) {
  32.         $round = round($number);
  33.         $bins[$round]++;
  34.  
  35.         // echo "$num,$number\n";
  36.         $num++;
  37.         if ($num >= 5000) {
  38.             break;
  39.         }
  40.     }
  41.  
  42.     foreach ($bins as $key => $bin) {
  43.         echo "$key,$bin\n";
  44.     }
  45.  
  46.     exit();
  47. }
  48.  
  49. // Число участков
  50. $uikCount = 50000;
  51.  
  52. // Мин. число число изб. на участке
  53. $minVoters = 1000;
  54.  
  55. // Макс. число изб. на участке
  56. $maxVoters = 2000;
  57.  
  58. // Средняя явка в процентах
  59. $meanTurnout = 55;
  60.  
  61. // Станд. отклонение явки (сигма) в процентах
  62. // 99% значений будут в диапазоне (среднее +- 3 сигмы)
  63. // При этом, значения явки выше 100% приводятся к 100%
  64. $turnoutStdDev = 10;
  65.  
  66. // Размер бина в процентах
  67. $binSize = 1;
  68. if (in_array('--small-bin', $argv)) {
  69.     $binSize = 0.2;
  70. }
  71.  
  72. // Генератор числа людей на участке, равномерно распределенный
  73. $voterGen = generateMtRand();
  74. if (in_array('--voters-norm', $argv)) {
  75.     // Опция для генерации числа изб-й на участке нормальным распределением
  76.     $voterGen = generateNormallyDistributedNumber($voterGen, 0.5, 0.2);
  77. }
  78.  
  79.  
  80. // Генератор явки, нормально распределенный
  81. $randomGen = generateMtRand();
  82. if (in_array('--random-int', $argv)) {
  83.     // опция для использования более случайного генератора явки
  84.     $randomGen = generateRandomInt();
  85. }
  86.  
  87. $turnoutGen = generateNormallyDistributedNumber($randomGen, $meanTurnout, $turnoutStdDev);
  88.  
  89. // Генерируем участки и явку на них
  90. // Сохраняем результат в массив
  91. $uiks = [];
  92.  
  93. for ($i=0; $i < $uikCount; $i++) {
  94.  
  95.     // Всего избирателей
  96.     $voterRandom = $voterGen->current();
  97.     $voterGen->next();
  98.     $votersTotal = round($minVoters + $voterRandom * ($maxVoters - $minVoters));
  99.    
  100.     $turnoutPercent = $turnoutGen->current();
  101.     $turnoutGen->next();
  102.  
  103.     // При этом явка не может быть выше 100% или ниже 0%
  104.     $turnoutPercent = min(100, max(0, $turnoutPercent));
  105.  
  106.     // Явка, генерируем цифру в процентах и приводим к целому числу людей
  107.     // Чтобы учесть эффекты от деления целых чисел
  108.     $turnoutPeople = round($turnoutPercent * $votersTotal / 100);
  109.  
  110.     $uiks[] = [
  111.         'voters'    => $votersTotal,
  112.         'turnout'   => $turnoutPeople
  113.     ];
  114. }
  115.  
  116. // Мы имеем данные по каждому УИКу, теперь раскладываем их по бинам
  117. // Число бинов
  118. $binCount = ceil(100 / $binSize);
  119. $bins = [];
  120. for ($i=0; $i < $binCount; $i++) {
  121.  
  122.     $min = ($i * 100 / $binCount);
  123.     $max = (($i + 1) * 100 / $binCount);
  124.  
  125.     $bins[$i] = [
  126.         // В этом бине явка от столько процентов
  127.         'min'  =>  $min,
  128.         // До стольки
  129.         'max'    =>  $max,
  130.         // Число участков с такой явкой
  131.         'count' =>  0
  132.     ];
  133. }
  134.  
  135. foreach ($uiks as $uik) {
  136.     // Явка в процентах
  137.     $uikPercentTurnout = $uik['turnout'] / $uik['voters'] * 100;
  138.  
  139.     // при определении номера бина округляем вниз
  140.     $binNumber = floor($uikPercentTurnout / $binSize);
  141.  
  142.     // Для 100% данные идут в последний бин
  143.     if ($binNumber >= count($bins)) {
  144.         $binNumber = count($bins) - 1;
  145.     }
  146.  
  147.     $bins[$binNumber]['count']++;
  148. }
  149.  
  150. // Выводим результаты по бинам
  151. foreach ($bins as $bin) {
  152.     printf("%3f,%d\n", $bin['min'], $bin['count']);
  153. }
  154.  
  155. /* echo "\n\nUiks\n";
  156. var_dump($uiks);
  157. echo "\n\nBins\n";
  158. var_dump($bins);
  159. */
  160.  
  161.  
  162. /**
  163.  * Возвращает генератор, который на каждой итерации возвращает
  164.  * случайное число с нормальным распределением, средним mu и
  165.  * стандартным отклонением sigma.
  166.  *
  167.  * На вход также передается функция-генератор случайных чисел. Это
  168.  * позволяет использовать разные алгоритмы генерации случайных
  169.  * чисел (mt_rand, random_bytes итд.). $random должен возвращать
  170.  * случайное число от 0 до 1.
  171.  *
  172.  * Использует алгоритм с преобразованием Box-Muller
  173.  * https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
  174.  */
  175. function generateNormallyDistributedNumber(Generator $random, $mu, $sigma)
  176. {
  177.     $two_pi = M_PI * 2;
  178.     $epsilon = 1e-20;
  179.  
  180.     while (true) {
  181.  
  182.         do {
  183.  
  184.             $u1 = $random->current();
  185.             $random->next();
  186.  
  187.             $u2 = $random->current();
  188.             $random->next();
  189.  
  190.         } while ($u1 <= $epsilon);
  191.  
  192.         $z0 = sqrt(-2.0 * log($u1)) * cos($two_pi * $u2);
  193.         $z1 = sqrt(-2.0 * log($u1)) * sin($two_pi * $u2);
  194.         yield $z0 * $sigma + $mu;
  195.         yield $z1 * $sigma + $mu;
  196.     }
  197. }
  198.  
  199. /**
  200.  * Генератор случайных чисел в диапазоне 0..1 на основе
  201.  * mt_rand (Вихрь Мерсенна):
  202.  * https://ru.wikipedia.org/wiki/%D0%92%D0%B8%D1%85%D1%80%D1%8C_%D0%9C%D0%B5%D1%80%D1%81%D0%B5%D0%BD%D0%BD%D0%B0
  203.  */
  204. function generateMtRand()
  205. {
  206.     $max = mt_getrandmax();
  207.     while (true) {
  208.         $random = mt_rand() / $max;
  209.         yield $random;
  210.     }
  211. }
  212.  
  213. /**
  214.  * Генератор случайных числел в диапазоне 0..1 на основе
  215.  * random_int() (доступно с PHP7, работает медленнее)
  216.  */
  217. function generateRandomInt()
  218. {
  219.     $max = PHP_INT_MAX;
  220.     while (true) {
  221.         $random = random_int(0, $max) / $max;
  222.         yield $random;
  223.     }
  224. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement