как получить пересечение множества векторов ячеек разной длины в Matlab

У меня есть n векторов ячеек разной длины, назовите их c{i}, i=1,2,...,n.

Я хочу знать, является ли c{j} подмножеством c{i}, например:

c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];

тогда я надеюсь, что смогу найти, что c{4} является подмножеством c{1}, c{5} является подмножеством c{2}.

Я использовал два цикла for с функцией пересечения, которые могут его обработать, но я надеюсь, что смогу использовать не более одного цикла для его обработки, есть ли способ добиться этого?


person Yevis    schedule 17.08.2013    source источник


Ответы (3)


Основываясь на других ответах, вы также можете использовать ismember:

sets = {[1 2 3 4 5 6], [1 3 5 7], [2 4 6 8], [1 4 6], [3 7]};

N = numel(sets);          % number of sets
idx = nchoosek(1:N,2);    % indices of combinations
subsets = false(N,N);
for i = 1:size(idx,1)
    a = idx(i,1); b = idx(i,2);

    % check that set A is a subset of B, and the other way around as well
    subsets(a,b) = all(ismember(sets{a},sets{b}));
    subsets(b,a) = all(ismember(sets{b},sets{a}));
end

Получаем логическую матрицу:

>> subsets
subsets =
     0     0     0     0     0
     0     0     0     0     0
     0     0     0     0     0
     1     0     0     0     0
     0     1     0     0     0

где ненулевые значения указывают отношение подмножества:

>> [i,j] = find(subsets)
i =
     4
     5
j =
     1
     2

т.е. c{4} является подмножеством c{1}, а c{5} является подмножеством c{2}

Примечание: очевидно, что любое множество является подмножеством самого себя, поэтому диагонали матрицы subsets тоже нужно сделать 1. Вы можете добавить это, если хотите использовать:

subsets(1:N+1:end) = true;
person Amro    schedule 18.08.2013
comment
Привет, Амро! Большое спасибо! У меня есть еще один вопрос, могу ли я подсчитать количество подмножеств c{i} в вашем цикле? - person Yevis; 18.08.2013
comment
У меня есть вопрос, когда я тестирую ваш код для моего c{i}, i=1,2,...,n, - person Yevis; 18.08.2013
comment
Я обнаружил, что некоторые из c{i} пусты, поэтому эти векторы пустых ячеек, как правило, являются подмножеством большей части c{i}, можете ли вы это исправить? Спасибо! - person Yevis; 18.08.2013
comment
технически пустой набор является подмножеством любого другого набора: ∀A: ∅⊆A, поэтому результат правильный. Я не уверен, что понимаю первый вопрос, вы имеете в виду подсчет количества других наборов, для которых c{i} является подмножеством? - person Amro; 18.08.2013
comment
это либо суммы строк, либо суммы столбцов, в зависимости от того, что вы подразумеваете под количеством подмножеств c{i} (теперь я все еще уверен, какое именно из них вы имели в виду). Так что либо sum(subsets,1), либо sum(subsets,2) - person Amro; 18.08.2013
comment
кстати, если вы все еще хотите игнорировать пустые наборы, вы можете определить их как: idx = cellfun(@isempty,sets) и использовать эти индексы для обнуления соответствующих строк в матрице subsets: subsets(idx,:)=false - person Amro; 18.08.2013
comment
Большое спасибо, я тестирую три метода, ваш самый быстрый, это идеально! - person Yevis; 18.08.2013

Вот вариант с использованием nchoosek — как и cellfun, это также замаскированный цикл курс:

c{1} = [1 2 3 4 5 6];
c{2} = [1 3 5 7];
c{3} = [2 4 6 8];
c{4} = [1 4 6];
c{5} = [3 7];
combs = nchoosek(1:numel(c),2);
subC = cell(size(combs,1),1);
for i = 1:size(combs,1)
    subC{i} = intersect(c{combs(i,:)});
end

что приводит к массиву ячеек

subC = 

    [1x3 double]
    [1x3 double]
    [1x3 double]
    [         3]
    [1x0 double]
    [         1]
    [1x2 double]
    [1x2 double]
    [1x0 double]
    [1x0 double]

Каждая ячейка в subC соответствует пересечению индексов ячеек в combs (матричная форма может быть легко построена внутри цикла, если это предпочтительнее).

EDIT: если вы просто хотите узнать, является ли один вектор подмножеством другого, то вы можете либо использовать subC и combs сверху, чтобы определить это, либо вычислить его напрямую

combs = nchoosek(1:numel(c),2);
isSubC = logical(eye(numel(c)));
for i = 1:size(combs,1)
    subC = intersect(c{combs(i,:)});
    isSubC(combs(i,1),combs(i,2)) = isequal(subC,c{combs(i,2)});
    isSubC(combs(i,2),combs(i,1)) = isequal(subC,c{combs(i,1)});
end

где isSubC(i,j) указывает, является ли c{j} подмножеством c{i}.

