0

In an array (a) with indexes from 1 to m, I want to compare the values of this array one by one with each other, and if the distance (Difference) between two values is more than a value (z), for example, the difference between a(i) and a(j) at indexes i and j is more than z, I want to save these two indexes i and j and represent them in the output. I wrote these codes:

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

but there is an error in if line:

Subscript indices must either be real positive integers or logicals.

How can I define indexes for matlab. Is a for loop (for i=1:m) needed for passing the array, If a loop is necessary, should I put fprintf out of the loop because it will repeat. For saving and representing the indexes i and j in the output, I'm looking for better functions besides disp or fprintf.

Argyll
  • 8,591
  • 4
  • 25
  • 46
  • 1
    because you defined either `i` or `j` to be non-positive or non integer. But you haven't posted a [mcve], so we can not help – Ander Biguri Jul 31 '19 at 11:55
  • What are `i` and `j`? They should be integers, otherwise you cannot use them to index `a`. I would advise to use some other variable names since `i` and `j` are also the complex numbers. – rinkert Jul 31 '19 at 11:56
  • Not memory efficient, but extremly compact: [i,j] = find(abs(a-a.')>z), suitable for small array. – obchardon Jul 31 '19 at 14:40

2 Answers2

2

(Starting with a being 1-by-m or m-by-1)

Solution 1

(Credit to @Cris Luengo)

abs(a-a.')>z

The outer product like behavior requires Matlab 2016b or newer.

Solution 2

Using bsxfun

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

Solution 3

Using repmat

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

To see how to manipulate the results, consult mathwork documentations on logical arrays.

Technically, you only need tril or triu of the results.


Regarding speed

Looping is out of my considerations. I've seen it being slow in too many occasions. Even though looping technically saves half the amount of computation, it comes with other overheads. So I still expect vectorized methods to be faster and I do not really see a need to test looping. Even repmat with its high memory allocation cost should be faster.

See below for timing data in R2018a. You may wish to use a more accurate timer such as this one written in c if you want to do more testing. For full disclosure, while I tested, there was little cpu demand other than Matlab. I did try with and without 'warm up' rounds -- especially since I do have defined boost behaviors for some cores on my cpu -- but 'warming up' actually made the numbers worse in every subsequent 1000 or 10000 runs. Perhaps my RAM wasn't keeping up with repmat.

Basically, the new syntax (Solution 1) is blazing fast. Time to upgrade if you haven't already. bsxfun is your next best until you upgrade. repmat's memory allocation cost scales too poorly. It's best to avoid 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.
Argyll
  • 8,591
  • 4
  • 25
  • 46
  • You can skip `repmat` for MATLAB >= 2016b. For older versions `bsxfun` is more efficient than `repmat`. Also, `'` is the complex transpose. Use `.'` to transpose a matrix. – Cris Luengo Aug 03 '19 at 13:13
  • @CrisLuengo: re: `'` vs `.'`, thank you for pointing it out. For `bsxfun` vs logical indexing, I am not convinced. See [this question](https://stackoverflow.com/questions/31644737/in-matlab-is-there-a-way-to-copy-lower-triangular-half-of-a-matrix-to-upper-tri). – Argyll Aug 03 '19 at 13:21
  • I’m not sure how that question relates. `bsxfun` has always been faster than `repmat` for built-in arithmetic operations. Especially for larger matrices that don’t fit in cache. The new implicit singleton expansion is even faster. – Cris Luengo Aug 03 '19 at 13:31
  • `bsxfun` may be faster for large data size, but not necessarily small ones like 100s x 100s. Yes, neither that question itself nor the question linked in its answer uses `repmat` exactly. But both cases involve copying matrices. And in both cases `bsxfun` is slower. – Argyll Aug 03 '19 at 13:37
  • Do you mean [this benchmark](https://stackoverflow.com/a/26137901/3293881)? Those are comparing to very different operations, and use a function handle inside `bsxfun`. For arithmetic operations there is not much difference between `repmat` and `bsxfun` for small matrices, and a very large difference for very large matrices. You should try timing those. I’ve stopped using `meshgrid` for the same reason. :) – Cris Luengo Aug 03 '19 at 14:02
  • @CrisLuengo: Using `bsxfun` for this question requires using `circshift` right? – Argyll Aug 03 '19 at 14:24
  • In newer MATLAB do `abs(a-a.')>z`. For older ones it is `abs(bsxfun(@minus,a,a.'))` – Cris Luengo Aug 03 '19 at 14:33
  • In the docs for `bsxfun` they recommend you use the new syntax (`a-a.'`): “Compared to using bsxfun, implicit expansion offers faster speed of execution, better memory usage, and improved readability of code.” – Cris Luengo Aug 03 '19 at 14:36
  • @CrisLuengo: Added some test results. The new syntax is blazing fast. `repmat` is certainly to be avoided. Even 1000x1000 dooms it. – Argyll Aug 04 '19 at 03:38
  • 1
    Nice! Thanks for posting that! (BTW: try [`timeit`](https://www.mathworks.com/help/matlab/ref/timeit.html)!) – Cris Luengo Aug 04 '19 at 03:42
  • @CrisLuengo: More confirmation to what you have been saying :) – Argyll Aug 04 '19 at 03:46
  • @CrisLuengo: if I dont want to make a .m file for a test, will using anonymous function decrease performance? – Argyll Aug 04 '19 at 18:10
  • I don’t know. But if all tests use an anonymous function, the comparison should still be fair. I usually write one M-file for a test, with local functions for each of the methods I’m comparing. – Cris Luengo Aug 04 '19 at 18:56
1

Using 2 nested for loop

% 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)    

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

Adam
  • 2,726
  • 1
  • 9
  • 22
  • You are missing an `abs` in your comparison. Also, the inner loop can be `j=i+1:m` to avoid duplicates. – Cris Luengo Aug 03 '19 at 13:15
  • @CrisLuengo I'm missing `abs` on purpose because `a(i)-a(j)` and `a(j)-a(i)` both will be executed. We are just looking for the indices, the order does not matter.Agree on using `j = i + 1:m` to avoid duplicates. – Adam Aug 03 '19 at 13:43
  • But what if `a` is complex? – Cris Luengo Aug 03 '19 at 13:47
  • I never hear about comparing two complex numbers. I assume `a` is not. And using `abs` which is a function that will eventually cost a time to evaluate is not necessary. – Adam Aug 03 '19 at 13:51