MATLAB中的加权随机数

时间:2010-06-04 20:18:03

标签: matlab random

如何从向量a中随机选取N个数字,并为每个数字指定权重?

让我们说:

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

在这种情况下,拾取1的概率应该比拾取2的概率高3倍。

所有权重的总和可以是任何东西。

4 个答案:

答案 0 :(得分:38)

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

randsample包含在统计工具箱


否则您可以使用某种roulette-wheel selection进程。请参阅此similar question(尽管不是特定于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获取和长度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]

或多或少匹配

答案 1 :(得分:16)

amro给出了一个很好的答案(我对其进行了评分),但如果您希望从大型集合中生成许多数字,那么它将非常密集。这是因为bsxfun操作可以生成一个巨大的数组,然后将其求和。例如,假设我有一组10000个值来取样,所有都有不同的权重?现在,从该样本生成1000000个数字。

这需要做一些工作,因为它将在内部生成一个10000x1000000数组,其中包含10 ^ 10个元素。它将是一个逻辑数组,但即使这样,也必须分配10 GB的ram。

更好的解决方案是使用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的第一个参数。这会导致第一个参数立即转储到位桶中。

答案 2 :(得分:2)

TL; DR

为获得最佳性能,如果您只需要一个单一的样品,请使用

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

如果您需要多个样品,请使用

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

避免使用randsample。预先生成多个样本比生成单个值快三个数量级。


效果指标

由于这显示在我的谷歌搜索的顶部附近,我只是想添加一些性能指标,以表明正确的解决方案将在很大程度上取决于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中生成的值少了三个数量级在类似的执行时间内的情况)。

代码可用here

答案 3 :(得分:2)

Amro对这个话题有一个非常好的答案。但是,人们可能希望超快速实现从大型PDF中进行采样,其中域可能包含数千个。对于这种情况,非常频繁地使用bsxfun和cumsum可能会很繁琐。受Gnovice's answer的启发,使用行程编码模式实现轮盘赌算法是有意义的。我使用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长度。因此访问速度非常快。