Равномерная выборка случайно из n-мерного единичного симплекса
Равномерная выборка случайным образом из n-мерного симплекса - это причудливый способ сказать, что вы хотите n случайных чисел, таких, чтобы
- все они неотрицательны,
- они складываются в один, и
- каждый возможный вектор из n неотрицательных чисел, суммирующих одно, одинаково вероятен.
В случае n=2 вы хотите сделать выборку равномерно из сегмента линии x+y=1 (т.е. y=1-x), который находится в положительном квадранте. В случае n=3 вы производите выборку из треугольной части плоскости x+y+z=1, которая находится в положительном октанте R3:
(Изображение с http://en.wikipedia.org/wiki/Simplex.)
Обратите внимание, что выборка n равномерных случайных чисел, а затем их нормализация, чтобы они суммировались в одно, не работает. Вы в конечном итоге смещены в сторону менее экстремальных чисел.
Точно так же, выбор n-1 равномерных случайных чисел и затем принятие n-го равным одному минус сумма их также вводит смещение.
Википедия предлагает два алгоритма, чтобы сделать это правильно: http://en.wikipedia.org/wiki/Simplex(Хотя второй в настоящее время утверждает, что он верен только на практике, а не в теории. Я надеюсь это исправить или проясните это, когда я пойму это лучше. Сначала я застрял в "ПРЕДУПРЕЖДЕНИИ: такая-то и такая-то статья утверждает, что следующее неверно" на этой странице Википедии, и кто-то другой превратил это в предупреждение "работает только на практике".)
Наконец, вопрос: что вы считаете лучшей реализацией симплексной выборки в Mathematica (желательно с эмпирическим подтверждением того, что это правильно)?
Смежные вопросы
6 ответов
Этот код может работать:
samples[n_] := Differences[Join[{0}, Sort[RandomReal[Range[0, 1], n - 1]], {1}]]
В основном вы просто выбираете n - 1
места на интервале [0,1]
разделить его, а затем взять размер каждой из частей, используя Differences
,
Быстрый пробег Timing
на этом видно, что это немного быстрее, чем первый ответ Януса.
Немного покопавшись, я нашел эту страницу, которая дает отличную реализацию Dirichlet Distribution. Оттуда кажется, что было бы довольно просто следовать методу Википедии 1. Это кажется лучшим способом сделать это.
В качестве теста:
In[14]:= RandomReal[DirichletDistribution[{1,1}],WorkingPrecision->25]
Out[14]= {0.8428995243540368880268079,0.1571004756459631119731921}
In[15]:= Total[%]
Out[15]= 1.000000000000000000000000
Сюжет из 100 образцов:
Я с zdav: кажется, что дистрибутив Dirichlet - самый простой путь, и алгоритм выборки дистрибутива Dirichlet, на который ссылается zdav, также представлен на странице Википедии в дистрибутиве Dirichlet.
Внедрение полной реализации дистрибутива Dirichlet требует больших усилий, так как все, что вам действительно нужно, это n
случайный Gamma[1,1]
образцы. Сравните ниже
Простая реализация
SimplexSample[n_, opts:OptionsPattern[RandomReal]] :=
(#/Total[#])& @ RandomReal[GammaDistribution[1,1],n,opts]
Полная реализация Дирихле
DirichletDistribution/:Random`DistributionVector[
DirichletDistribution[alpha_?(VectorQ[#,Positive]&)],n_Integer,prec_?Positive]:=
Block[{gammas}, gammas =
Map[RandomReal[GammaDistribution[#,1],n,WorkingPrecision->prec]&,alpha];
Transpose[gammas]/Total[gammas]]
SimplexSample2[n_, opts:OptionsPattern[RandomReal]] :=
(#/Total[#])& @ RandomReal[DirichletDistribution[ConstantArray[1,{n}]],opts]
тайминг
Timing[Table[SimplexSample[10,WorkingPrecision-> 20],{10000}];]
Timing[Table[SimplexSample2[10,WorkingPrecision-> 20],{10000}];]
Out[159]= {1.30249,Null}
Out[160]= {3.52216,Null}
Таким образом, полный Дирихле в 3 раза медленнее. Если вам нужно m>1 выборочных точек за раз, вы можете выиграть, выполнив (#/Total[#]&)/@RandomReal[GammaDistribution[1,1],{m,n}]
,
Вот хорошая краткая реализация второго алгоритма из Википедии:
SimplexSample[n_] := Rest@# - Most@# &[Sort@Join[{0,1}, RandomReal[{0,1}, n-1]]]
Это адаптировано отсюда: http://www.mofeel.net/1164-comp-soft-sys-math-mathematica/14968.aspx(Первоначально он имел Союз вместо Sort@Join - последний немного быстрее.)
(Смотрите комментарии для некоторых доказательств того, что это правильно!)
Я создал алгоритм равномерной генерации случайных чисел по симплексу. Подробности можно найти в следующей ссылке: http://www.tandfonline.com/doi/abs/10.1080/03610918.2010.551012
Вкратце, вы можете использовать следующие формулы рекурсии, чтобы найти случайные точки над n-мерным симплексом:
x1= 1-R11 / n-1
xk= (1-Σi = 1kxi) (1-Rk1 / nk), k = 2,..., n-1
xn= 1-Σi = 1n-1xi
Где R_i - это случайные числа от 0 до 1.
Сейчас я пытаюсь создать алгоритм для генерации случайных равномерных выборок из ограниченного симплекса. Это пересечение между симплексом и выпуклым телом.
Старый вопрос, и я опаздываю на вечеринку, но этот метод намного быстрее, чем принятый ответ, если он реализован эффективно.
В коде системы Mathematica:# / Total[#,{2}]&@ [email protected] [{0,1},{n,d}]
Говоря простым языком, вы генерируете n строк * d столбцов случайных чисел, равномерно распределенных между 0 и 1. Затем возьмите Журнал всего. Затем нормализуйте каждую строку, разделив каждый элемент в строке на общее количество строк. Теперь у вас есть n образцов, равномерно распределенных по симплексу (d-1).
Если вы нашли этот метод здесь: https://mathematica.stackexchange.com/questions/33652/uniformly-distributed-n-dimensional-probability-vectors-over-a-simplex
Признаюсь, я не уверен, почему это работает, но он проходит все статистические тесты, которые я могу придумать. Если у кого-то есть доказательство того, почему этот метод работает, я бы с удовольствием его увидел!