Случайное число в диапазоне, смещенном к минимальному значению этого диапазона - PullRequest
0 голосов
/ 06 апреля 2020

Я хочу генерировать случайные числа в пределах диапазона (1 - 100000), но вместо чисто случайных я хочу, чтобы результаты основывались на некотором распределении. Что я имею в виду, что в целом я хочу, чтобы числа «группировались» вокруг минимального значения диапазона (1).

Я читал о преобразовании Бокса – Маллера и нормальных распределениях, но я не совсем уверен, как использовать их для создания генератора чисел.

Как мне добиться такого алгоритма, используя C#?

1 Ответ

0 голосов
/ 08 апреля 2020

Есть много способов сделать это (с использованием равномерного распределения prng), но немногие, о которых я знаю:

  1. Объедините более однородные случайные переменные, чтобы получить желаемое распределение.

    Я не математик, но есть уравнения для этого. Такое решение обычно имеет лучшие свойства со случайности и статистической точки зрения. Для получения дополнительной информации см. Знаменитые:

    , но существует ограниченное количество распределений, для которых мы знаем комбинации.

  2. Применение нелинейной функции к равномерной случайной переменной

    Это простейшая реализация. Вы просто используете плавающие случайные числа в диапазоне <0..1>, применяете к ним нелинейную функцию (которая изменяет распределение в соответствии с желаемой формой) (в то время как результат все еще находится в диапазоне <0..1>) и масштабируете результат, например, в свой целочисленный диапазон ( в C ++):

    floor( pow( random(),5 ) * 100000 )
    

    Проблема в том, что это просто слепая подгонка дистрибутива, поэтому обычно нужно немного подкорректировать константы. Было бы неплохо визуализировать гистограммы и графики случайности, чтобы увидеть качество результата, как показано здесь:

    Вы также можете избежать слишком слепой подгонки с помощью BEZIERS, как здесь:

  3. Распределение после псевдослучайного генератора

    Есть два подхода, которые я знаю, для этого проще:

    1. создать достаточно большой массив размером n
    2. заполните его всеми значениями, следующими за распределением

      , чтобы просто l oop через все значения, которые вы хотите вывести, и вычислите, сколько из них будет в массиве n size (из вашего дистрибутива) и добавьте это количество чисел в массив. Остерегайтесь, что заполненный размер массива может быть немного меньше, чем n из-за округления. Если n слишком мало, вы пропустите некоторые менее встречающиеся числа. поэтому, если вы умножите вероятность наименьшего вероятного числа на n, оно должно быть не менее >=1. После заполнения измените n на реальный размер массива (количество действительно заполненных чисел в нем).

    3. перемешайте массив

    4. теперь используйте массив как линейный список случайных чисел

      , поэтому вместо random() вы просто выбираете число из массива и переходите к следующему. Как только вы попадаете в n -ое значение, перемешиваете массив и начинаете с первого снова.


    Это решение обладает очень хорошими статистическими свойствами (точно соответствует распределению), но свойства случайности не являются хорошими и требуют массива и случайного перемешивания. Для получения дополнительной информации см .:


    Другой вариант из этого, чтобы избежать использования массива и тасования. Это выглядит так:

    1. получить случайное значение в диапазоне <0..1>
    2. применить обратную распределенную функцию распределения для преобразования в целевой диапазон

      , как вы можете это выглядит как # 2 Применить нелинейную функцию ... , но вместо "некоторой" нелинейной функции вы используете непосредственное распределение. Так что если p(x) - это вероятность x в диапазоне <0..1>, где 1 означает 100%, то нам нужна функция, которая накапливает все вероятности вплоть до x (извините, не знаю точного математического термина в английском языке) sh). Для целых чисел:

      f(x) = p(0)+p(1)+...+p(x)
      

      Теперь нам нужна обратная функция g() для этого так:

      y = f(x)
      x = g(y) 
      

      Теперь, если моя память хорошо мне служит, тогда поколение должно выглядеть так:

      y = random(); // <0..1>
      x = g(y);     // probability -> value 
      

      Во многих дистрибутивах есть функция g(), но для тех, кто не (или нам лень ее выводить), вы можете использовать бинарный поиск в p(x). Лень его кодировать, поэтому здесь медленнее линейного поиска версии:

      for (x=0;x<max;x++) if (f(x)>=y) break;
      

      Итак, когда все сложено (и только с использованием p(x)), я получил (C ++):

      y=random();               // uniform distribution pseudo random value in range <0..1>
      for (f=0.0,x=0;x<max;x++) // loop x through all values
       {
       f+=p(x);                 // f(x) cumulative distribution function
       if (f>=y) break;        
       }
      // here x is your pseudo random value following p(x) distribution
      

    Такое решение обычно очень хорошо и статистически, и случайно свойств и не требует, чтобы распределение было непрерывной функцией (вместо этого это может быть просто массив значений).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...