提高效率Matlab是大型矩阵的成员:比逻辑索引更快的建议吗?

时间:2018-04-10 15:59:29

标签: matlab

考虑Matlab中的以下例程,在循环中多次调用ismember

clear
rng default
%%%% Parameters
XZsup=[1 2; 2 2; 3 2; 1 5; 2 5 ; 3 5];
N=100:100:2000; 

%%%% Allocate space
P1=zeros(size(XZsup,1),size(N,2));
P2=zeros(size(XZsup,1),size(N,2));
time=zeros(size(N,2),1);

for l=1:size(N,2)
    n=N(l);
    %%%% 1) Generate data
    X=unidrnd(3,n,1); %nx1
    XZmu=[unidrnd(3,n^2,1) unidrnd(5,n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1)]; %n^2x5
    %%%% 2) Using ismember, compare each row in XZsup with XZmu and X and fill P1 and P2
    tic
    for h=1:size(XZsup,1)
        num=ismember(XZmu, [XZsup(h,:) 1 1 0], 'rows'); 
        den=ismember(XZmu(:,1:2), XZsup(h,:), 'rows');  
        P1(h,l)=sum(num)/ sum(den);      

        num=ismember(XZmu, [XZsup(h,:) 0 0 1], 'rows'); 
        den=ismember(X,XZsup(h,1));
        P2(h,l)=sum(num)/sum(den); 
    end
    time(l)=toc;
end

对于N的大值,循环内的代码非常慢。的确,我得到了

    time =
    0.0373
    0.1583
    0.3651
    0.7003
    1.2631
    2.0064
    2.9665
    4.0512
    5.4177
    7.2097
    8.9078
   11.0968
   13.3023
   16.0414
   19.0199
   22.3131
   26.3962
   30.1553
   34.3927
   37.9660

我希望您能帮助我们提高效率。这可能是一个选项

clear
rng default
%%%% Parameters
XZsup=[1 2; 2 2; 3 2; 1 5; 2 5 ; 3 5];
N=100:100:2000; 

%%%% Allocate space
P3=zeros(size(XZsup,1),size(N,2));
P4=zeros(size(XZsup,1),size(N,2));
time=zeros(size(N,2),1);

for l=1:size(N,2)
    n=N(l);
    %%%% 1) Generate data
    X=unidrnd(3,n,1); %nx1
    XZmu=[unidrnd(3,n^2,1) unidrnd(5,n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1)]; %n^2x5
    %%%% 2) Using ismember, compare each row in XZsup with XZmu and X and fill P3 and P4
    tic
    for h=1:size(XZsup,1)
        num=(sum(XZmu==[XZsup(h,:) 1 1 0], 2)==5); 
        den=(sum(XZmu(:,1:2)==XZsup(h,:),2)==2);  
        P3(h,l)=sum(num)/ sum(den);    

        num=(sum(XZmu==[XZsup(h,:) 0 0 1], 2)==5); 
        den=(X==XZsup(h,1));
        P4(h,l)=sum(num)/sum(den); 
    end
    time(l)=toc;
end

我得到了

time =

    0.0051
    0.0047
    0.0087
    0.0183
    0.0277
    0.0365
    0.0491
    0.0714
    0.0878
    0.1098
    0.1426
    0.1825
    0.2176
    0.2440
    0.2830
    0.3353
    0.3783
    0.4215
    0.4732
    0.5399

您有更快的建议吗?

我已经阅读了几个与此相关的问题/答案,例如,

  • here:当我输入builtin('_ismemberoneoutput',...,...)时,Matlab告诉我Error using builtin Cannot find builtin function '_ismemberoneoutput';我也尝试使用ismembc,但它没有rows选项。

  • here但它似乎不适合我的情况

1 个答案:

答案 0 :(得分:1)

ismember很贵。 因此,我们可以尽可能少地使用它来节省时间。

我做了一个函数来比较OP的方法和我的方法。 由于我已经从your previous question知道纯粹存储大型矩阵也很慢,这次我试图避免它。

function [P1,P2] = testf(X,XZmu,XZsup, K)

