从单个集合生成所有子集

时间:2012-06-03 11:19:40

标签: c++

我试图理解从一组生成所有子集的代码。这是代码

#include <stdio.h>

/* Applies the mask to a set like {1, 2, ..., n} and prints it */
void printv(int mask[], int n) {
    int i;
    printf("{ ");
    for (i = 0; i < n; ++i)
        if (mask[i])
            printf("%d ", i + 1); /*i+1 is part of the subset*/
    printf("\\b }\\n");
}

/* Generates the next mask*/
int next(int mask[], int n) {
    int i;
    for (i = 0; (i < n) && mask[i]; ++i)
        mask[i] = 0;

    if (i < n) {
        mask[i] = 1;
        return 1;
    }
    return 0;
}

int main(int argc, char *argv[]) {
    int n = 3;

    int mask[16]; /* Guess what this is */
    int i;
    for (i = 0; i < n; ++i)
        mask[i] = 0;

    /* Print the first set */
    printv(mask, n);

    /* Print all the others */
    while (next(mask, n))
        printv(mask, n);

    return 0;
}

我不理解下一个函数中这一行for (i = 0; (i < n) && mask[i]; ++i)背后的逻辑。如何在这里生成下一个掩码?

代码和算法在这里看: http://compprog.wordpress.com/2007/10/10/generating-subsets/

4 个答案:

答案 0 :(得分:6)

这只是counting in binary的一种实现。基本思想是将最不重要(最后)零更改为一,并将其​​后的所有零更改为零。如果被解释为二进制数,“下一个”掩码将比前一个掩盖“多一个”。

因为数组首先排列在一个位置,所以它从传统的数字符号向后看。

除了使用布尔值数组之外,它还可以使用一个数字和++运算符的二进制表示中的位。

int next(int &mask, int n) { // using C++ reference
    if ( mask == ( 1u << n ) - 1 ) return 0;
    ++ mask;
    return 1;
}

void printv(int mask, int n) {
    int i;
    printf("{ ");
    for (i = 0; i < n; ++i)
        if (mask & ( 1 << i ) )
            printf("%d ", i + 1); /*i+1 is part of the subset*/
    printf("\\b }\\n");
}

我使用了一点C ++,因为你标记了这个问题,但是发布的代码是普通的C.

答案 1 :(得分:2)

去年我参加了第六届ITAT比赛的C语言比赛,我通过掩码的帮助解决了第二个问题(尽管,这可能不是解决这个问题的最佳解决方案。)

当您尝试派生{a,b,c}的所有子集时,您可以这样做:

  1. 您可能会也可能不会使用第一个元素a
  2. 可能会或可能不会采用第二个元素b
  3. c相同。
  4. 所以你结束了一系列3个采取或不采取选择。这可以用二进制或布尔值表示:代表1,而不是0

    您将获得以下八个面具:(按a,b,c的顺序排列)

    000 100 010 110 001 101 011 111

    生成110的下一个掩码:

    1. 元素0是1。将其切换为0
    2. 元素1是1。将其切换为0
    3. 元素2是0。将其切换为1
    4. 现在你有001这是下一个掩码,它会生成子集{c}
    5. for (i = 0; (i < n) && mask[i]; ++i)正是如此。

      1. 从元素0开始。
      2. while(i不超过你的面具长度AND元素i是1
      3. 执行将我翻转到0和++ i(转到下一个元素)的正文代码。转到2(检查)。
      4. 如果当前掩码为111(最后一个掩码),next()函数只返回1以表示END。

        (P.S。非零整数总是表示真。)

答案 2 :(得分:0)

问题中的循环从数组的开头开始,并将所有1设置为0,直到遇到0。下一个语句将此0设置为1(如果可能)。那么会发生什么:0,0,0 - &gt; 1,0,0 - &gt; 0,1,0 - &gt; 1,1,0 - &gt; 0,0,1 ......我不是一个核心的C程序员,但我认为通过使用一个位字段并迭代递增1可以更容易。

答案 3 :(得分:0)

for (i = 0; (i < n) && mask[i]; ++i)
  1. 为:
  2. 从0开始,每次增加1
  3. 当i小于n且位置i的掩码数组中的位设置为
  4. 时,不要停止

    它真的很直白:for语句的3个部分:初始状态;结束条件;操作

    因此,如果你能理解for (i=0; i < 5; i++)意味着从0开始并每次增加1,直到它不能小于5,你就可以理解你所询问的更复杂的for循环。

    在这种情况下,它通过循环查找未设置的掩码的第一个元素,清除每个元素,然后执行其他操作 - 即如果没有设置掩码位,则到达阵列的末尾。对我来说就像一个简单的方法,只将一个数组中的一个元素设置为1,依次获得结果:100,010,001

相关问题