C++ 新手需要帮助打印整数组合

Posted

技术标签:

【中文标题】C++ 新手需要帮助打印整数组合【英文标题】:C++ Newbie needs helps for printing combinations of integers 【发布时间】:2010-12-24 23:47:05 【问题描述】:

假设给定我:

    整数范围iRange(即从1iRange)和 所需的组合数量

我想找出所有可能组合的数量并打印出所有这些组合。

例如:

给定iRange = 5n = 3

那么组合数为iRange! / ((iRange!-n!)*n!) = 5! / (5-3)! * 3! = 10组合,输出为:

123 - 124 - 125 - 134 - 135 - 145 - 234 - 235 - 245 - 345

另一个例子:

给定iRange = 4n = 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,我无法使其普遍适用,即任何niRange

【问题讨论】:

几点-请以问题的形式提出您的要求。另外,想想你在问什么。我相信您正在寻找的是一种置换算法(iRange P n),它是从“iRange”值中选择的“n”个数字的所有组合。 在您的示例输出中:123 - 124 - 125 - 134 - 135 - 142 - 145 - 234 - 245 - 345,您有124142,看起来像一个排列。一个组合应该让你: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),它打印[iMiniMax]范围内n数字的所有组合,并返回组合的总数。对于n = 1,它应该打印从iMiniMax 的每个数字并返回iMax - iMin + 1

对于您的iRange = 5n = 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++ 新手需要帮助打印整数组合的主要内容,如果未能解决你的问题,请参考以下文章

C++ 嵌套 for 循环,用于设置基数/指数的指数

遍历整数数组所有组合的算法

需要有关 C++ 中二维数组的动态内存分配的帮助

c++中对象的组合

正整数 uniq 的组合(顺序不重要)

如何在向量向量上使用lower_bound?