Генератор случайных чисел, который производит степенное распределение?
Я пишу несколько тестов для приложения Linux для командной строки C++. Я хотел бы сгенерировать кучу целых чисел с степенным распределением / длинным хвостом. То есть я получаю некоторые цифры очень часто, но большинство из них относительно редко.
В идеале это были бы магические уравнения, которые я мог бы использовать с rand() или одной из случайных функций stdlib. Если нет, то простой в использовании кусок C/C++ был бы великолепен.
Спасибо!
4 ответа
На этой странице в Wolfram MathWorld обсуждается, как получить степенное распределение из равномерного распределения (именно это обеспечивает большинство генераторов случайных чисел).
Краткий ответ (вывод по вышеуказанной ссылке):
x = [(x1^(n+1) - x0^(n+1))*y + x0^(n+1)]^(1/(n+1))
где y - равномерная переменная, n - степень распределения, x0 и x1 определяют диапазон распределения, а x - ваша степенная распределенная переменная.
Если вы знаете, какое распределение вы хотите (называемое функцией распределения вероятностей (PDF)) и правильно ли оно нормализовано, вы можете интегрировать его для получения функции накопительного распределения (CDF), а затем инвертировать CDF (если это возможно), чтобы получить преобразование, которое вы нужно из униформы [0,1]
раздача по вашему желанию.
Итак, вы начинаете с определения дистрибутива, который вы хотите.
P = F(x)
(для х в [0,1]) затем интегрируется, чтобы дать
C(y) = \int_0^y F(x) dx
Если это можно перевернуть, вы получите
y = F^{-1}(C)
Так что звоните rand()
и включите результат в виде C
в последней строке и используйте y.
Этот результат называется фундаментальной теоремой выборки. Это хлопотно из-за требования нормализации и необходимости аналитически инвертировать функцию.
В качестве альтернативы вы можете использовать технику отклонения: бросить число равномерно в желаемом диапазоне, затем бросить другое число и сравнить с PDF в месте, не определенном вашим первым броском. Отклонить, если второй бросок превышает PDF. Как правило, неэффективно для PDF-файлов с большим количеством областей с низкой вероятностью, например, с длинными хвостами...
Промежуточный подход включает в себя инвертирование CDF с помощью грубой силы: вы сохраняете CDF как таблицу поиска и выполняете обратный поиск, чтобы получить результат.
Настоящий вонючий здесь так просто x^-n
распределения не нормируются в диапазоне [0,1]
, поэтому вы не можете использовать теорему выборки. Попробуйте (x+1)^-n вместо...
Я просто хотел провести реальное моделирование как дополнение к (справедливо) принятому ответу. Хотя в R этот код настолько прост, что представляет собой (псевдо)-псевдокод.
Одно крошечное различие между формулой Wolfram MathWorld в принятом ответе и другими, возможно, более общими уравнениями заключается в том, что показатель степенного закона n
(который обычно обозначается как альфа) не имеет явного отрицательного знака. Таким образом, выбранное альфа-значение должно быть отрицательным, обычно от 2 до 3.
x0
а также x1
обозначают нижний и верхний пределы распределения.
Итак, вот оно:
x1 = 5 # Maximum value
x0 = 0.1 # It can't be zero; otherwise X^0^(neg) is 1/0.
alpha = -2.5 # It has to be negative.
y = runif(1e5) # Number of samples
x = ((x1^(alpha+1) - x0^(alpha+1))*y + x0^(alpha+1))^(1/(alpha+1))
hist(x, prob = T, breaks=40, ylim=c(0,10), xlim=c(0,1.2), border=F,
col="yellowgreen", main="Power law density")
lines(density(x), col="chocolate", lwd=1)
lines(density(x, adjust=2), lty="dotted", col="darkblue", lwd=2)
или построены в логарифмическом масштабе:
h = hist(x, prob=T, breaks=40, plot=F)
plot(h$count, log="xy", type='l', lwd=1, lend=2,
xlab="", ylab="", main="Density in logarithmic scale")
Вот краткое изложение данных:
> summary(x)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.1000 0.1208 0.1584 0.2590 0.2511 4.9388
Я не могу комментировать математику, необходимую для получения степенного распределения (другие посты содержат предложения), но я бы посоветовал вам ознакомиться со средствами случайных чисел в стандартной библиотеке TR1 C++ в <random>
, Они обеспечивают больше функциональности, чем std::rand
а также std::srand
, Новая система задает модульный API для генераторов, движков и дистрибутивов и предоставляет набор пресетов.
Включены предустановки распространения:
uniform_int
bernoulli_distribution
geometric_distribution
poisson_distribution
binomial_distribution
uniform_real
exponential_distribution
normal_distribution
gamma_distribution
Когда вы определите свое распределение по степенным законам, вы сможете подключить его к существующим генераторам и двигателям. Книга Питера Беккера " Расширения стандартной библиотеки C++" имеет большую главу <random>
,
Вот статья о том, как создавать другие дистрибутивы (с примерами для Коши, Chi-squared, Student t и Snedecor F)