【问题标题】:Enumerate and return all the combinations of choosing k out of n items in a 2-dimensional array from a recursive function in C从 C 中的递归函数中枚举并返回从二维数组中的 n 项中选择 k 的所有组合
【发布时间】:2013-09-13 08:26:15
【问题描述】:

我正在使用递归函数(来自此处的一篇文章)来枚举从 n 中选择 k 项的所有组合。我已经修改了这个函数以保存并返回二维数组中的枚举组合(作为 arrPtr 传递给函数)。我在 for 循环(从 main)中为不同的 k 值(k 从 1 到 n-1)调用这个递归函数,以生成 n 和 k 的任何值的所有组合。现在,将“count”定义为静态整数,该函数生成 k=1 的所有组合,然后转到 k=2,然后在某一点停止。原因是我使用变量“count”作为 arrPtr 中行的索引。由于它是一个静态变量,因此在其他轮次(k=2,3,4 等)调用该函数时它不会重置为 0。因此,在某个点之后会导致 arrPtr 的访问冲突。当我删除 'count' 的 'static' 时,它会生成不同 k 值的所有组合,但只有每轮中的最后一个组合保存在 arrPtr 中(同样是由于删除了 'static')。如何将每个生成的组合保存在 arrPtr 中的一行中,以便我可以获取(并返回)保存在 arrPtr 最后指向的一个位置的所有组合?

我尝试使用按引用传递(传递变量的地址)将 arrPtr 中的行的索引传递给函数,但是当递归函数调用自身时会遇到麻烦。 我搜索了很多,在这里找到了类似的主题(例如,从递归函数返回数组),但它们主要用于其他编程语言(我只使用 C;甚至没有 C++)。我花了很多时间来解决这个问题,现在真的需要帮助。先感谢您。

int** nCk(int n,int loopno,int ini,int *a,int **arrPtr, int k)
{
static int count=0;

int total; // equal to the total number of combinations of nCk
int i,j;

total = factorial(n)/(factorial(n-k)*factorial(k)); 

loopno--;
if(loopno<0)
{
    a[k-1]=ini;

    for(j=0;j<k;j++)
    {
    printf("%d,",a[j]);
    arrPtr[count][j]=a[j];
    }
    printf("count =%d\n",count);

    count++;

    return 0;
}

for(i=ini;i<=n-loopno-1;i++)
{
    a[k-1-loopno]=i+1;
    nCk(n,loopno,i+1,a,arrPtr,k);
}

if(ini==0)

return arrPtr; // arrPtr is in fact an array of pointers, where each pointer points to an array of size k (one of the combinations of selecting k out of n elements

else
return 0;
}

【问题讨论】:

  • 这个问题很难看。你能想出一个SSCCE 来说明同样的问题,除了更简单的解释吗?
  • 哦,对不起。之所以长,是因为我尽量解释清楚,说清楚。如果有机会,我会尝试按照SSCCE写作。谢谢。

标签: c arrays recursion combinations


【解决方案1】:

我理解的是

您想计算 nCk 中 n 和 k 的任意值的组合,

在外面定义一个 factorial() 函数 定义一个combi()函数...计算n和k变量的组合值

这两个函数在定义 main() 函数之前...这样你就可以避免声明然后定义(我的意思是避免额外的代码行)。

这里是combi()函数的代码

function combi(int n , int k){
int nFact, kFact, n_kFact, p;
int comb;
nFact=factorial(n);
kFact=factorial(k);
p=n-k;
n_kFact=factorial(p);
comb= nFact / ((n_kFact) * kFact);
return comb;
}

你可以在你的 main 函数中调用这个函数 .... 使用 for 循环来存储相对 n 和 k 的组合值 .... 这样你会得到你需要的 .... 也可以传递指针或

&array[0][0]

即数组的起始地址...以便您可以在程序中的任何位置访问该数组。

希望这对您有所帮助。谢谢

【讨论】:

  • 非常感谢您的帮助。我很抱歉没有使用正确的术语。我不应该说nCk!我正在寻找的是枚举任何 n 和 k 值的所有组合。再次感谢。
【解决方案2】:

GCC 4.7.3:gcc -Wall -Wextra -std=c99 enum-nck.c

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// Textbook recursive definition of, n-choose-k.
int nCk(int n, int k) {
  assert(0 < k && k <= n);
  if (k == n) { return 1; }
  if (k == 1) { return n; }
  return nCk(n - 1, k) + nCk(n - 1, k - 1);
}

// But you asked for a procedure to enumerate all the combinations.
void aux_enum_nCk(int n, int k, int* all, int* j, int a[], int i) {
  a[i] = n;
  if (i == k - 1) {
    memcpy(&all[*j], &a[0], k * sizeof(int));
    *j += k;
    return;
  }
  for (int c = n - 1; c > 0; --c) {
    aux_enum_nCk(c, k, all, j, a, i + 1);
  }
}

void enum_nCk(int n, int k, int* arr) {
  assert(0 < k && k <= n);
  int j = 0;
  int a[k];
  for (int i = 0; i < k; ++i) { a[i] = 0; }
  for (int c = n; c >= n - k - 1; --c) {
    aux_enum_nCk(c, k, arr, &j, a, 0);
  }
}

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

  printf("%d choose %d = %d\n", n, k, x);

  int arr[x][k];

  enum_nCk(n, k, &arr[0][0]);

  for (int i = 0; i < x; ++i) {
    for (int j = 0; j < k; ++j) {
      printf("%d ", arr[i][j]);
    }
    printf("\n");
  }

  return 0;
}

【讨论】:

  • 非常感谢您的帮助。但是,为了更好地理解您的解决方案,我尝试编译并运行您的代码,但出现错误。 (我使用的是 Microsoft Visual Studio 2008)。你有什么主意吗?是的,没错。我需要列举所有的组合。很抱歉使用了错误的术语。谢谢。
  • 可能是因为它是 C++,而不是 C。试试cpp enumerate.c -o enumerate
  • 糟糕。我通常包含我用来构建的命令行。忘记这一次。已编辑帖子以包含构建命令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
  • 2011-07-09
  • 1970-01-01
相关资源
最近更新 更多