函数总结所有排列

时间:2017-05-12 02:26:04

标签: c recursion permutation

前段时间我编写了一个程序,打印给定数组的所有可能排列,甚至打印所有部分数组:

    #define MAXARRAY 32
    #include <stdio.h>
    void combinations(int array[], int temp[], int start, int end,  int index, int r);

    void print_combinations(int array[], int n, int r){
    int temp[r];
    combinations(array, temp, 0, n-1, 0, r);
    }

    void combinations(int array[], int temp[], int start, int end, int index, int r){
    if (index == r){
        for (int j=0; j<r; j++)
            printf("%d ", temp[j]);
        printf("\n");
        return;
    }

    for (int i=start; i<=end && end-i+1 >= r-index; i++){
        temp[index] = array[i];
        combinations(array, temp, i+1, end, index+1, r);
    }
}

int main(){
    int array[MAXARRAY];
    int r;
    int n = sizeof(array)/sizeof(array[0]);
    int i=MAXarray, j;
    for(j=0;j<MAXARRAY;j++){
        array[j]=j+1;
    }
    for(r=0;r<=i;r++)
    print_combinations(array, n, r);    
}

现在我正在尝试将此程序转换为执行以下操作:

我想要总结所有排列并将总和与固定值进行比较,如果排列中的数字总和真的等于那个固定值,那么它最终会增加计数器,而不是打印排列。我可以检查多少排列等于该值。这就是我现在想出来的:

#define MAXARRAY 32
#include <stdio.h>
int combinations (int array[], int temp[], int start, int end,  int index, int r);

void print_combinations (int array[], int n, int r){
    int temp[r];
    combinations(array, temp, 0, n-1, 0, r);
}

int combinations (int array[], int temp[], int start, int end, int index, int r){
    int sum=0, counter=0;
    if (index == r) {
        for (int j=0; j<r; j++){
            sum=sum+temp[j];
            }
        if(sum==264){
        counter++;
        }
    }
    for (int i=start; i<=end && end-i+1 >= r-index; i++){
        temp[index] = array[i];
        combinations(array, temp, i+1, end, index+1, r);
    }
    return counter;
}


int main()
{
    int array[MAXARRAY];
    int r;
    int n = sizeof(array)/sizeof(array[0]);
    int i=MAXARRAY, j;
    for(j=0;j<MAXARRAY;j++){
        array[j]=j+1;
    }
    for(r=0;r<=i;r++)
    print_combinations(array, n, r);

我不知道如何正确地改变它以获得我想要的东西,正是我有点迷失了如何切换void函数来打印一个没有出现在函数中的计数器,我不确定是否我可以轻松地“改变”这段代码以获得我想要的东西,或者我只需要编写全新的函数。

1 个答案:

答案 0 :(得分:1)

您想知道有多少种方法可以从给定的集合中选择数字,以便它们总结到给定的目标值。你似乎以错误的方式接近这个,因为你混淆了排列和组合。

Permutations是具有固定大小n的一组项目的不同排列,如果所有项目都不同,则可能的排列数量为n!。这在这里没用,因为求和是可交换的;操作数的顺序无关紧要。

Combinations告诉你哪些项目包括哪些项目,哪些不包含。这就是你想要的。幸运的是,只有2种可能的方法从一组 n 中选择项目,包括所有项目或没有项目。

你也可以递归地解决这个问题。每个级别的递归都会处理一个项目,您可以选择是否包含它。对于您的项目,您将获得以下决策树:

                                  0
                                /   \
                              0       1
                             / \     / \
                            0   2   0   2
                           / \ / \ / \ / \
                           0 3 0 3 0 3 0 3

                 sum       0 3 2 5 1 4 3 6

取左边的分支省略一个项目,然后选择右边的分支来包含它。这将给你3次的总和以及所有其他的0到6之和,包括一次。有8条可能的路径。

以下程序可以:

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

#define N 32
#define TARGET 264

/*
 *      Print the summands
 */
void print(const int a[], int n)
{
    int i;

    for (i = 0; i < n; i++) {
        if (i) printf(" + ");
        printf("%d", a[i]);
    }

    puts("");
}

/*
 *      Actual recursive combination function
 */
size_t combine_r(const int pool[],      // summand pool
                 int res[],             // currently included items
                 int max,               // length of pool
                 int n,                 // length of res
                 int i,                 // current item's index in pool
                 int sum,               // running sum
                 int target)            // desired target
{
    int count = 0;

    if (i == max) {
        if (sum == target) {
            //print(res, n);
            count++;
        }
    } else {
        count += combine_r(pool, res, max, n, i + 1, sum, target);

        res[n++] = pool[i];
        count += combine_r(pool, res, max, n, i + 1,
            sum + pool[i], target);
    }

    return count;
}

/*
 *      Interface function for the recursive function.
 */
size_t combine(const int pool[], int n, int target)
{
    int res[n];

    return combine_r(pool, res, n, 0, 0, 0, target);
}



int main()
{
    int pool[N];
    size_t n;
    int i;

    for (i = 0; i < N; i++) pool[i] = i + 1;

    n = combine(pool, N, TARGET);
    printf("%zu combinations.\n", n);

    return 0;
}

如果总和等于目标,则函数沿着每条路径向下并记录命中。从递归返回并返回树时,返回每个子树中的命中数,以便根级别获得总命中数。

函数combine只是实际递归函数的前端,因此您不必从main传递这么多的零。递归函数的参数可能会更加优雅地减少和组织。 (它们的存在只是因为在C中你必须传递一个数组的长度。如果你只是想计算可能性,你可以摆脱resn,这只是为了服务于打印数组。)