【问题标题】:Enumerating Permutations of a set of subsets枚举一组子集的排列
【发布时间】:2009-10-11 08:02:34
【问题描述】:

我有设置 S1 = {s11,s12,s13), S2 = {s21,s22,s23) 等等直到 SN。我需要生成由 S1,S2..SN.. 组成的所有排列每个集合中只有一个元素。

例如:

S1 = {a,b,c}
S2 = {d,e,f}
S3 = {g,h,i}

我的排列是:

{a,d,g}, {a,d,h}, {a,d,i}, {a,e,g}, {a,e,h}....

我该怎么做呢? (我可以随机从每个中提取 1 个并将它们合并,但在我看来,这甚至是个坏主意)。

为了一般性,假设每个集合中有“n”个元素。我正在考虑在 C 中实现它。请注意,'N' 和 'n' 不是固定的。

【问题讨论】:

  • 是的。它基本上是集合的笛卡尔积。
  • 我可以有一个二维矩阵表示。但是,同样,可以有任何数据结构,只要它不是过度杀伤并且在 C 中可以编写代码。

标签: c set permutation


【解决方案1】:

这只是一个递归问题。让我们假设这些定义。

const int MAXE = 1000, MAXN = 1000;
int N;                // number of sets.
int num[MAXN];        // number of elements of each set.
int set[MAXN][MAXE];  // elements of each set. i-th set has elements from
                      // set[i][0] until set[i][num[i]-1].
int result[MAXN];     // temporary array to hold each permutation.

功能是

void permute(int i)
{
    if (i == N)
    {
        for (int j = 0; j < N; j++)
            printf("%d%c", result[j], j==N-1 ? '\n' : ' ');
    }
    else
    {
        for (int j = 0; j < num[i]; j++)
        {
            result[i] = set[i][j];
            permute(i+1);
        }
    }
}

要生成排列,只需调用permute(0);

