我发现MATLAB的随机数生成器似乎只能精确到单精度,即使“双倍”'用于返回类型。
我正在使用的简单测试是
平均((兰特(1,100000000,'双')))
通过在rand()上执行运行平均而得到的结果相同。平均值以单精度收敛。
我错过了什么吗?
编辑:
快速运行平均值,以总结大量样本:
% use vpa to reduce round off
runningMean = vpa(0);
samples = vpa(0);
% grab groups (significantly faster)
groupSize = 5000000;
while(true)
runningMean = (runningMean*samples+groupSize*mean(rand(1,1,groupSize)));
samples = samples + groupSize;
runningMean = runningMean / samples;
fprintf('\nN: %d \tmean: %.15f', double(samples), double(runningMean));
end
编辑:
错误与样本的关系图
答案 0 :(得分:1)
我运行了稍微修改过的代码版本,收集了所有部分结果并绘制了它们。叠加是理论RMS误差:
% use vpa to reduce round off
runningMean = vpa(0);
samples = vpa(0);
% grab groups (significantly faster)
groupSize = 5000000;
K = 1000;
results = NaN(1,K); %// initiallize
for k = 1:K
runningMean = (runningMean*samples+groupSize*mean(rand(1,1,groupSize)));
samples = samples + groupSize;
runningMean = runningMean / samples;
fprintf('\nIteration: %d \tN: %d \tmean: %.15f',...
k, double(samples), double(runningMean));
results(k) = runningMean;
end
%// plot
loglog((1:k)*groupSize, abs(results-.5))
hold on
loglog(groupSize*[1 k], 1/sqrt(12)./sqrt(groupSize*[1 k]), 'g--') %// theoretical
如您所见,获得的值(蓝色)似乎遵循理论RMS误差(绿色)给出的预期趋势。当然,比较远非完美,因为我将错误的样本值(每个N
的单个值)与 RMS进行比较错误。要做得对,你必须运行实验说1e4次;每个N
计算那些1e4错误的RMS值;并且 应该与理论RMS值更加一致。尽管如此,即使只有一次运行,这种趋势仍然可以被欣赏。
答案 1 :(得分:1)
同一事物的略微简化的实现:
sz = 5e6; % batch size
num = 1000; % number of batches
s = zeros(1,num); % sum in each batch
for i=1:num
s(i) = sum(rand(sz,1));
end
csz = sz .* (1:num); % cumulative sizes
cs = cumsum(s) ./ csz; % cumulative means
现在我们将结果与@LuisMendo讨论的理论错误一起绘制。我们再次看到理论趋势线后面的经验误差曲线:
subplot(121), semilogx(csz, cs)
line(csz([1 end]), [0.5 0.5], 'Color','m', 'LineStyle',':')
title('x = rand(N,1)'), xlabel('N'), ylabel('mean(x)')
axis tight
subplot(122), loglog(csz, abs(cs-0.5))
line(csz([1 end]), 1./sqrt(csz([1 end])*12), 'Color','m', 'LineStyle',':')
title('x = rand(N,1)'), xlabel('N'), ylabel('|mean(x)-0.5|')
axis tight
您还可以尝试使用更好summation algorithm的代码(请参阅FEX上的XSum
)。只需将循环中的sum
替换为XSum
,将最终cumsum
替换为:
cs = arrayfun(@(i) XSum(s(1:i)), 1:numel(s)) ./ csz;