P1=zeros(size(XZsup,1),1);
P2=zeros(size(XZsup,1),1);
switch K
    case 1
        for h=1:size(XZsup,1)
            num=ismember(XZmu, [XZsup(h,:) 1 1 0], 'rows'); 
            den=ismember(XZmu(:,1:2), XZsup(h,:), 'rows');  
            P1(h)=sum(num)/ sum(den);

            num=ismember(XZmu, [XZsup(h,:) 0 0 1], 'rows'); 
            den=ismember(X,XZsup(h,1));
            P2(h)=sum(num)/sum(den); 
        end
    case 2
        for h=1:size(XZsup,1)
            den = ismember(XZmu(:,1:2), XZsup(h,:), 'rows');
            tempNum = (XZmu(:,3)==1) & (XZmu(:,4)==1) & (XZmu(:,5)==0);
            num = tempNum & den;
            P1(h) = sum(num)/sum(den);

            tempNum2 = (XZmu(:,3)==0) & (XZmu(:,4)==0) & (XZmu(:,5)==1);
            num2 = tempNum2 & den;
            den2 = X==XZsup(h,1);
            P2(h) = sum(num2)/sum(den2);
        end
end
end

测试代码: 我再次更喜欢多次使用timeit

rng default
XZsup=[1 2; 2 2; 3 2; 1 5; 2 5 ; 3 5];
N=[10:20:100-1, 100:200:1000-1, 1000:1000:2000];
Ntrial = 5; % even timeit has variation, so make multiple trials
time=zeros(size(N,2),2);
tempT(Ntrial,2) = 0;

for l=1:size(N,2)
    n=N(l);
    %%%% 1) Generate data
    X=unidrnd(3,n,1); %nx1
    XZmu=[unidrnd(3,n^2,1) unidrnd(5,n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1) randi([0 1],n^2,1)]; %n^2x5
    %%%% 2) Using ismember, compare each row in XZsup with XZmu and X and fill P1 and P2
    for nn = 1:Ntrial
        tempT(nn,:) = [timeit(@() testf(X, XZmu,XZsup, 1), 2), ....
            timeit(@() testf(X, XZmu,XZsup, 2), 2)];
    end
    time(l,:)=mean(tempT,1);
end
plot(N, time(:,1), '--', N, time(:,2), '-')

结果:时间比较图

(我打算跑到N=3000,但速度太慢......最后看到我的笔记)

time comparison

在Matlab R2015b,Core i7上运行。

旁注:您的代码非常适合parfor

我将for循环更改为XZsupparfor,这是结果,与案例2相比。

time comparison with parfor

在8-vCPU系统上运行Matlab R2018a。

对于像300x300这样的大型矩阵,parfor优于for。 对于较小的矩阵,我的猜测是需要更多时间与工人沟通。

您的里程可能会有所不同。

附注2:此方法对数据有更多限制,令人惊讶的是它不能更快​​地工作....

    case 3
        tempNumCol1 = XZmu(:,3)==1;
        tempNumCol2 = XZmu(:,4)==1;
        tempNumCol3 = XZmu(:,5)==1;
        tempNum = (tempNumCol1) & (tempNumCol2) & (~tempNumCol3);
        tempNum2 = (~tempNumCol1) & (~tempNumCol2) & (tempNumCol3);
        for h=1:size(XZsup,1)
            den = ismember(XZmu(:,1:2), XZsup(h,:), 'rows');
            num = tempNum & den;
            P1(h) = sum(num)/sum(den);

            num2 = tempNum2 & den;
            den2 = X==XZsup(h,1);
            P2(h) = sum(num2)/sum(den2);
        end

我的两种方法之间的比较:

time comparison 2

编辑:为什么不完全摆脱ismember ......

    case 3
        tempNum = (XZmu(:,3)==1) & (XZmu(:,4)==1) & (XZmu(:,5)==0);
        tempNum2 = (XZmu(:,3)==0) & (XZmu(:,4)==0) & (XZmu(:,5)==1);
        for h=1:size(XZsup,1)
            tempDen1 = XZmu(:,1)==XZsup(h,1);
            tempDen2 = XZmu(:,2)==XZsup(h,2);
            den = tempDen1&tempDen2;

            num = tempNum & den;
            P1(h) = sum(num)/sum(den);

            num2 = tempNum2 & den;
            den2 = X==XZsup(h,1);
            P2(h) = sum(num2)/sum(den2);
        end

time comparison with no ismember

它已经比在我的机器上生成随机数字更快了。

profiler