【问题标题】:how to get the intersection of many different length cell vectors in matlab如何在matlab中获得许多不同长度单元向量的交集
【发布时间】:2013-08-17 17:48:53
【问题描述】:

我有 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循环,有intersect函数可以处理,但我希望最多可以用一个循环处理,有什么办法可以实现吗?

【问题讨论】:

    标签: matlab vector subset cell-array set-intersection


    【解决方案1】:

    在其他答案的基础上,您还可以使用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;
    

    【讨论】:

    • 您好 Amro,非常感谢您!我还有一个问题,我可以计算你循环中 c{i} 子集的数量吗?
    • 我有一个问题,当我为我的 c{i}, i=1,2,...,n, 测试您的代码时,
    • 我发现一些 c{i} 是空的,所以这些空单元向量往往是大多数 c{i} 的子集,你能解决它吗?谢谢!
    • 从技术上讲,empty set 是任何其他集合的子集:∀A: ∅⊆A,因此结果是正确的。我不确定我是否理解第一个问题,您的意思是计算c{i} 所属的其他集合的数量吗?
    • 它是行总和或列总和,具体取决于您所说的“c{i} 有多少子集”(我现在仍然确定您的意思是哪一个)。所以要么sum(subsets,1)要么sum(subsets,2)
    【解决方案2】:

    这是一个使用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 中单元格索引的交集(如果愿意,可以在循环中轻松构建矩阵形式)。

    编辑:如果您只是想知道一个向量是否是另一个向量的subset,那么您可以使用上面的subCcombs 来确定或直接计算它

    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} 的子集。

    【讨论】:

    • 谢谢 horchler,这是一个不错的工作,但我如何检查 c{j} 是来自 subC 的 c{i} 的子集?
    • 查看我的编辑。您确实要求“...许多不同长度单元向量的交集”,这是第一种情况给出的。正如我所指出的,combssubC 可用于查找子集。一种方法是使用isequal(subC{find(combs(:,1)==i&combs(:,2)==j)},c{j}),其中j 必须> i
    • 嗨,horchler,我可以用这个循环计算 c{i} 子集的数量吗?
    • 当然。你试过了吗?您可以在循环中执行此操作,也可以在事后使用nnz(isSubC) 执行此操作(请注意,我已将集合设为自身的子集,因此如果您不想包含,您可能需要从总数中减去 numel(c)他们)。在循环中,您可以创建一个计数器变量,并在每次迭代时添加isequal(subC,c{combs(i,2)})isequal(subC,c{combs(i,1)})
    • 嗨 horchler,当我用你的代码测试我的向量时,我发现了一个问题,
    【解决方案3】:

    您可以使用 cellfun,但正如 here 所述,这不是一个好主意。

    为此,只需一个循环:

    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)');
    

    结果:

    isSubset =

     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
    

    如果km 的子集,则通过isSubset(k,m) 返回一个布尔值。

    【讨论】:

    • 谢谢 Werner,你说 cellfun 不会删除循环,也就是说时间复杂度不会随之降低,对吧?
    • 另外,如何检查 c{j} 是来自昆虫的 c{i} 的子集?
    • @user2692500 正如您在链接参考中看到的(单击此处并检查我的答案),当使用带有匿名函数的 cellfun 时,时间复杂度实际上会更高(@(argument) expression 语法函数)。 cellfun 似乎只有使用纯内置函数才能更快。
    • @user2692500 好了……现在拜托,你有 3 种不同的解决方案,选择一个并解决。如果您不了解它们的工作原理,请向他们学习。
    • 谢谢,你所有的方法都很好,你是对的 cellfun 可能不是一个好的选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-05
    相关资源
    最近更新 更多