【讨论】:

    【解决方案2】:

    如果您确切知道有多少个集合,并且数量很少,那么通常可以使用嵌套循环执行此操作。如果集合的数量大于 2 或 3,或者它是可变的,那么递归算法就开始有意义了。

    如果这是家庭作业,那么实现递归算法很可能是整个作业的目标。想想看,对于每一个集合,你都可以递归调用枚举函数,让它开始枚举下一个集合……

    【讨论】:

    • 这不是家庭作业。我也一直在考虑soln。作为基于递归的过程。让我看看我能不能想出这个。
    【解决方案3】:

    如果它们在容器中,只需遍历每个:

    #include <stdio.h>
    
    int main(void)
    {
        int set1[] = {1, 2, 3};
        int set2[] = {4, 5, 6};
        int set3[] = {7, 8, 9};
    
        for (unsigned i = 0; i < 3; ++i)
        {
            for (unsigned j = 0; j < 3; ++j)
            {
                for (unsigned k = 0; k < 3; ++k)
                {
                    printf("(%d, %d, %d)", set1[i], set2[j], set3[k]);
                }
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 这很简单 :) not 中的 Set 数量是固定的。
    • 基本上你会想要一些动态容器。我将举一个例子,但我的免责声明是我是 C++ 程序员,而不是 C 程序员。
    • 转念一想,giorgian 基本上读懂了我的想法。我将把它留在这里作为路人的简单解决方案。
    • 没问题。 “乔治”回答了我。非常感谢。
    【解决方案4】:

    通用解决方案:

    typedef struct sett
    {
      int* nums;
      int size;
    } t_set;
    
    
    inline void swap(t_set *set, int a, int b)
    {
      int tmp = set->nums[a];
      set->nums[a] = set->nums[b];
      set->nums[b] = tmp;
    }
    
    void permute_set(t_set *set, int from, void func(t_set *))
    {
      int i;
      if (from == set->size - 1) {
        func(set);
        return;
      }
      for (i = from; i < set->size; i++) {
        swap(set, from, i);
        permute_set(set, from + 1, func);
        swap(set, i, from);
      }
    }
    
    
    t_set* create_set(int size)
    {
      t_set *set = (t_set*) calloc(1, sizeof(t_set));
      int i;
      set->size = size;
      set->nums = (int*) calloc(set->size, sizeof(int));
      for(i = 0; i < set->size; i++)
        set->nums[i] = i + 1;
      return set;
    }
    
    void print_set(t_set *set) {
      int i;
      if (set) {
        for (i = 0; i < set->size; i++)
          printf("%d  ", set->nums[i]);
        printf("\n");
      }
    }
    
    int main(int argc, char **argv)
    {
      t_set *set = create_set(4);
      permute_set(set, 0, print_set);
    
    }
    

    【讨论】:

    • 它是一个指向函数的指针,它接受一个指向t_set 的指针并且什么都不返回。回调函数。
    • 非常感谢。这正是我需要的。你已经把这个编码了? :)
    • 哇! create_set() 中的错误检查在哪里?当他的操作系统内存不足时,您将如何向愤怒的客户解释段错误?
    • 另外,不要使用int 作为尺寸。 最少使用一些unsigned类型,最好使用size_t,因为它是设计用于存储大小的类型(任何其他类型可能不够大或太大,可能会导致问题)。
    • @chris:你是对的两次;请随时编辑代码。
    【解决方案5】:

    这是一个相当简单的迭代实现,您应该能够根据需要进行调整:

    #define SETSIZE 3
    #define NSETS 4
    
    void permute(void)
    {
        char setofsets[NSETS][SETSIZE] = {
            { 'a', 'b', 'c'},
            { 'd', 'e', 'f'},
            { 'g', 'h', 'i'},
            { 'j', 'k', 'l'}};
        char result[NSETS + 1];
        int i[NSETS]; /* loop indexes, one for each set */
        int j;
    
        /* intialise loop indexes */
        for (j = 0; j < NSETS; j++)
            i[j] = 0;
    
        do {
            /* Construct permutation as string */
            for (j = 0; j < NSETS; j++)
                result[j] = setofsets[j][i[j]];
            result[NSETS] = '\0';
    
            printf("%s\n", result);
    
            /* Increment indexes, starting from last set */
            j = NSETS;
            do {
                j--;
                i[j] = (i[j] + 1) % SETSIZE;
    
            } while (i[j] == 0 && j > 0);
        } while (j > 0 || i[j] != 0);
    }
    

    【讨论】:

    • Caf:在我在这里问这个问题之前,我没有能力花更多的时间去思考,我被困在了我必须增加索引的部分。使用 mod 运算符,你做对了。
    【解决方案6】:

    您可以将集合的元素视为循环计数器的值。 3 组意味着 3 个循环(如在 GMan answare 中),N 组意味着 N(模拟)循环:

    #include <stdlib.h>
    #include <stdio.h> 
    
    int set[3][2] = { {1,2}, {3,4}, {5,6} };
    
    void print_set( int *ndx, int num_rows ){
      for( int i=0; i<num_rows; i++ ) printf("%i ", set[i][ndx[i]] );
      puts("");
    }
    
    int main(){
      int num_cols = sizeof(set[0])/sizeof(set[0][0]);
      int num_rows = sizeof(set)/sizeof(set[0]);
      int *ndx = malloc( num_rows * sizeof(*ndx) );
    
      int i=0; ndx[i] = -1;
      do{
        ndx[i]++; while( ++i<num_rows ) ndx[i]=0;
        print_set( ndx, num_rows );
        while( --i>=0 && ndx[i]>=num_cols-1 );
      }while( i>=0 );
    }
    

    【讨论】:

      【解决方案7】:

      我能想到的最有效的方法(在 C# 中):

      string[] sets = new string[] { "abc", "def", "gh" };
      int count = 1;
      foreach (string set in sets)
      {
          count *= set.Length;
      }
      
      for (int i = 0; i < count; ++i)
      {
          var prev = count;
          foreach (string set in sets)
          {
              prev = prev / set.Length;
              Console.Write(set[(i / prev) % set.Length]);
              Console.Write(" ");
          }
      
          Console.WriteLine();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-05
        • 1970-01-01
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        • 2015-12-05
        • 1970-01-01
        • 2015-09-23
        相关资源
        最近更新 更多