C++ 新手需要帮助打印整数组合
Posted
技术标签:
【中文标题】C++ 新手需要帮助打印整数组合【英文标题】:C++ Newbie needs helps for printing combinations of integers 【发布时间】:2010-12-24 23:47:05 【问题描述】:假设给定我:
-
整数范围
iRange
(即从1
到iRange
)和
所需的组合数量
我想找出所有可能组合的数量并打印出所有这些组合。
例如:
给定:iRange = 5
和 n = 3
那么组合数为iRange! / ((iRange!-n!)*n!) = 5! / (5-3)! * 3! = 10
组合,输出为:
123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345
另一个例子:
给定:iRange = 4
和 n = 2
那么组合数为iRange! / ((iRange!-n!)*n!) = 4! / (4-2)! * 2! = 6
组合,输出为:
12 - 13 - 14 - 23 - 24 - 34
到目前为止我的尝试是:
#include <iostream>
using namespace std;
int iRange= 0;
int iN=0;
int fact(int n)
if ( n<1)
return 1;
else
return fact(n-1)*n;
void print_combinations(int n, int iMxM)
int iBigSetFact=fact(iMxM);
int iDiffFact=fact(iMxM-n);
int iSmallSetFact=fact(n);
int iNoTotComb = (iBigSetFact/(iDiffFact*iSmallSetFact));
cout<<"The number of possible combinations is: "<<iNoTotComb<<endl;
cout<<" and these combinations are the following: "<<endl;
int i, j, k;
for (i = 0; i < iMxM - 1; i++)
for (j = i + 1; j < iMxM ; j++)
//for (k = j + 1; k < iMxM; k++)
cout<<i+1<<j+1<<endl;
int main()
cout<<"Please give the range (max) within which the combinations are to be found: "<<endl;
cin>>iRange;
cout<<"Please give the desired number of combinations: "<<endl;
cin>>iN;
print_combinations(iN,iRange);
return 0;
我的问题:
我的代码中与组合打印相关的部分仅适用于n = 2, iRange = 4
,我无法使其普遍适用,即任何n
和iRange
。
【问题讨论】:
几点-请以问题的形式提出您的要求。另外,想想你在问什么。我相信您正在寻找的是一种置换算法(iRange P n),它是从“iRange”值中选择的“n”个数字的所有组合。 在您的示例输出中:123 - 124 - 125 - 134 - 135 - 142 - 145 - 234 - 245 - 345
,您有124
和142
,看起来像一个排列。一个组合应该让你:123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345
.
我要求的是帮助/想法/指导来更改 for 循环,以便我的代码适用于 n 和 iRange 的任何值,并像示例中那样产生输出。我认为也许 for 循环应该被另一个递归函数替换,或者将当前函数转换为递归函数。但我想不出递归的基本情况。
@Mike DeSimone 谢谢我刚刚纠正了它。
next_combination
在 Google 中的第一次点击是不错的读物:sites.google.com/site/hannuhelminen/next_combination
【参考方案1】:
我创建了一个类似于next_permutation()
的next_combination()
函数,但需要有效输入才能使其工作
//nums should always be in ascending order
vector <int> next_combination(vector<int>nums, int max)
int size = nums.size();
if(nums[size-1]+1<=max)
nums[size-1]++;
return nums;
else
if(nums[0] == max - (size -1))
nums[0] = -1;
return nums;
int pos;
int negate = -1;
for(int i = size-2; i>=0; i--)
if(nums[i]+1 <= max + negate)
pos = i;
break;
negate --;
nums[pos]++;
pos++;
while(pos<size)
nums[pos] = nums[pos-1]+1;
pos++;
return nums;
【讨论】:
【参考方案2】:这是我的 C++ 函数,具有不同的接口(基于 sts::set)但执行相同的任务:
typedef std::set<int> NumbersSet;
typedef std::set<NumbersSet> CombinationsSet;
CombinationsSet MakeCombinations(const NumbersSet& numbers, int count)
CombinationsSet result;
if (!count) throw std::exception();
if (count == numbers.size())
result.insert(NumbersSet(numbers.begin(), numbers.end()));
return result;
// combinations with 1 element
if (!(count - 1) || (numbers.size() <= 1))
for (auto number = numbers.begin(); number != numbers.end(); ++number)
NumbersSet single_combination;
single_combination.insert(*number);
result.insert(single_combination);
return result;
// Combinations with (count - 1) without current number
int first_num = *numbers.begin();
NumbersSet truncated_numbers = numbers;
truncated_numbers.erase(first_num);
CombinationsSet subcombinations = MakeCombinations(truncated_numbers, count - 1);
for (auto subcombination = subcombinations.begin(); subcombination != subcombinations.end(); ++subcombination)
NumbersSet cmb = *subcombination;
// Add current number
cmb.insert(first_num);
result.insert(cmb);
// Combinations with (count) without current number
subcombinations = MakeCombinations(truncated_numbers, count);
result.insert(subcombinations.begin(), subcombinations.end());
return result;
【讨论】:
【参考方案3】:这是您使用 recursive 解决方案编辑的代码 :D :D:
#include <iostream>
int iRange=0;
int iN=0; //Number of items taken from iRange, for which u want to print out the combinations
int iTotalCombs=0;
int* pTheRange;
int* pTempRange;
int find_factorial(int n)
if ( n<1)
return 1;
else
return find_factorial(n-1)*n;
//--->Here is another solution:
void print_out_combinations(int *P, int K, int n_i)
if (K == 0)
for (int j =iN;j>0;j--)
std::cout<<P[j]<<" ";
std::cout<<std::endl;
else
for (int i = n_i; i < iRange; i++)
P[K] = pTheRange[i];
print_out_combinations(P, K-1, i+1);
//Here ends the solution...
int main()
std::cout<<"Give the set of items -iRange- = ";
std::cin>>iRange;
std::cout<<"Give the items # -iN- of iRange for which the combinations will be created = ";
std::cin>>iN;
pTheRange = new int[iRange];
for (int i = 0;i<iRange;i++)
pTheRange[i]=i+1;
pTempRange = new int[iN];
iTotalCombs = (find_factorial(iRange)/(find_factorial(iRange-iN)*find_factorial(iN)));
std::cout<<"The number of possible combinations is: "<<iTotalCombs<<std::endl;
std::cout<<"i.e.the combinations of "<<iN<<" elements drawn from a set of size "<<iRange<<" are: "<<std::endl;
print_out_combinations(pTempRange, iN, 0);
return 0;
【讨论】:
【参考方案4】:看起来是一个很好的递归问题。
定义一个函数f(prefix, iMin, iMax, n)
,它打印[iMin
,iMax
]范围内n
数字的所有组合,并返回组合的总数。对于n
= 1,它应该打印从iMin
到iMax
的每个数字并返回iMax - iMin + 1
。
对于您的iRange = 5
和n = 3
案例,请致电f("", 1, 5, 3)
。输出应该是123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345
。
请注意,第一组输出只是在f("", 2, 5, 2)
的输出上加上前缀1
,即f("1", 2, 5, 2)
,然后是f("2", 3, 5, 2)
和f("3", 4, 5, 2)
。看看你会如何用循环来做到这一点。在这之间,上面n
= 1 的情况,以及错误输入的陷阱(最好是什么都不打印并返回 0,它应该会简化你的循环),你应该可以写 f()
。
我停下来,因为这看起来像是一项家庭作业。这足以让您入门吗?
编辑:只是为了咯咯笑,我写了一个 Python 版本。 Python 可以更轻松地处理事物的集合和列表并保持清晰。
#!/usr/bin/env python
def Combos(items, n):
if n <= 0 or len(items) == 0:
return []
if n == 1:
return [[x] for x in items]
result = []
for k in range(len(items) - n + 1):
for s in Combos(items[k+1:], n - 1):
result.append([items[k]] + s)
return result
comb = Combos([str(x) for x in range(1, 6)], 3)
print len(comb), " - ".join(["".join(c) for c in comb])
请注意,Combos()
不关心 items
列表中项目的类型。
【讨论】:
谢谢,我会试一试,让你知道。它没有家庭作业,我正在使用一本书学习 C++ 编码。我正在尝试编写更大的代码,而这只是一小部分。我想遍历 mxm 2D 迷宫的行,但不是“一次一个”,而是取迷宫行的“子集”,即考虑一些“空/不感兴趣”行,我想“扫描”迷宫行。我的想法是创建行子集的组合,然后扫描这些子集。也许是错误的想法,有更好的选择:d 我认为迷宫路径是一个经典的递归问题。祝你好运。【参考方案5】:这是一个简单的递归解决方案的示例。如果你用循环替换递归,我相信会有一个更优化的实现。这可能是你的作业:)
#include <stdio.h>
const int iRange = 9;
const int n = 4;
// A more efficient way to calculate binomial coefficient, in my opinion
int Cnm(int n, int m)
int i;
int result = 1;
for (i = m + 1; i <= n; ++i)
result *= i;
for (i = n - m; i > 1; --i)
result /= i;
return result;
print_digits(int *digits)
int i;
for (i = 0; i < n; ++i)
printf("%d", digits[i]);
printf("\n");
void plus_one(int *digits, int index)
int i;
// Increment current digit
++digits[index];
// If it is the leftmost digit, run to the right, setup all the others
if (index == 0)
for (i = 1; i < n; ++i)
digits[i] = digits[i-1] + 1;
// step back by one digit recursively
else if (digits[index] > iRange)
plus_one(digits, index - 1);
// otherwise run to the right, setting up other digits, and break the recursion once a digit exceeds iRange
else
for (i = index + 1; i < n; ++i)
digits[i] = digits[i-1] + 1;
if (digits[i] > iRange)
plus_one(digits, i - 1);
break;
int main()
int i;
int digits[n];
for (i = 0; i < n; ++i)
digits[i] = i + 1;
printf("%d\n\n", Cnm(iRange, n));
// *** This loop has been updated ***
while (digits[0] <= iRange - n + 1)
print_digits(digits);
plus_one(digits, n - 1);
return 0;
【讨论】:
感谢您的回答。我试过你的代码,它编译但是给出了错误的结果:例如对于:常量 int iRange = 5;常量 int n = 2;输出---> 10 13 14 15 23 24 25 34 35 45 我认为缺少一个解决方案 对不起,我搞砸了主循环。它的逻辑现在是固定的——一旦第一个数字等于 (iRange - n + 1),这是最后一次打印数字,因为对 plus_one() 的以下调用将增加每个数字。【参考方案6】:您的解决方案仅适用于 n=2。考虑使用具有 n 个整数的数组(梳子),然后循环将勾选数组中的最后一项。当该项目达到最大更新时,然后组合 [n-2] 项目并将最后一个项目设置为上一个值 +1。
基本上像时钟一样工作,但您需要逻辑来找到要上升的内容以及下一个最小值是什么。
【讨论】:
以上是关于C++ 新手需要帮助打印整数组合的主要内容,如果未能解决你的问题,请参考以下文章