Генератор случайных чисел, который дает степенное распределение?
Я пишу несколько тестов для Linux-приложения командной строки С++. Я хотел бы создать кучу целых чисел с распределением полномочий/длинного хвоста. Смысл, я получаю некоторые цифры очень часто, но большинство из них относительно нечасто.
В идеале были бы только некоторые магические уравнения, которые я мог бы использовать с помощью rand() или одной из случайных функций stdlib. Если нет, простой в использовании кусок C/С++ будет большим.
Спасибо!
Ответы
Ответ 2
Если вам известно распределение, которое вы хотите (называемое функцией распределения вероятностей (PDF)) и правильно ли оно нормализовано, вы можете интегрировать его для получения функции кумулятивного распределения (CDF), а затем инвертировать CDF (если возможно), чтобы получить преобразование, которое вам нужно, от равномерного распределения [0,1]
к вашему желаемому.
Итак, вы начинаете с определения необходимого вам дистрибутива.
P = F(x)
(для 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 вместо этого...
Ответ 3
Я не могу прокомментировать математику, необходимую для создания распределения закона власти (у других сообщений есть предложения), но я предлагаю вам ознакомиться с возможностями случайных номеров стандартной библиотеки TR1 С++ в <random>
. Они обеспечивают большую функциональность, чем std::rand
и std::srand
. Новая система определяет модульный API для генераторов, движков и дистрибутивов и предоставляет набор пресетов.
Включенные пресеты распространения:
-
uniform_int
-
bernoulli_distribution
-
geometric_distribution
-
poisson_distribution
-
binomial_distribution
-
uniform_real
-
exponential_distribution
-
normal_distribution
-
gamma_distribution
Когда вы определяете распределение мощности по закону, вы должны подключить его к существующим генераторам и двигателям. В книге "Стандартные расширения библиотек С++" Пита Беккера есть замечательная глава в <random>
.
Вот статья о том, как создавать другие дистрибутивы (примеры для Коши, Chi-squared, Student t и Snedecor F)
Ответ 4
Я просто хотел провести фактическое симуляцию в качестве дополнения к (по праву) принятому ответу. Хотя в R код настолько прост, как быть (псевдо) -pseudo-code.
Одно небольшое различие между формулой 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