前段时间我编写了一个程序,打印给定数组的所有可能排列,甚至打印所有部分数组:
#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函数来打印一个没有出现在函数中的计数器,我不确定是否我可以轻松地“改变”这段代码以获得我想要的东西,或者我只需要编写全新的函数。
答案 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中你必须传递一个数组的长度。如果你只是想计算可能性,你可以摆脱res
和n
,这只是为了服务于打印数组。)