我有两个for循环用于非常大的数组(10k x 10k)或更多。显然,这个程序的部分是一个巨大的瓶颈和非常耗时的任务。
有4个阵列:vm(10000,1)
,va(10000,1)
,yr(10000,10000)
和yi(10000,10000)
for i = 1: 10000
psum = 0;
for j = 1: 10000
psum = psum + vm(i)*vm(j)*(yr(i,j)*cos(va(i)-va(j)) + yi(i,j)*sin(va(i)-va(j)));
end
pcal(i) = psum;
end
答案 0 :(得分:1)
在您的情况下,一次性计算总和是很简单的。基本上,您创建数组,其中元素分别是vm
和va
的适当产品和差异(使用bsxfun
),然后是逐行的元素乘法和求和。< / p>
pcal = sum(bsxfun(@times,vm,vm') .* (...
yr.*cos(bsxfun(@minus,va,va')) + ...
yi.*sin(bsxfun(@minus,va,va'))),2);
请注意,在vecorization中,您倾向于交换内存与CPU周期。如果你没有足够的内存,你可能最终会进行分页,这会使矢量化解决方案变慢。
答案 1 :(得分:0)
最快的选择是@Joans回答。但是,如果遇到内存问题,这里有一个半向量化选项(只有一个循环):
pcal = zeros(N,1); % N is the 10000 in your example
for m = 1: N
va_va = va(m)-va(1:N);
pcal(m) = sum(vm(m)*vm(1:N).*(yr(m,1:N).'.*cos(va_va)+yi(m,1:N).'.*sin(va_va)));
end
以下是此方法与您和@Joans的基准,以及另一种使用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
):
loop_time =
7.0296
loop2_time =
3.3722
bsx_time =
1.2716
ndg_time =
6.3568
这样可以节省一个循环,节省约50%的时间。
答案 2 :(得分:0)
你可以根据trigonometric identities:
来重新表达你的等式sin(a-b) = sin a cos b - cos a sin b;
cos(a-b) = cos a cos b + sin a sin b;
所以预先计算正弦和余弦,并在循环或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