Взвешенные случайные числа в MATLAB - PullRequest
17 голосов
/ 05 июня 2010

Как случайным образом подобрать N чисел из вектора a с весом, назначенным для каждого числа?

Скажем:

a = 1:3; % possible numbers
weight = [0.3 0.1 0.2]; % corresponding weights

В этом случае вероятность подобрать 1 должнабыть в 3 раза выше, чем забрать 2.

Сумма всех весов может быть любой.

Ответы [ 4 ]

38 голосов
/ 05 июня 2010
R = randsample([1 2 3], N, true, [0.3 0.1 0.2])

randsample включено в Панель инструментов статистики


В противном случае вы можете использовать какой-либо процесс выбора колеса рулетки .См. аналогичный вопрос (хотя и не относится к MATLAB).Вот моя однострочная реализация:

a = 1:3;             %# possible numbers
w = [0.3 0.1 0.2];   %# corresponding weights
N = 10;              %# how many numbers to generate

R = a( sum( bsxfun(@ge, rand(N,1), cumsum(w./sum(w))), 2) + 1 )

Объяснение:

Рассмотрим интервал [0,1].Мы присваиваем каждому элементу в списке (1:3) подинтервал длины, пропорциональный весу каждого элемента;следовательно, 1 get и интервал длины 0.3/(0.3+0.1+0.2), то же самое для остальных.

Теперь, если мы сгенерируем случайное число с равномерным распределением по [0,1], то любое число в [0,1]имеет равную вероятность быть выбранным, таким образом, длины подинтервалов определяют вероятность того, что случайное число попадет в каждый интервал.

Это соответствует тому, что я делаю выше: выберите число X ~ U [0, 1] (больше похоже на N числа), затем найдите, в какой интервал он попадает в векторизованном виде.


Вы можете проверить результаты двух вышеописанных методов, сгенерировав достаточно большую последовательностьN=1000:

>> tabulate( R )
  Value    Count   Percent
      1      511     51.10%
      2      160     16.00%
      3      329     32.90%

, которые более или менее соответствуют нормализованным весам w./sum(w) [0.5 0.16667 0.33333]

16 голосов
/ 05 июня 2010

amro дает хороший ответ (который я оценил), но он будет очень интенсивным, если вы захотите сгенерировать много чисел из большого набора. Это потому, что операция bsxfun может генерировать огромный массив, который затем суммируется. Например, предположим, у меня был набор из 10000 значений для выборки, все с разными весами? Теперь сгенерируйте 1000000 номеров из этого образца.

Это займет определенную работу, поскольку он сгенерирует массив 10000x1000000 внутри, с 10 ^ 10 элементами в нем. Это будет логический массив, но даже в этом случае должно быть выделено 10 гигабайт оперативной памяти.

Лучшее решение - это использовать hisc. Таким образом ...

a = 1:3
w = [.3 .1 .2];
N = 10;

[~,R] = histc(rand(1,N),cumsum([0;w(:)./sum(w)]));
R = a(R)
R =
     1     1     1     2     2     1     3     1     1     1

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

a = 1:10000;
w = rand(1,10000);
N = 1000000;

tic
[~,R] = histc(rand(1,N),cumsum([0;w(:)./sum(w)]));
R = a(R);
toc
Elapsed time is 0.120879 seconds.

Правда, для написания моей версии требуется 2 строки. Операция индексирования должна происходить во второй строке, так как она использует второй вывод команды Histc. Также обратите внимание, что я использовал возможность нового релиза Matlab, с оператором тильды (~) в качестве первого аргумента для Histc. Это приводит к тому, что первый аргумент немедленно сбрасывается в область битов.

2 голосов
/ 21 октября 2014

У Амро действительно хороший ответ на эту тему. Однако может потребоваться сверхбыстрая реализация для выборки из огромных PDF-файлов, где домен может содержать несколько тысяч. Для таких сценариев может быть утомительно использовать bsxfun и cumsum очень часто. Основанный на ответе Gnovice , имело бы смысл реализовать алгоритм колеса рулетки со схемой кодирования длин серий. Я выполнил тест с помощью решения Amro и нового кода:

%% Toy example: generate random numbers from an arbitrary PDF

a = 1:3;                                %# domain of PDF
w = [0.3 0.1 0.2];                      %# Probability Values (Weights)
N = 10000;                              %# Number of random generations

%Generate using roulette wheel + run length encoding
factor = 1 / min(w);                    %Compute min factor to assign 1 bin to min(PDF)
intW = int32(w * factor);               %Get replicator indexes for run length encoding
idxArr = zeros(1,sum(intW));            %Create index access array
idxArr([1 cumsum(intW(1:end-1))+1]) = 1;%Tag sample change indexes
sampTable = a(cumsum(idxArr));          %Create lookup table filled with samples
len = size(sampTable,2);

