Взвешенные случайные числа в MATLAB

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

допустим:

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

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

сумма всех Весов может быть что угодно.

4 ответов


R = randsample([1 2 3], N, true, [0.3 0.1 0.2])

randsample входит в набор инструментов статистики


в противном случае вы можете использовать какой-то выбор колеса рулетки


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

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

лучшим решением является использование histc. Таким образом...

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. Это приводит к тому, что первый аргумент немедленно сбрасывается в битное ведро.


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 конечно, выглядит как плохой выбор во всем. Это также показывает, что если алгоритм может быть организован для генерации всех случайных величин заранее, то он будет выполнять много лучше (обратите внимание ,что есть три порядка величины меньше значений, генерируемых в the N=1 case в аналогичное время выполнения).

код здесь.


У Amro есть действительно хороший ответ на эту тему. Однако может потребоваться супер-быстрая реализация для выборки из огромных 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 сто тысяч. Следовательно, доступ супер-быстрый.