person horchler    schedule 17.08.2013
comment
Спасибо, horchler, это хорошая работа, но как я могу проверить, что c{j} является подмножеством c{i} из subC? - person Yevis; 18.08.2013
comment
Смотрите мою правку. Вы просили ... пересечение множества векторов ячеек разной длины, что и дает первый случай. Как я уже говорил, combs и subC можно использовать для поиска подмножеств. Один из способов — isequal(subC{find(combs(:,1)==i&combs(:,2)==j)},c{j}), где j должно быть › i. - person horchler; 18.08.2013
comment
Привет, horchler, могу я подсчитать количество подмножеств c{i} с помощью этого цикла? - person Yevis; 18.08.2013
comment
Конечно. Ты это пробовал? Вы можете сделать это в цикле или постфактум с помощью nnz(isSubC) (обратите внимание, что я сделал наборы подмножествами самих себя, поэтому вам может потребоваться вычесть numel(c) из общего количества, если вы не хотите их включать). В цикле вы можете просто создать переменную-счетчик и добавлять к ней isequal(subC,c{combs(i,2)}) и isequal(subC,c{combs(i,1)}) на каждой итерации. - person horchler; 18.08.2013
comment
Привет, Хорхлер, когда я тестировал свои векторы с помощью твоего кода, я обнаружил проблему, - person Yevis; 18.08.2013
comment
некоторые из c{i} пусты, поэтому эти пустые векторы являются подмножествами большинства c{i}, я пытаюсь это исправить, но не могу, можете ли вы исправить это для меня? - person Yevis; 18.08.2013
comment
Вы запустили данные пустого набора, используя мой ответ? isSubC не будет соответствовать [], как у @Amro. - person horchler; 18.08.2013
comment
Извините, я ошибся, я имел в виду, что диагонали isSubC равны 1, но это не проблема, спасибо! - person Yevis; 18.08.2013
comment
Ваш код очень хорош, но мне нужен более быстрый алгоритм, поэтому я выбираю код Амро, причина, по которой я думаю, заключается в том, что intersect должен вызывать функцию ismember, поэтому он медленнее, чем прямой вызов ismember. Спасибо еще раз! - person Yevis; 18.08.2013
comment
@Yevis: Нет, в R2012a+ intersect вызывает unique, а не ismember. ismember тоже звонит unique. И обратите внимание, что в моем коде есть только один вызов intersect, а не два вызова ismember. Но только вы знаете, насколько быстро он работает на вашем компьютере (при условии, что вы правильно рассчитали время). Ваш вопрос не указывает на то, что вы заботитесь о скорости, просто упрощая цикл. Если вам действительно нужна скорость, вы можете использовать простые for циклы. - person horchler; 18.08.2013

Вы можете использовать cellfun, но, как указано здесь, это не очень хорошо идея.

Для этого с помощью одного цикла:

c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];
cSize = numel( c);
isect=cell(1,cSize)
for k=1:cSize
  isect{k}=cellfun(@(in) intersect(in,c{k}),c,'UniformOutput',false);
end

Эта процедура может быть повторена, чтобы исключить другую для:

c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];
isect=cellfun(@(in) cellfun(@(in2) intersect(in,in2),c,'UniformOutput',false),c,'UniformOutput',false);

isect{i}{j} это перекресток от c{i} до {j}

Примечание: cellfun будет выполнять внутренний цикл по значению ячейки, поэтому на самом деле вы не удаляете циклы.


Хотя это был не первоначальный вопрос, поиск подмножеств:

c{1}=[1 2 3 4 5 6]; c{2}=[1 3 5 7];c{3}=[2 4 6 8];
c{4}=[1 4 6];c{5}=[3 7];c{6}=[];
isSubset=cell2mat(cellfun(@(in) cellfun(@(in2) isequal(intersect(in,in2),in)|isempty(in),c),c,'UniformOutput',false)');

Результаты:

подмножество =

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

Что возвращает логическое значение, если k является подмножеством m, выполнив isSubset(k,m).

person Werner    schedule 17.08.2013
comment
спасибо, Вернер, вы сказали, что cellfun не будет удалять петли, то есть сложность времени не уменьшится, верно? - person Yevis; 18.08.2013
comment
Кроме того, как я могу проверить, что c {j} является подмножеством c {i} от насекомых? - person Yevis; 18.08.2013
comment
@user2692500 user2692500 Как вы можете видеть в связанной ссылке (нажмите здесь и проверьте мой ответ), сложность времени на самом деле станет выше при использовании cellfun с анонимной функцией (функции синтаксиса @(argument) expression). cellfun кажется быстрее только с чистыми встроенными функциями. - person Werner; 18.08.2013
comment
@user2692500 user2692500 Я не знаю, быстрее или медленнее решение horchler, чем 2 цикла, вы можете проверить это сами, используя тик-так, как это показано в теме мобильного телефона по ссылке… - person Werner; 18.08.2013
comment
@ user2692500: Если вам нужна скорость, я очень подозреваю, что некоторые простые for циклы, выполненные правильно, будут быстрее, чем все, что мы здесь придумали. - person horchler; 18.08.2013
comment
@user2692500 user2692500 на самом деле мой ответ неверен, подмножество - это когда в наборе присутствует подмножество отверстий, а не только часть. Другие решения достаточно хороши, я только что обновил свое, чтобы оно не вводило вас или других людей в заблуждение… не забудьте выбрать принятый ответ и добро пожаловать в stackoverflow x) - person Werner; 18.08.2013
comment
Я обнаружил, что некоторые из c{i} пусты, поэтому эти пустые векторы являются подмножествами большинства c{i}, я пытаюсь это исправить, но не могу, можете ли вы исправить это для меня? - person Yevis; 18.08.2013
comment
@user2692500 user2692500 Вот и все… теперь, пожалуйста, у вас есть 3 разных решения, выберите одно и разработайте его. И учитесь у них, если вы не понимаете, как именно они работают. - person Werner; 18.08.2013
comment
Спасибо, все ваши методы очень хороши, вы правы. Cellfun может быть не лучшим выбором. - person Yevis; 18.08.2013