【问题标题】:All possible N choose K WITHOUT recusion所有可能的 N 选择 K 没有递归
【发布时间】:2015-05-29 20:46:54
【问题描述】:

我正在尝试创建一个函数,该函数能够遍历行向量并输出 n 选择 k 无需递归的可能组合。

例如:3 在 [a,b,c] 上选择 2 输出 [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?

【问题讨论】:

  • 是作业还是论文?
  • 为什么不只是nchoosek('abc',2)
  • 程序员分享了多种语言组合的算法,have an idea (rosetta code, inspired from rosetta stone)
  • 这是我正在做的一个模拟。我不能使用 nchoosek,因为 simulink 无法将其导出到 C 代码中。

标签: algorithm matlab combinations


【解决方案1】:

这是一个简单的函数,它将采用k 1 和 n-k 0 的排列,并返回 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

【讨论】:

  • 在测试完您的代码后,它会返回正确数量的答案,但它不会遍历所有组合。当我将向量更改为 C=1:4;将下一个组合设置为 [1 1 0 0] 并做了 4 选择 2 它返回:[1,2; 1,3; 2,3; 2,4; 3,4; 1,2]。 nextCombination 跳过 1,4 导致它在末尾复制 1,2
  • @Scoterster86 抱歉,我以前的版本中有这行,但不知何故被遗漏了。此外,如果您使用ideone 版本,它们目前似乎存在一些时钟问题,导致时间戳警告在 stderr 上喷涌而出。不过不应该影响脚本的执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多