erbal
erbal

Reputation: 431

How to identify first occurrence of at least 10 adjacent '1's in a vector in MATLAB?

How to identify first occurrence of at least 10 adjacent '1's in a vector starting from 1st element which is composed of zeros and ones only?

A=[0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1]

The code should return index of A (12 in this example), where series of 1's start.

Upvotes: 3

Views: 182

Answers (5)

reverse_engineer
reverse_engineer

Reputation: 4269

Isn't the kind of "memory-intensive" solution faster? (Can't check here, don't have Matlab any more :´( )

A = [0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1];

len_A = length(A);

helper = repmat(A,10,2);

helper = helper(bsxfun(@plus,bsxfun(@plus,(0:10-1)',0:len_A-1)*10,(1:10)'));

result = find(all(helper(:,1:end-10)),1)

EDIT: What about:

helper = cumsum(A);
result = 1 + find(helper - [helper(11:end) nan(1,10)]==-10,1)

should be quite good too

Upvotes: 1

Stewie Griffin
Stewie Griffin

Reputation: 14939

Summary: Loops beat arrayfun by a mile!


Here's a way to do it using a classic old loop:

function x = test_loop(A)
for ii = 1:numel(A)-9
    x = ii*(all(A(ii:ii+9)));
    if x
        break;
    end   
end
end

Now, let's investigate the performance of arrayfun vs classic for-loops:

The arrayfun approach:

test_array = @(A)find(arrayfun(@(x) all(A(x:x+9)==ones(1,10)), 1:numel(A)-9)==1,1)

A = [0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1];
B = repmat(A,1,10);   %% 1x300 array

f = @() test_array(A);
g = @() test_loop(A);

Benchmarking results for your array (1x30):

timeit(f)
ans =
   2.1397e-04

timeit(g)
ans =
   2.6224e-05

Benchmarking results for array of size 1x300:

f = @() test_array(B);
g = @() test_loop(B);

timeit(f)
ans =
    0.0021

timeit(g)
ans =
   2.6598e-05

If the first sequence of is found late in the vector, the loop will be a bit slower, but still way faster than arrayfun:

B(1:220) = 0;

f = @() test_array(B);
g = @() test_loop(B);

timeit(f)
ans =
    0.0021
timeit(g)
ans =
   4.5033e-04

Edit:

Why increment the counter by 1 for every iteration? The following loop is approximately twice as fast as the for loop, depending on where the sequence appear in the vector, and the spacing between the zeros.

It's still slower that conv, but thought it was worth sharing anyway:

function x = test_loop(A)    
ii = 1;
x = false;
while ~x
    k = find(A(ii:ii+9)~=1, 1, 'last');
    x = isempty(k)*ii;
    ii = ii + k;
end
end

Upvotes: 5

Rafael Monteiro
Rafael Monteiro

Reputation: 4549

Possible solution using diff and find:

temp = diff([0 A 0]);
start = find(temp == 1);
finish = find(temp == -1);
result = start(find(finish - start >= 10, 1, 'first'));

Upvotes: 2

Luis Mendo
Luis Mendo

Reputation: 112659

You could use a regexp:

A = [0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1];
N = 10;
result = regexp(char(A+'0'), ['1{' num2str(N) '}'], 'once');

Or convolution:

A = [0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1];
N = 10;
result = find(conv(2*A-1, ones(1,N), 'valid')==N, 1)

Upvotes: 3

Gnimuc
Gnimuc

Reputation: 8566

you can use arrayfun to do Matlab-style "array comprehension":

A=[0 1 0 0 0 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 0 1]
b = arrayfun(@(x) all(A(x:x+9)==ones(1,10)), 1:numel(A)-9)
find(b==1)

Upvotes: 3

Related Questions