如何在不创建子矩阵的情况下在Matlab中对矩阵的一部分求和?

时间:2015-07-03 00:57:07

标签: matlab matrix indexing sum

所以在Matlab中,我说我有一个大小为N乘N的矩阵X,而我是一个大小为1的逻辑索引向量。然后我可以做

sum(X(i,i))

问题是它等同于首先为

分配内存
Y=X(i,i),

然后计算Y上的总和,并删除Y.我是对的吗? (Hoki的回答表明它是对的。)

在没有(隐式)创建Y的情况下,是否有更快的方法来计算总和?如果Y很大,则可以在内存操作中消耗大量时间。换句话说,是否可以执行以下操作:

S=zeros(1,nnz(i));

for k=find(i)
    for j=find(i)
        S(k)=S(k)+X(j,k);
    end
end

通过这种方式,除了X之外我们需要的所有内存都是向量S - 我们不需要为大Y分配内存。当然,循环可能很慢,但你得到了我的想法。

2 个答案:

答案 0 :(得分:3)

你对内存管理的运作方式有太多假设。

<强>定时:

我用timeit运行了一个基准测试。从N = 10到N = 20000,两种形式的执行时间绝对存在无明显差异

此外,如果我关闭JIT加速度,我仍会得到相同的结果...所以优化可能只是Matlab lazy-copy行为的结果。

内存使用情况:

在记忆方面,似乎有所不同。间接方法(使用临时变量)似乎为此临时变量分配内存(分配的大小与临时变量的大小完全对应)。另一方面,direct方法不需要任何额外的内存分配来返回结果。

这达到了我对这些事情的掌握。我不够专业,不能假装解释为什么这种内存使用差异不会导致时序差异。我知道记忆力很快,但对于N的高阶,我认为它会有所作为。显然不是......

更多信息:

有关Matlab内存管理的更多详细信息,请您在Matlab上阅读Loren的这篇文章:
Memory Management for Functions and Variables

或者如果您想阅读更深入的机制测试:
Internal Matlab memory optimizations

时间基准:

基准测试结果:

exectime

基准代码:

function ExecTimes = benchmark_sumcol

%// prepare logarithmic progression (up to what my 16GB RAM can take)
nOrder = (1:9).' * 10.^(1:3) ; nOrder = [nOrder(:) ; 10000 ; 20000] ; %'
npt = numel(nOrder) ;

ExecTimes = zeros( npt , 2 ) ;

for k = 1:npt
    %// Sample data
    N = nOrder(k) ;
    X = rand(N) ;
    ci = logical(randi([0 1],1,N)) ;

    %// Benchmark
    f1 = @() direct_sum(X,ci) ;
    f2 = @() indirect_sum(X,ci) ;
    ExecTimes(k,1) = timeit( f1 ) ;
    ExecTimes(k,2) = timeit( f2 ) ;

    clear X ci
    disp(N)
end

function R = direct_sum(X,ci)
R = sum(X(:,ci)) ;

function R = indirect_sum(X,ci)
Y = X(:,ci) ;
R = sum(Y) ;

内存基准:

  • 两项功能摘要

both

  • 间接求和的详细信息,带有临时变量。我强调了内存分配:

both

  • 直接求和的详情:

both

内存基准代码

%% // set profiler options
clear all
profile('-memory','on');
setpref('profiler','showJitLines',1);
profile on

%% // sample data
N = 1000 ;
X = rand(N) ;
ci = logical(randi([0 1],1,N)) ;

%% // Benchmark
R2 = bench_indirect_sum(X,ci) ;
R1 = bench_direct_sum(X,ci) ;

%% // result
profile viewer
p = profile('info');
profsave(p,'profile_results')

最后编辑:

我已将loop版本添加到测试中,但我必须稍微修改它以使其实际工作(并提供与其他版本相同的结果):

function R = bench_loop_sum(X,ci)
    R = zeros(1,nnz(ci));
    idxRes=1 ;
    for k=find(ci)
         for j=1:size(X,1)
             R(idxRes)=R(idxRes)+X(j,k);
         end
        idxRes = idxRes+1 ;
    end

结果在内存方面是好的(对于临时阵列没有额外的内存分配),但在速度方面是灾难性的: loopjiton

正如我们所期望的那样,循环越多,关闭JIT就越糟糕: loopjitoff

现在一个简单的改变来抑制内循环使事情变得更好,但仍然有点落后于直接方式(请注意,此版本为临时列分配内存以进行总和):

function R = bench_loop_sum(X,ci)
    R = zeros(1,nnz(ci));
    idxRes=1 ;
    for k=find(ci)
        R(idxRes) = sum(X(:,k));
        idxRes = idxRes+1 ;
    end

loopshortjiton
使用JIT。

答案 1 :(得分:1)

有两个答案,如果你一直在寻找完整的专栏,答案很简单

class Celsius < Temperature
  def initialze(n)
  # should be initialize
     super(:c => n)
  end
end

是一行,包含所有列的总和

然后

t=sum(X);

是你想要的。

如果您正在寻找奇怪的形状,线性指数可能正是您所寻找的。

请参阅 sub2ind

首先在矩阵中创建一个线性索引(一维索引),然后直接使用该索引

列i中的六个项目(5到10)的使用总和

ans=sum(t(i))