Я хотел бы генерировать некоторые псевдослучайные числа, и до сих пор я был очень доволен функцией .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))
}
Конечно, это формула квадратизации случайного числа, поэтому вы производите случайное число по квадратичной кривой.