tic;
R = sampTable( uint32(randi([1 len],N,1)) );
toc;
tabulate(R);

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

a ~ 15000, n = 10000
Without table: Elapsed time is 0.006203 seconds.
With table:    Elapsed time is 0.003308 seconds.
ByteSize(sampTable) 796.23 kb

a ~ 15000, n = 100000
Without table: Elapsed time is 0.003510 seconds.
With table:    Elapsed time is 0.002823 seconds.

a ~ 35000, n = 10000
Without table: Elapsed time is 0.226990 seconds.
With table:    Elapsed time is 0.001328 seconds.
ByteSize(sampTable) 2.79 Mb

a ~ 35000  n = 100000
Without table: Elapsed time is 2.784713 seconds.
With table:    Elapsed time is 0.003452 seconds.

a ~ 35000  n = 1000000
Without table: bsxfun: out of memory
With table   : Elapsed time is 0.021093 seconds.

Идея состоит в том, чтобы создать таблицу кодирования длин серий, в которой частые значения PDF тиражируются больше по сравнению с нечастыми значениями. В конце дня мы выбираем индекс для таблицы взвешенной выборки, используя равномерное распределение, и используем соответствующее значение.

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

2 голосов
/ 07 августа 2014

TL; DR

Для максимальной производительности, если вам нужен только один образец, используйте

R = a( sum( (rand(1) >= cumsum(w./sum(w)))) + 1 );

и если вам нужно несколько образцов, используйте

[~, R] = histc(rand(N,1),cumsum([0;w(:)./sum(w)]));

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


Показатели производительности

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

Для больших N, или даже N > 1:

a = 1:3;             % possible numbers
w = [0.3 0.1 0.2];   % corresponding weights
N = 100000000;       % number of values to generate

w_normalized = w / sum(w)  % normalised weights, for indication

fprintf('randsample:\n');
tic
R = randsample(a, N, true, w);
toc
tabulate(R)

fprintf('bsxfun:\n');
tic
R = a( sum( bsxfun(@ge, rand(N,1), cumsum(w./sum(w))), 2) + 1 );
toc
tabulate(R)

fprintf('histc:\n');
tic
[~, R] = histc(rand(N,1),cumsum([0;w(:)./sum(w)]));
toc
tabulate(R)

Результаты:

w_normalized =

    0.5000    0.1667    0.3333

randsample:
Elapsed time is 2.976893 seconds.
  Value    Count   Percent
      1    49997864     50.00%
      2    16670394     16.67%
      3    33331742     33.33%
bsxfun:
Elapsed time is 2.712315 seconds.
  Value    Count   Percent
      1    49996820     50.00%
      2    16665005     16.67%
      3    33338175     33.34%
histc:
Elapsed time is 2.078809 seconds.
  Value    Count   Percent
      1    50004044     50.00%
      2    16665508     16.67%
      3    33330448     33.33%

В этом случае histc является самым быстрым

Однако в случае, когда, возможно, невозможно сгенерировать все N значений заранее, возможно, потому что веса обновляются на каждой итерации, т.е. N=1:

a = 1:3;             % possible numbers
w = [0.3 0.1 0.2];   % corresponding weights
I = 100000;          % number of values to generate

w_normalized = w / sum(w)  % normalised weights, for indication

R=zeros(N,1);

fprintf('randsample:\n');
tic
for i=1:I
    R(i) = randsample(a, 1, true, w);
end
toc
tabulate(R)

fprintf('cumsum:\n');
tic
for i=1:I
    R(i) = a( sum( (rand(1) >= cumsum(w./sum(w)))) + 1 );
end
toc
tabulate(R)

fprintf('histc:\n');
tic
for i=1:I
    [~, R(i)] = histc(rand(1),cumsum([0;w(:)./sum(w)]));
end
toc
tabulate(R)

Результаты:

    0.5000    0.1667    0.3333

randsample:
Elapsed time is 3.526473 seconds.
  Value    Count   Percent
      1    50437     50.44%
      2    16149     16.15%
      3    33414     33.41%
cumsum:
Elapsed time is 0.473207 seconds.
  Value    Count   Percent
      1    50018     50.02%
      2    16748     16.75%
      3    33234     33.23%
histc:
Elapsed time is 1.046981 seconds.
  Value    Count   Percent
      1    50134     50.13%
      2    16684     16.68%
      3    33182     33.18%

В этом случае пользовательский подход cumsum (основанный на версии bsxfun) является самым быстрым.

В любом случае randsample определенно выглядит как плохой выбор со всех сторон. Это также показывает, что если алгоритм может быть организован для генерации всех случайных переменных заранее, то он будет работать на намного лучше (обратите внимание, что в случае N=1 в случае *1042* генерируются значения на три порядка меньше). аналогичное время выполнения).

Код доступен здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...