Поиск всех индексов

Это то, что описано в одном из примеров ismember:

Определите два вектора со значениями.

A = [5 3 4 2]; B = [2 4 4 4 6 8];

Определите, какие элементы A также находятся в B, а также их соответствующие местоположения в B.

[Lia,Locb] = ismember(A,B)

Результат:

Lia =
     0     0     1     1

Locb =    
     0     0     2     1

Элемент в B с наименьшим индексом, который соответствует A(3), равен B(2). A(4) равно B(1). Есть ли способ, с помощью которого мы могли бы найти все индексы элементов B, соответствующих одному и тому же элементу в A?

Ответ 1

Вы можете поменять входные аргументы на ismember:

[tf, ia] = ismember(B, A)

Для вашего примера вы должны получить:

tf =
     1     1     1     1     0     0

ia = 
     4     3     3     3     0     0

Это позволяет вам найти, скажем, индексы всех элементов B, которые равны A(3), просто:

find(ia == 3)

Здесь отличное решение для общего случая:

[tf, ia] = ismember(B, A);
idx = 1:numel(B);
ib = accumarray(nonzeros(ia), idx(tf), [], @(x){x});

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

ib = 
    []
    []
    [2     3     4]
    [      1]

что означает, что нет элементов в B, соответствующих A(1) и A(2), A(3) соответствует элементам B(2), B(3) и B(4), а A(4) равно B(1).

Ответ 2

Эта строка вернет все индексы:

F=arrayfun(@(x)(find(A(x)==B)),1:numel(A),'UniformOutput',false)

Или более длинная версия, которая может быть легче читать:

F=cell(numel(A),1);
for x=1:numel(A)
  F{x}=find(A(x)==B);
end

find(A(x)==B) проверяет все вхождения A(x) в B. Это делается для каждого элемента массива, используя либо цикл for, либо arrayfun.

Ответ 3

Простым подходом является использование bsxfun для проверки равенства между каждым элементом A и B:

ind = bsxfun(@eq, A(:), B(:).');
list = cellfun(@find, mat2cell(ind, ones(1,numel(A)), numel(B)), 'uni', 0);

Матрица ind дает результат в логической форме (т.е. 0 или 1 значения), а list - это массив ячеек, содержащий индексы:

>>  ind

ind =

     0     0     0     0     0     0
     0     0     0     0     0     0
     0     1     1     1     0     0
     1     0     0     0     0     0

>> celldisp(list)

list{1} =

     []


list{2} =

     []    

list{3} =

     2     3     4

list{4} =

     1

Ответ 4

Самые элегантные решения (т.е. те, которые не используют итерации find), включают в себя обмен входов на ismember и группировку, как индексы с accumarray, как в ответе Eitan, или векторизация find с помощью bsxfun, как в Ответ Луиса Мендо, ИМХО.

Однако для тех, кто интересуется решением с недокументированной функциональностью и, по общему признанию, хакерским подходом, вот еще один способ сделать это (т.е. для каждого элемента A найти индексы всех соответствующих элементов в B), Мысль выглядит следующим образом: в отсортированном B, что, если у вас были первые и последние индексы каждого соответствующего элемента? Оказывается, есть две вспомогательные функции, используемые ismember (если у вас R2012b +, я думаю), что даст вам оба этих индекса: _ismemberfirst (a builtin) и ismembc2.

Для примера данных A = [5 3 4 2]; B = [2 4 4 4 6 8]; в вопросе, вот реализация:

[Bs,sortInds] = sort(B); % nop for this B, but required in general
firstInds = builtin('_ismemberfirst',A,Bs) % newish version required
firstInds =
     0     0     2     1
lastInds = ismembc2(A,Bs)
lastInds =
     0     0     4     1

Теперь выполняется тяжелая работа. У нас есть первый и последний индексы в B для каждого элемента в A без необходимости выполнять цикл. В B не встречается A(1) или A(2) (5 или 3), поэтому эти индексы 0. Значение 4 (A(3)) происходит в местах 2: 4 (т.е. all(B(2:4)==A(3))). Аналогично, A(4) находится в B(1:1).

Мы можем игнорировать sortInds в приведенном выше примере, так как B уже отсортирован, но несортированный B обрабатывается простым поиском местоположений в несортированном массиве. Мы можем быстро выполнить этот поиск и упаковать каждый диапазон индексов с помощью arrayfun, имея в виду, что уже интенсивно работающая вычислить задача фактического поиска индексов уже выполнена:

allInds = arrayfun(@(x,y)sortInds(x:y-(x==0)),firstInds,lastInds,'uni',0)
allInds = 
    [1x0 double]    [1x0 double]    [1x3 double]    [1]

Каждая ячейка имеет индексы в B (если есть) каждого элемента A. Первые две ячейки представляют собой пустые массивы, как и ожидалось. Подойдя ближе к третьему элементу:

>> allInds{3}
ans =
     2     3     4
>> A(3)
ans =
     4
>> B(allInds{3})
ans =
     4     4     4

Операция тестирования с несортированным B:

B(4:5) = B([5 4])
B =
     2     4     4     6     4     8

[Bs,sortInds] = sort(B);
firstInds = builtin('_ismemberfirst',A,Bs);
lastInds = ismembc2(A,Bs);
allInds = arrayfun(@(x,y)sortInds(x:y-(x==0)),firstInds,lastInds,'uni',0);

allInds{3} % of A(3) in B
ans =
     2     3     5

B(allInds{3})
ans =
     4     4     4

Стоит ли делать это таким образом, с штрафом за sort и двумя эффективными вызовами ismember? Может быть, нет, но я считаю это интересным решением. Если у вас есть отсортированный B, он еще быстрее, так как две встроенные функции предполагают, что второй аргумент (Bs) сортируется и не тратит время на проверки. Попробуйте и посмотрите, что сработает для вас.

Ответ 5

Решения Eitan T. и Daniel R отвечают на ваш вопрос в целом. Мое решение является удобной и простой альтернативой, если вас интересуют только те элементы, которые являются общими для обоих векторов, но НЕ как они связаны с помощью ismember, например. вы просто хотите отфильтровать данные для общих элементов:

Я бы использовал инверсию противоположности: setxor

A = [5 3 4 2]; 
B = [2 4 4 4 6 8];

[~,iai,ibi] = setxor(A,B);   % elements which are not in common
ia = 1:numel(A);
ib = 1:numel(B);
ia(iai) = [];                %indices of elements of B in A
ib(ibi) = [];                %indices of elements of A in B

Или просто одно и то же:

[~,iai,ibi] = setxor(A,B);
ia = setxor(1:numel(A),iai);
ib = setxor(1:numel(B),ibi);

возвращает в обоих случаях индексы элементов, также существующих в соответствующем другом векторе, так сказать, реализацию ~isnotmember

ia =

     3     4

ib =

     1     2     3     4