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 案例中生成的值要少三个数量级类似的执行时间)。
代码可用here。