【问题标题】:How to compare the values of an one-dimensional matrix with each other?如何比较一维矩阵的值?
【发布时间】:2019-07-31 11:37:01
【问题描述】:

在索引从 1 到 m 的数组 (a) 中,我想将这个数组的值一一比较,如果两个值之间的距离 (Difference) 大于一个值 (z) ,例如索引 i 和 j 处的 a(i) 和 a(j) 之间的差异大于 z,我想保存这两个索引 i 和 j 并在输出中表示它们。我写了这些代码:

if abs(a(i)-a(j))> z
   disp(i);
   disp(j);
   fprintf('result is between %10.6f and %10.6f',i,j);
end

但是 if 行有错误:

Subscript indices must either be real positive integers or logicals.

如何为 matlab 定义索引。 传递数组是否需要for循环(for i = 1:m),如果需要循环,我是否应该将fprintf排除在循环之外,因为它会重复。 为了在输出中保存和表示索引 i 和 j,我正在寻找除 disp 或 fprintf 之外的更好的函数。

【问题讨论】:

  • 因为您将 ij 定义为非正数或非整数。但是你还没有发minimal reproducible example,所以我们无能为力
  • ij 是什么?它们应该是整数,否则你不能用它们来索引a。我建议使用其他一些变量名,因为 ij 也是复数。
  • 内存效率不高,但非常紧凑:[i,j] = find(abs(a-a.')>z),适用于小数组。

标签: matlab indexing implicit-expansion


【解决方案1】:

(以a 开头为 1×m 或 m×1)

解决方案 1

(归功于@Cris Luengo)

abs(a-a.')>z

outer product 类似行为需要 Matlab 2016b 或更高版本。

解决方案 2

使用bsxfun

abs(bsxfun(@minus,a,a.'))>z

解决方案 3

使用repmat

A=repmat(a,fliplr(size(a)));
abs(A-A.')>z

要了解如何处理结果,请参阅 logical arrays 上的数学工作文档。

从技术上讲,您只需要triltriu 的结果。


关于速度

循环不在我的考虑范围之内。我在很多场合都看到它很慢。尽管循环在技术上节省了一半的计算量,但它也带来了其他开销。所以我仍然希望矢量化方法更快,我并不认为需要测试循环。即使是内存分配成本高的repmat,也应该更快。

R2018a 中的时序数据见下文。如果您想进行更多测试,您可能希望使用更准确的计时器,例如 this one written in c。为了全面披露,当我测试时,除了 Matlab 之外几乎没有 cpu 需求。我确实尝试过使用和不使用“热身”轮次——特别是因为我确实为我的 cpu 上的某些内核定义了提升行为——但“热身”实际上使每 1000 或 10000 次运行中的数字更糟。也许我的内存跟不上repmat

基本上,新语法(解决方案 1)的速度非常快。如果您还没有升级,是时候升级了。 bsxfun 是您升级前的最佳选择。 repmat 的内存分配成本太差了。最好避开repmat

%%%%%%% 1000 x 1000
>> clear all
>> a = randi([-10, 10], [1, 1000]); z=5; N=1000;
>> T=zeros(N,1);tic; for i=1:N; abs(a-a.')>z;T(i)=toc;end; mean(T),
ans =
    0.2680

>> clear all
>> a = randi([-10, 10], [1, 1000]); z=5; N=1000;
>> T=zeros(N,1);tic; for i=1:N; abs(bsxfun(@minus,a,a.'))>z;T(i)=toc;end; mean(T),
ans =
    1.3556

>> clear all
>> a = randi([-10, 10], [1, 1000]); z=5; N=1000;
>> T=zeros(N,1);tic; for i=1:N; A=repmat(a,fliplr(size(a))); temp=abs(A-A.')>z;T(i)=toc;end; mean(T),
ans =
    4.0573

%%%%%%% 100 x 100

>> clear all
>> a = randi([-10, 10], [1, 100]); z=5; N=10000;
>> T=zeros(N,1);tic; for i=1:N; abs(a-a.')>z;T(i)=toc;end; mean(T),
ans =
    0.0610

>> clear all
>> a = randi([-10, 10], [1, 100]); z=5; N=10000;
>> T=zeros(N,1);tic; for i=1:N; abs(bsxfun(@minus,a,a.'))>z;T(i)=toc;end; mean(T),
ans =
    0.1537

>> clear all
>> a = randi([-10, 10], [1, 100]); z=5; N=10000;
>> T=zeros(N,1);tic; for i=1:N; A=repmat(a,fliplr(size(a))); temp=abs(A-A.')>z;T(i)=toc;end; mean(T),
ans =
    0.1815

% Note: assigning the output to a variable or not does not change the result. At least tic toc is not able to detect it.

【讨论】:

  • 对于 MATLAB >= 2016b,您可以跳过 repmat。对于旧版本,bsxfunrepmat 更有效。此外,' 是复杂的转置。使用.' 转置矩阵。
  • @CrisLuengo:回复:'.',感谢您指出。对于bsxfun 与逻辑索引,我不相信。见this question
  • 我不确定这个问题有什么关系。对于内置算术运算,bsxfun 始终比 repmat 快。特别是对于不适合缓存的较大矩阵。新的隐式单例扩展更快。
  • bsxfun 对于大数据大小可能更快,但不一定是像 100s x 100s 这样的小数据。是的,该问题本身及其答案中链接的问题都没有完全使用repmat。但是这两种情况都涉及复制矩阵。在这两种情况下,bsxfun 都比较慢。
  • 不错!感谢您发布! (顺便说一句:试试timeit!)
【解决方案2】:

使用2个嵌套for循环

% Given array
a = randi([-10, 10], [1, 10]);

% Array length
m = length(a);

% Reference Distance 
z = 14;

% To save indices 
result = [];

for i = 1:m
    for j = 1:m
        % Don't compare same elements 
        if i == j
        else
            % Don't use absolute value(abs), it helps discard duplicates 
            if a(i)-a(j) > z
                % Save the indices 
                result = [result; [i, j]];
            end


        end
    end


end

disp('result is between:')
disp('     i and j')
disp(result)    

结果

a = [6    -2    -5    -2    -8    -8     9    10     2    -9]

result is between:
     i and j
     1    10
     7     5
     7     6
     7    10
     8     3
     8     5
     8     6
     8    10

【讨论】:

  • 您在比较中缺少abs。此外,内部循环可以是j=i+1:m,以避免重复。
  • @CrisLuengo 我故意遗漏了abs,因为a(i)-a(j)a(j)-a(i) 都将被执行。我们只是在寻找索引,顺序无关紧要。同意使用j = i + 1:m 以避免重复。
  • 但是如果a 很复杂呢?
  • 我从来没有听说过比较两个复数。我假设a 不是。并且没有必要使用abs,这是一个最终会花费时间进行评估的函数。
猜你喜欢
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-28
  • 1970-01-01
  • 2019-10-08
相关资源
最近更新 更多