所有可能的N选择K而不进行重复

时间:2015-05-29 20:46:54

标签: algorithm matlab combinations

我正在尝试创建一个能够通过行向量并输出n的可能组合的函数,选择k 而不递归

例如:3选择[a,b,c]输出[a,b; A,C; B,C]

我发现了这个:How to loop through all the combinations of e.g. 48 choose 5,它显示了如何为固定的n选择k做这个:https://codereview.stackexchange.com/questions/7001/generating-all-combinations-of-an-array显示了如何获得所有可能的组合。使用后面的代码,我设法在matlab中创建一个非常简单和低效的函数,它返回结果:

function [ combi ] = NCK(x,k)
%x - row vector of inputs
%k - number of elements in the combinations

combi = [];
letLen = 2^length(x);

for i = 0:letLen-1

    temp=[0];
    a=1;

    for j=0:length(x)-1

        if (bitand(i,2^j))
            temp(k) = x(j+1);
            a=a+1;
        end
    end

    if (nnz(temp) == k)
        combi=[combi; derp];
    end
end
combi = sortrows(combi);
end

这适用于非常小的向量,但我需要能够使用长度至少为50的向量。我已经找到了许多如何以递归方式执行此操作的示例,但是有没有一种有效的方法可以在没有递归的情况下执行此操作,并且仍然能够执行可变大小的向量和ks?

1 个答案:

答案 0 :(得分:2)

这是一个简单的函数,它会对k个和n-k个零进行排列,并返回nchoosek的下一个组合。它完全独立于nk的值,直接从输入数组中获取值。

function [nextc] = nextComb(oldc)
   nextc = [];
   o = find(oldc, 1);                 %// find the first one
   z = find(~oldc(o+1:end), 1) + o;   %// find the first zero *after* the first one
   if length(z) > 0
      nextc = oldc;
      nextc(1:z-1) = 0;
      nextc(z) = 1;                   %// make the first zero a one
      nextc(1:nnz(oldc(1:z-2))) = 1;  %// move previous ones to the beginning
   else
      nextc = zeros(size(oldc));
      nextc(1:nnz(oldc)) = 1;         %// start over
   end
end

(请注意,仅当您希望组合从最后一个组合环绕到第一个组合时,才需要else子句。)

如果您使用例如:

调用此函数
A = [1 1 1 1 1 0 1 0 0 1 1]
nextCombination = nextComb(A)

输出将是:

A =

   1   1   1   1   1   0   1   0   0   1   1

nextCombination =

   1   1   1   1   0   1   1   0   0   1   1

然后,您可以将其用作字母表中的掩码(或您想要组合的任何元素)。

C = ['a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k']
C(find(nextCombination))

ans = abcdegjk

此排序中的第一个组合是

1   1   1   1   1   1   1   1   0   0   0

,最后一个是

0   0   0   1   1   1   1   1   1   1   1

以编程方式生成第一个组合,

n = 11; k = 8;
nextCombination = zeros(1,n);
nextCombination(1:k) = 1;

现在您可以遍历这些组合(或者您愿意等待的许多组合):

for c = 2:nchoosek(n,k)   %// start from 2; we already have 1
   nextCombination = nextComb(A);
   %// do something with the combination...
end

上面的示例:

nextCombination = [1 1 0];
C(find(nextCombination))
for c = 2:nchoosek(3,2)
   nextCombination = nextComb(nextCombination);
   C(find(nextCombination))
end

ans = ab
ans = ac
ans = bc

注意:我已经更新了代码;我忘了包含该行来将所有在交换数字之前发生的1移动到数组的开头。当前代码(除了上面更正)还在ideone here上。 4 choose 2的输出为:

allCombs =

   1   2
   1   3
   2   3
   1   4
   2   4
   3   4