Генератор псевдослучайных чисел - экспоненциальное распределение

Я хотел бы генерировать некоторые псевдослучайные числа, и до сих пор я был очень доволен функцией .Net library Random.Next(int min, int max). Предполагается, что PRNG этого сорта должны использовать Uniform distribution, но я очень хотел бы генерировать некоторые числа, используя Экспоненциальное распределение.

Я программирую на С#, хотя я буду принимать псевдокод или С++, Java или тому подобное.

Любые предложения/фрагменты кода/алгоритмы/мысли?

Ответ 1

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

Итак, создайте равномерное случайное число, u, в [0,1), затем вычислите x по:

x = log(1-u)/( & минус; & Lambda; ),

где & lambda; - параметр скорости экспоненциального распределения. Теперь x - случайное число с экспоненциальным распределением. Заметим, что log выше ln, натуральный логарифм.

Ответ 2

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

Если у вас есть желаемое распределение F(x), нормированное на [a,b]. Вы вычисляете

C(y) = \int_a^y F(x) dx

инвертируйте это, чтобы получить C^{-1}, равномерно наложите z на [0,1) и найдите

x_i = C^{-1}(z_i)

который будет иметь желаемое распределение.


В вашем случае: F(x) = ke^{-kx}, и я предполагаю, что вы хотите [0,infinity]. Получаем:

C(y) = 1 - e^{-ky}

которая обратима, чтобы дать

x = -1/k  ln(1 - z)

для z, равномерно распределенного на [0,1).


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

Ответ 3

Если вам нужны хорошие случайные числа, подумайте о ссылке на подпрограммы gsl: http://www.gnu.org/software/gsl/. У них есть рутина gsl_ran_exponential. Если вы хотите генерировать случайные числа, используя встроенный генератор с равномерным распределением на [0, 1) (например, u = Random.Next(0, N-1)/N, для некоторого большого N), просто используйте:

-mu * log (1-u)

См. randist/exponential.c в источнике gsl.

EDIT: просто для сравнения с некоторыми более поздними ответами - это эквивалентно mu = 1/lambda. mu здесь - среднее значение распределения, также называемое параметром scale на странице wikipedia, связанной с OP, и лямбда - параметр скорости.

Ответ 4

Одно интересное свойство экспоненциального распределения: Рассмотрим процесс прихода с экспоненциальными промежутками времени. Возьмите любой период времени (t1, t2) и прибытия в этот период. Эти поступления UNIFORMLY распределены между t1 и t2. (Шелдон Росс, Стохастические процессы).

Если у меня есть генератор псевдослучайных чисел и по какой-то причине (например, мое программное обеспечение не может вычислять журналы), вы не хотите делать вышеуказанное преобразование, но хотите экспоненциальный r.v. со значением 1.0.

Вы можете:

1) Создать 1001 U (0,1) случайных величин.

2) Сортировать по порядку

3) Вычтите второй из первого, третьего со второго,..., чтобы получить 1000 различий.

4) Эти различия являются экспоненциальными RVs из распределения со средним значением = 1.0.

Менее эффективным, я думаю, но средством для достижения той же цели.

Ответ 5

Открытый Необычная библиотека Maths от Дэна Дайера предоставляет генераторы случайных чисел, распределения вероятностей, комбинаторику и статистику для Java.

Среди других ценных классов ExponentialGenerator по существу реализовала идею, объясненную @Alok Singhal. В в своем учебном блоге приведен фрагмент кода для моделирования случайного события, которое произошло в среднем 10 раз в минуту:

final long oneMinute = 60000;
Random rng = new MersenneTwisterRNG();

// Generate events at an average rate of 10 per minute.
ExponentialGenerator gen = new ExponentialGenerator(10, rng);
boolean running = true;
while (true)
{
    long interval = Math.round(gen.nextValue() * oneMinute);
    Thread.sleep(interval);

    // Fire event here.
}

Конечно, если вы предпочитаете единицу времени per second (вместо a minute здесь), вам просто нужно установить final long oneMinute = 1000.

Подойдя глубже в исходный код метода nextValue() ExponentialGenerator, вы найдете так называемую обратную выборку обратного преобразования в Generation_exponential_variates [wiki]:

public Double nextValue()
{
    double u;
    do
    {
        // Get a uniformly-distributed random double between
        // zero (inclusive) and 1 (exclusive)
        u = rng.nextDouble();
    } while (u == 0d); // Reject zero, u must be positive for this to work.
    return (-Math.log(u)) / rate.nextValue();
}  

P.S.: Недавно я использую библиотеку Uncommons Maths. Спасибо Дэн Дайер.

Ответ 6

Если я понимаю вашу проблему, и вы можете принять конечное число PRNG, вы можете выполнить такой подход, как:

  • Создайте массив, в котором каждый элемент находится в экспоненциальном дистрибутиве
  • Генерировать PRNG, который является целым индексом в массиве. Верните элемент в массив по этому индексу.

Ответ 7

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

// sorry.. pseudocode, mine was in Tcl:

int weighted_random (int max) {
    float random_number = rand();
    return floor(max - ceil( max * random_number * random_number))
}

Конечно, это формула квадратизации случайного числа, поэтому вы производите случайное число по квадратичной кривой.