如何从向量a
中随机选取N个数字,并为每个数字指定权重?
让我们说:
a = 1:3; % possible numbers
weight = [0.3 0.1 0.2]; % corresponding weights
在这种情况下,拾取1的概率应该比拾取2的概率高3倍。
所有权重的总和可以是任何东西。
答案 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)
为获得最佳性能,如果您只需要一个单一的样品,请使用
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长度。因此访问速度非常快。