Matlab: поиск строк в матрице с фиксированным первым и последним элементом с векторизацией

У меня есть матрица, подобная следующей (произвольные столбцы/строки):

1    0    0    0    0
1    2    0    0    0
1    2    3    0    0
1    2    3    4    0
1    2    3    4    5
1    2    5    0    0
1    2    5    3    0
1    2    5    3    4
1    4    0    0    0
1    4    2    0    0
1    4    2    3    0
1    4    2    5    0
1    4    2    5    3
1    4    5    0    0
1    4    5    3    0
2    0    0    0    0
2    3    0    0    0
2    3    4    0    0
2    3    4    5    0
2    5    0    0    0
2    5    3    0    0
2    5    3    4    0
3    0    0    0    0
3    4    0    0    0
3    4    2    0    0
3    4    2    5    0
3    4    5    0    0

и теперь я хочу получить все строки, где первый элемент - это определенное значение X, а последний элемент (то есть последний элемент! = 0) - это определенное значение Y, ИЛИ повернуто: первый - Y, а последний - X .

Не вижу никакого быстрого кода, который НЕ использует цикл for :( Спасибо!

EDIT: отфильтровать все строки с определенным первым элементом очень просто, вам не нужно мне здесь помогать. Итак, давайте предположим, что я хочу сделать только следующее: отфильтровать все строки, где последний элемент (т. е. последний элемент! = 0 в каждой строке) равен X или Y.

EDIT Большое спасибо за ваши сообщения. Я сравнил три возможных решения с матрицей из 473408*10 элементов. Вот тестовый скрипт: http://pastebin.com/9hEAWw9a.

Результаты были следующими:

t1 = 2.9425 Jonas
t2 = 0.0999 Brendan
t3 = 0.0951 Oli

Так что большое спасибо, ребята, я придерживаюсь решения Оли и поэтому принимаю его. Спасибо за все остальные решения!


person tim    schedule 18.01.2012    source источник


Ответы (3)


Вот хитрость: найдите числа с 0 справа и просуммируйте их все:

H=[1 2 0 0 0;
   2 3 1 0 0;
   4 5 8 0 0;
   8 5 4 2 2];

lastNumber=sum(H.*[H(:,2:end)==0 true(size(H,1),1)],2)

ans =

     2
     1
     8
     2

Остальное легко:

firstNumber=H(:,1);

find( (firstNumber==f) & (lastNumber==l) )
person Oli    schedule 18.01.2012
comment
хорошая идея, даже если она потерпит неудачу, если перед концом будут нули. +1 в любом случае - person Jonas; 18.01.2012
comment
@Jonas: Вы имеете в виду, как в решении Брендана? На самом деле, как я прокомментировал сообщение brendans, это не имеет значения, поскольку на самом деле в конце есть ТОЛЬКО нули. - person tim; 19.01.2012
comment
@Oli: Спасибо, Оли, это тоже очень мило! Посмотрите на мое редактирование в первом посте, там есть тестовый скрипт, и ваш скрипт побеждает (: Итак, я принял! - person tim; 19.01.2012

Все, что вам нужно сделать, это найти линейные индексы последнего ненулевого элемента каждой строки. Остальное легко:

[nRows,nCols] = size(A);
[u,v] = find(A); %# find all non-zero elements in A
%# for each row, find the highest column index with accumarray
%# and convert to linear index with sub2ind
lastIdx = sub2ind([nRows,nCols],(1:nRows)',accumarray(u,v,[nRows,1],@max,NaN));

Чтобы отфильтровать строки, вы можете написать

goodRows = A(:,1) == X & A(lastIdx) == Y
person Jonas    schedule 18.01.2012
comment
Это кажется милым, спасибо. Завтра протестирую и потом скажу! Я даже не знал о sub2ind(), так что это вызвало бы у меня много головной боли :) Но если кто-то это знает, это может быть легко, ха-ха. Спасибо! Редактировать: А еще не знал accumarray :) - person tim; 18.01.2012
comment
@ColHeather: accumarray — это функция, которую сначала немного сложно понять, но она невероятно полезна. - person Jonas; 18.01.2012
comment
Это работает даже в том случае, если мое решение не работает. Надо будет почитать accumarray... - person Brendan; 18.01.2012
comment
Это в 3 раза быстрее, чем arrayfun(@(i) find(A(i,:)~=0,1,'last'),1:nRows); - person cyborg; 18.01.2012
comment
+1, потому что теперь я знаю о accumarray! Я вспомнил десятки ситуаций, когда я мог бы его использовать. - person Andrey Rubshtein; 18.01.2012

Это работает только если числа в каждой строке представляют собой x ненулевых чисел, за которыми следует серия нулей. то есть это не сработает, если возможно следующее 1 0 3 4 0 0, я предполагаю, что это невозможно, основываясь на введенном вами образце...

% 'a' is your array
[nx, ny] = size(a);
inds = [0:ny:ny*(nx-1)]' + sum(a ~= 0, 2);
% Needs to transpose so that the indexing reads left-to-right
aT = a'; 
valid1 = aT(inds) == Y;
valid2 = a(:,1) == X;
valid = valid1 & valid2;
valid_rows = a(valid,:);

Это грязно, я знаю...

person Brendan    schedule 18.01.2012
comment
Да, в конце будут ТОЛЬКО нули, как вы сказали :) Завтра тоже попробую, спасибо! - person tim; 19.01.2012
comment
Хорошо, спасибо Брендан, это работает очень хорошо для меня. Посмотрите на мою правку выше для тестового сценария :-) Вы довольно близки к решению Оли... :) - person tim; 19.01.2012