How to find the previous position in an array where a value is met?

16 views (last 30 days)
Hi everyone,
As it is said in the title, I'd like to find the previous position(not the last position) in an array where a value is met. I have a program that works but I'm looking for improvement or optimazations of my program.
To be specific, let's suppose I have an array A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1] and I'd like to fill in values in another array B based on values in A.
The rule is, if A(i)=1, then B(i)=1;
if A(i)=0, find the index of the previous element in A which is 1, and compute the difference between that index and current index number. If the difference is larger than 1 and no larger than 3, B(i)=2; otherwise B(i)=0;
For example, when i=5, the index of previous element in A which is 1 is 3, then 5-3=2<3, so B(5)=2; when i=7, the index of previouse element in A which is 1 is 3, then 7-3=4>3, so B(7)=0;
So in the end, I hope to have an array B=[1 0 1 0 2 2 0 1 1 1 0 2 2 0 1];
Here is what my current program looks like
A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1];
B=NaN(length(A),1);
for i=1:length(A)
if A(i)==1
B(i)=1;
else
if i-find(B==1,1,'last')>1 && i-find(B==1,1,'last')<=3
B(i)=2;
else
B(i)=0;
end
end
end
The program works well. However the thing is, in future calculations, the length of array A could be as large as 1.5million and there will be some other calculations with indefinite amount of elements in the inner loop. So it's really time-comsuming to execute this find command in each iteration(last time I tried, it took 8hrs to compute for 500000 data points).
I tried to break the program into two one-layer-if-statements by allocating zero value to all data that satisfies the conditon in the inner loop and start another loop through them to find for which element I should specify 2 instead of 0. However, the problem is, the command find(X,n,direction) could only find the last position of an array where a value is met. So it always returns number 15 as it is the last position where value 1 appears in array A. I'm wondering, is there any other command that finds the index of the previous position where a value is met? For example, when i=5, the command would return number 3 while when i=11:14, it would return 10?

Accepted Answer

Johannes Fischer
Johannes Fischer on 28 Aug 2019
Edited: Johannes Fischer on 28 Aug 2019
This is an ideal playgrouond to see how you can increase speed by avoiding for loops.
% My idea is to create an array, that in each field contains the value to
% the index of the previous '1' in A. To get there, I first get the indices
% where A is 1:
A=[1 0 1 0 0 0 0 1 1 1 0 0 0 0 1];
I = 1:numel(A);
AA = I;
AA(A==0)=0;
% AA = [1 0 3 0 0 0 0 8 9 10 0 0 0 0 15];
% Now the idea is to fill the zeros in AA with the value that preceeds
% contiguous zeros to get to Last = [1 1 3 3 3 3 3 8 9 10 10 10 10 10 15];
% Im not sure this is the most efficient or easiest to understand way but
% here is the idea: use cumsum() to propagate the information of the
% previous index along the array. For that to work the array must be
% prepared such that at index 8 there is a 5 that will add up to 8 = 5+3.
% The 5 in this case also is the number of zeros +1 between the 3 and 8 in
% AA. Since the indices are linearly increasing, we can get it by taking
% diff() where AA is not equal to 0.
d = [1 diff(AA(AA~=0))];
% you did not specify what to do if A(1) is 0. My approach only works when
% A(1) is 1
% now place the values in d at the position where A is equal to 1
a = A;
a(A~=0) = d;
% and calculate the cumulative sum
Last = cumsum(a);
% To get the distance to the last '1' in A its merely
diffToLast = I-Last;
% Followed by
B = zeros(1, numel(A));
B(A==1) = 1;
B(diffToLast == 2 | diffToLast == 3) = 2;
edit: restructured added more commentary to code.
  3 Comments
Johannes Fischer
Johannes Fischer on 28 Aug 2019
As I said, that case wasnt specified, but if you replace with
if A(1) == 1
d = [1 diff(AA(AA~=0))];
else
d = [find(A==1, 1, 'first') diff(AA(AA~=0))];
end
the results for B should be identical except for the values before the second '1' in A. For the others, the result assumes that A(-1) is 1.

Sign in to comment.

More Answers (1)

darova
darova on 28 Aug 2019
Edited: darova on 28 Aug 2019
Probably Johannes's version could be faster but mine is simpler to understand
clc,clear
A = randi([0 1],10,1);
% divide array into sections
ind = find(abs(diff(A))>0);
% ignore first zeros
if A(1) == 0
ind(1) = [];
end
% if last element is zero - add index
if A(end) == 0
ind(end+1) = length(A);
end
B = A;
for i = 1:2:length(ind)
i1 = ind(i)+1; % first zero in current section
i2 = ind(i+1); % last zero
if i1+0 < i2 % if second zero exists
B(i1+1) = 2; % replace 2d zero with '2'
end
if i1+1 < i2 % if third zero exists
B(i1+2) = 2; % replace 3d zero with '2'
end
end
[A B]
  1 Comment
darova
darova on 28 Aug 2019
Shorter version. Same idea as Johannes: numerate zeros
clc,clear
A = randi([0 1], 1, 10);
A1 = A*0;
i1 = find(A,1,'first'); % start from first '1'
for i = i1:length(A)
if A(i) == 0
k = k + 1;
else
k = 0;
end
A1(i) = k;
end
B = A;
B(A1==2 | A1==3) = 2;
[A;B]'

Sign in to comment.

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!