从 C 中的递归函数中枚举并返回从二维数组中的 n 项中选择 k 的所有组合

Posted

技术标签:

【中文标题】从 C 中的递归函数中枚举并返回从二维数组中的 n 项中选择 k 的所有组合【英文标题】:Enumerate and return all the combinations of choosing k out of n items in a 2-dimensional array from a recursive function in C 【发布时间】: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写作。谢谢。 【参考方案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 糟糕。我通常包含我用来构建的命令行。忘记这一次。已编辑帖子以包含构建命令。

以上是关于从 C 中的递归函数中枚举并返回从二维数组中的 n 项中选择 k 的所有组合的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 面试题4 二维数组中的查找

知识分享:C/C++函数返回二维数组指针

递归分治算法之二维数组二分查找(Java版本)

剑指offer之二维数组中的查找

从 C 中的函数返回多维数组

C语言试题二十二之定义了3×3的二维数组,并在主函数中赋值。函数的功能使求出数组周边元素的平均值并作为函数值返回给主函数中的s。