【问题标题】:A faster alternative to all(a(:,i)==a,1) in MATLABMATLAB 中 all(a(:,i)==a,1) 的更快替代方案
【发布时间】:2021-01-01 18:04:49
【问题描述】:

这是一个直截了当的问题:在 MATLAB 中是否有比 all(a(:,i)==a,1) 更快的替代方法?

我正在考虑一种在整个过程中受益于短路评估的实现。我的意思是,all() 肯定会从短路评估中受益,但 a(:,i)==a 不会。

我尝试了以下代码,

% example for the input matrix

m = 3;       % m and n aren't necessarily equal to those values.
n = 5000;    % It's only possible to know in advance that 'm' << 'n'.

a = randi([0,5],m,n); % the maximum value of 'a' isn't necessarily equal to 
                      % 5 but it's possible to state that every element in 
                      % 'a' is a positive integer.

% all, equal solution

tic
for i = 1:n % stepping up the elapsed time in orders of magnitude
    %%%%%%%%%% all and equal solution %%%%%%%%%
    ax_boo = all(a(:,i)==a,1);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end
toc

% alternative solution

tic
for i = 1:n % stepping up the elapsed time in orders of magnitude
    %%%%%%%%%%% alternative solution %%%%%%%%%%%
    ax_boo = a(1,i) == a(1,:);
    for k = 2:m
        ax_boo(ax_boo) = a(k,i) == a(k,ax_boo);
    end
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end
toc

但很明显,MATLAB 环境中的任何“for-loop-solution”都会自然而然地变慢。我想知道是否有用更快的语言编写的 MATLAB 内置函数。

编辑:

在运行更多测试后,我发现隐式扩展在评估a(:,i)==a 时确实会对性能产生影响。如果矩阵a 有多于一行,则all(repmat(a(:,i),[1,n])==a,1) 可能比all(a(:,i)==a,1) 快,具体取决于列数 (n)。对于n=5000,repmat 显式扩展已被证明更快。

但我认为,如果a 的所有元素都是正整数,那么 Kenneth Boyd 的答案的概括就是“终极解决方案”。我不会以原始形式处理am x n矩阵),而是存储和处理adec1 x n矩阵):

exps = ((0):(m-1)).';
base = max(a,[],[1,2]) + 1;
adec = sum( a .* base.^exps , 1 );

换句话说,每一列将被编码为一个整数。当然adec(i)==adecall(a(:,i)==a,1) 快。

编辑 2:

我忘了提到adec 方法有一个功能限制。充其量,将adec 存储为uint64,以下不等式必须满足base^m &lt; 2^64 + 1

【问题讨论】:

  • 这应该评估什么?您是否正在寻找与列 i 相同的列?
  • 如果是这样的话,就相当于只评估它们是否都与第 1 列相同。无需循环它们。
  • 在这种情况下,每列中的元素数量非常少,这意味着只有 8 个可能的唯一列。我会找出哪些列等于 8 种可能性中的每一种,而不是使用 b=([4 2 1]*a==[0:7].').' 之类的东西来匹配 5 列中的每一种,这在 Octave 中似乎要快 3 或 4 个数量级。但是,当然,更改输入大小可能会对速度差异产生巨大影响。
  • 这正是我选择留下评论而不是答案的原因。很明显,您没有向我们提供提供相关答案所需的详细信息。您评论中的所有信息都应包含在问题本身中。
  • 嗯@Zalnd,那个“终极解决方案”看起来很眼熟……但我以为你不想要不同的方法?

标签: performance matlab boolean comparison-operators


【解决方案1】:

由于您的目标是计算匹配的列数,因此我的示例将二进制编码转换为整数小数,然后您只需遍历可能的值(3 行是 8 个可能的值)并计算匹配数.

a_dec = 2.^(0:(m-1)) * a;
num_poss_values = 2 ^ m;
num_matches = zeros(num_poss_values, 1);
for i = 1:num_poss_values
   num_matches(i) = sum(a_dec == (i - 1));
end

在我的电脑上,使用 2020a,以下是前 2 个选项和上面代码的执行时间:

Elapsed time is 0.246623 seconds.
Elapsed time is 0.553173 seconds.
Elapsed time is 0.000289 seconds.

所以我的代码快了 853 倍!

我编写了我的代码,因此它可以与 m 作为任意整数一起使用。

num_matches 变量包含转换为小数时加起来为 0、1、2、...7 的列数。

【讨论】:

    【解决方案2】:

    您也可以使用unique 的第三个输出:

    [~, ~, iu] = unique(a.', 'rows');
    
    for i = 1:n
      ax_boo = iu(i) == iu;
    end
    

    如评论中所示:

    ax_boo 隔离我必须在行向量b 中求和的列的索引。所以,基本上下一行就像c = sum(b(ax_boo),2);

    这是accumarray的典型用法:

    [~, ~, iu] = unique(a.', 'rows');
    C = accumarray(iu,b);
    for i = 1:n
      c = C(i);
    end
    

    【讨论】:

    • 正如我之前所说,i-loop 仅用于基准测试目的。它的存在只是为了将经过的时间增加几个数量级。事实上,我确实使用unique 来处理我知道我必须检查每一列而不仅仅是少数列的情况。但是对于原始问题,我不需要检查所有列。我很确定在这种情况下调用 unique 会更慢,但我会在这里检查。最后,关于accumarray,它是我原来方法的有效替代方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 2014-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多