【问题标题】:Function to sum all permutations对所有排列求和的函数
【发布时间】:2017-05-12 02:26:03
【问题描述】:

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

    #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 函数以打印未出现在函数中的计数器有点迷茫,我不确定是否我可以轻松地“修改”这段代码以获得我想要的,或者我只需要编写全新的函数。

【问题讨论】:

  • 我不明白,如果您编写的代码可以计算所有排列,则无需转换任何内容。只计算每个排列的总和?
  • 为了澄清,首先代码打印所有排列。我需要第二个代码来计算所有先前打印的排列的总和。
  • 举第二个例子...
  • 请提供有关输入数据和限制的更多信息,这似乎是适用于 N 而非 N 的非递归任务!
  • 输入是从1到32的一系列数字,(1,2,3,4...31,32) 没有限制,根本不需要高效,我只需要一个一次性的答案。它不会在任何更大的程序中运行(目前)。

标签: c recursion permutation


【解决方案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,它们只是用来打印数组。 )

【讨论】:

    猜你喜欢
    • 2018-10-07
    • 2019-07-31
    • 1970-01-01
    • 1970-01-01
    • 2017-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多