Generating all combinations containing at least one element of a given set in Matlab

后端 未结 3 1646
刺人心
刺人心 2020-12-31 17:06

I use combnk to generate a list of combinations. How can I generate a subset of combinations, which always includes particular values. For example, for co

3条回答
  •  说谎
    说谎 (楼主)
    2020-12-31 17:29

    For a brute force solution, you can generate all your combinations with COMBNK then use the functions ANY and ISMEMBER to find only those combinations that contain one or more of a subset of numbers. Here's how you can do it using your above example:

    v = 1:10;            %# Set of elements
    vSub = [3 5];        %# Required elements (i.e. at least one must appear in the
                         %#   combinations that are generated)
    c = combnk(v,2);     %# Find pairwise combinations of the numbers 1 through 10
    rowIndex = any(ismember(c,vSub),2);  %# Get row indices where 3 and/or 5 appear
    c = c(rowIndex,:);   %# Keep only combinations with 3 and/or 5
    

    EDIT:

    For a more elegant solution, it looks like Steve and I had a similar idea. However, I've generalized the solution so that it works for both an arbitrary number of required elements and for repeated elements in v. The function SUBCOMBNK will find all the combinations of k values taken from a set v that include at least one of the values in the set vSub:

    function c = subcombnk(v,vSub,k)
    %#SUBCOMBNK   All combinations of the N elements in V taken K at a time and
    %#   with one or more of the elements in VSUB as members.
    
      %# Error-checking (minimal):
    
      if ~all(ismember(vSub,v))
        error('The values in vSub must also be in v.');
      end
    
      %# Initializations:
    
      index = ismember(v,vSub);  %# Index of elements in v that are in vSub
      vSub = v(index);           %# Get elements in v that are in vSub
      v = v(~index);             %# Get elements in v that are not in vSub
      nSubset = numel(vSub);     %# Number of elements in vSub
      nElements = numel(v);      %# Number of elements in v
      c = [];                    %# Initialize combinations to empty
    
      %# Find combinations:
    
      for kSub = max(1,k-nElements):min(k,nSubset)
        M1 = combnk(vSub,kSub);
        if kSub == k
          c = [c; M1];
        else
          M2 = combnk(v,k-kSub);
          c = [c; kron(M1,ones(size(M2,1),1)) repmat(M2,size(M1,1),1)];
        end
      end
    
    end
    

    You can test this function against the brute force solution above to see that it returns the same output:

    cSub = subcombnk(v,vSub,2);
    setxor(c,sort(cSub,2),'rows')   %# Returns an empty matrix if c and cSub
                                    %#   contain exactly the same rows
    

    I further tested this function against the brute force solution using v = 1:15; and vSub = [3 5]; for values of N ranging from 2 to 15. The combinations created were identical, but SUBCOMBNK was significantly faster as shown by the average run times (in msec) displayed below:

    N  | brute force | SUBCOMBNK
    ---+-------------+----------
    2  |     1.49    |    0.98
    3  |     4.91    |    1.17
    4  |    17.67    |    4.67
    5  |    22.35    |    8.67
    6  |    30.71    |   11.71
    7  |    36.80    |   14.46
    8  |    35.41    |   16.69
    9  |    31.85    |   16.71
    10 |    25.03    |   12.56
    11 |    19.62    |    9.46
    12 |    16.14    |    7.30
    13 |    14.32    |    4.32
    14 |     0.14    |    0.59*   #This could probably be sped up by checking for
    15 |     0.11    |    0.33*   #simplified cases (i.e. all elements in v used)
    

提交回复
热议问题