matlab中双for环矢量化求和
对于非常大的阵列(10k x 10k)或更大的阵列,我有两个for循环。显然,这个程序的部分是一个巨大的瓶颈和非常耗时的任务 有4个数组:matlab中双for环矢量化求和,matlab,vectorization,Matlab,Vectorization,对于非常大的阵列(10k x 10k)或更大的阵列,我有两个for循环。显然,这个程序的部分是一个巨大的瓶颈和非常耗时的任务 有4个数组:vm(10000,1),va(10000,1),yr(1000010000),和yi(1000010000) 在您的情况下,一次计算总和很简单。基本上,您可以创建数组,其中元素分别是vm和va的适当乘积和差异(使用bsxfun),然后是行之间的元素乘法和求和 pcal = sum(bsxfun(@times,vm,vm') .* (... yr.*co
vm(10000,1)
,va(10000,1)
,yr(1000010000)
,和yi(1000010000)
在您的情况下,一次计算总和很简单。基本上,您可以创建数组,其中元素分别是
vm
和va
的适当乘积和差异(使用bsxfun
),然后是行之间的元素乘法和求和
pcal = sum(bsxfun(@times,vm,vm') .* (...
yr.*cos(bsxfun(@minus,va,va')) + ...
yi.*sin(bsxfun(@minus,va,va'))),2);
注意,在矢量化中,您倾向于交换内存和CPU周期。如果您没有足够的RAM,可能会导致分页,这将使矢量化解决方案变慢为爬网。最快的选项是@Joans answer。但是,如果遇到内存问题,这里有一个半矢量化选项(只有一个循环): 下面是此方法的基准测试,以及您和@Joans one,以及使用
ndgrid
的另一种方法:
function sum_time
N = 10000;
vm = rand(N,1);
va = rand(N,1);
yr = rand(N);
yi = rand(N);
loop_time = timeit(@() loop(N,vm,va,yr,yi))
loop2_time = timeit(@() loop2(N,vm,va,yr,yi))
bsx_time = timeit(@() bsx(vm,va,yr,yi))
ndg_time = timeit(@() ndg(N,vm,va,yr,yi))
end
function pcal = loop(N,vm,va,yr,yi)
pcal = zeros(N,1);
for m = 1: N
psum = 0;
for n = 1: N
psum = psum + vm(m)*vm(n)*(yr(m,n)*cos(va(m)-va(n)) +...
yi(m,n)*sin(va(m)-va(n)));
end
pcal(m) = psum;
end
end
function pcal = loop2(N,vm,va,yr,yi)
pcal = zeros(N,1);
for m = 1: N
va_va = va(m)-va(1:N); % to avoid calculating twice
pcal(m) = sum(vm(m)*vm(1:N).*(yr(m,1:N).'.*cos(va_va)+yi(m,1:N).'.*sin(va_va)));
end
end
function pcal = bsx(vm,va,yr,yi)
pcal = sum(bsxfun(@times,vm,vm') .* (...
yr.*cos(bsxfun(@minus,va,va')) + ...
yi.*sin(bsxfun(@minus,va,va'))),2);
end
function pcal = ndg(N,vm,va,yr,yi)
[n,m] = ndgrid((1:N).',1:N);
yr_t = yr.';
yi_t = yi.';
va_va = va(m(:))-va(n(:));
vmt = vm(m(:)).*vm(n(:));
psum = vmt.*(yr_t(1:N^2).'.*cos(va_va)+yi_t(1:N^2).'.*sin(va_va));
pcal = sum(reshape(psum,N,N)).';
end
以及结果(对于N=10000
):
因此,使用一个循环可以节省约50%的时间。您可以根据以下内容重新计算公式: 因此,预先计算正弦和余弦,并在loop或bsxfun中使用它们。这是循环版本:
yr = rand(10000);
yi = rand(10000);
va = rand(1,10000);
vm = rand(1,10000);
sin_va = sin(va);
cos_va = cos(va);
for i = 1: 10000
pcal(i) = sum(vm(i)*vm.*(yr(i,:).*(cos_va(i) * cos_va + sin_va(i) * sin_va) + yi(i,:).*(sin_va(i) * cos_va - cos_va(i) * sin_va)));
end
试着获得一些关于矢量化和元素乘法的灵感,试着为你的问题提供一个自己的第一个解决方案,你会从中得到帮助。您可以使用模式在matlab中测量时间,我也建议不要直接使用值10000,而是使用
size
或numel
。如果明天你的输入向量有5000个元素,你会得到一个错误,但是如果向量有20000个元素,你几乎不会注意到其中有一半没有被计算。非常感谢Jonas,这段代码工作得非常好。这是我问题的答案谢谢EBH,我感谢您的详细解释
function sum_time
N = 10000;
vm = rand(N,1);
va = rand(N,1);
yr = rand(N);
yi = rand(N);
loop_time = timeit(@() loop(N,vm,va,yr,yi))
loop2_time = timeit(@() loop2(N,vm,va,yr,yi))
bsx_time = timeit(@() bsx(vm,va,yr,yi))
ndg_time = timeit(@() ndg(N,vm,va,yr,yi))
end
function pcal = loop(N,vm,va,yr,yi)
pcal = zeros(N,1);
for m = 1: N
psum = 0;
for n = 1: N
psum = psum + vm(m)*vm(n)*(yr(m,n)*cos(va(m)-va(n)) +...
yi(m,n)*sin(va(m)-va(n)));
end
pcal(m) = psum;
end
end
function pcal = loop2(N,vm,va,yr,yi)
pcal = zeros(N,1);
for m = 1: N
va_va = va(m)-va(1:N); % to avoid calculating twice
pcal(m) = sum(vm(m)*vm(1:N).*(yr(m,1:N).'.*cos(va_va)+yi(m,1:N).'.*sin(va_va)));
end
end
function pcal = bsx(vm,va,yr,yi)
pcal = sum(bsxfun(@times,vm,vm') .* (...
yr.*cos(bsxfun(@minus,va,va')) + ...
yi.*sin(bsxfun(@minus,va,va'))),2);
end
function pcal = ndg(N,vm,va,yr,yi)
[n,m] = ndgrid((1:N).',1:N);
yr_t = yr.';
yi_t = yi.';
va_va = va(m(:))-va(n(:));
vmt = vm(m(:)).*vm(n(:));
psum = vmt.*(yr_t(1:N^2).'.*cos(va_va)+yi_t(1:N^2).'.*sin(va_va));
pcal = sum(reshape(psum,N,N)).';
end
loop_time =
7.0296
loop2_time =
3.3722
bsx_time =
1.2716
ndg_time =
6.3568
sin(a-b) = sin a cos b - cos a sin b;
cos(a-b) = cos a cos b + sin a sin b;
yr = rand(10000);
yi = rand(10000);
va = rand(1,10000);
vm = rand(1,10000);
sin_va = sin(va);
cos_va = cos(va);
for i = 1: 10000
pcal(i) = sum(vm(i)*vm.*(yr(i,:).*(cos_va(i) * cos_va + sin_va(i) * sin_va) + yi(i,:).*(sin_va(i) * cos_va - cos_va(i) * sin_va)));
end