【问题标题】:Combinations in a range of numbers in C++C++中一系列数字的组合
【发布时间】:2015-12-30 01:25:14
【问题描述】:

我想构建一个 C++ 程序,根据 N 因子所采用的元素数量显示所有可能的组合。

让我们假设一个向量 vec[6] 上面有元素 1 2 3 4 5 6。
使用组合公式,6! /4! (6 - 4)! = 15 种可能性
我想生成一个函数,它给出所有 15 种可能性的结果,4 乘 4 不重复,例如:
1 2 3 4
1 2 3 5
1 2 3 6
2 3 4 5
等等……

我现在正在使用此代码,但我想使用矢量 (v[6]) 中的数字。

#include <algorithm>
#include <iostream>
#include <string>

void comb(int N, int K)
{
    std::string bitmask(K, 1); // K leading 1's
    bitmask.resize(N, 0); // N-K trailing 0's

    // print integers and permute bitmask
    do {
        for (int i = 0; i < N; ++i) // [0..N-1] integers
        {
            if (bitmask[i]) std::cout << " " << i;
        }
        std::cout << std::endl;
    } while (std::prev_permutation(bitmask.begin(), bitmask.end()));
}

int main()
{
    comb(6, 4);
}

请大家帮帮我好吗?我想知道我可以在哪里更改代码,以便我可以使用自己的向量。

我正在生成这个向量 v[i] 并使用冒泡排序对其进行排序,如下所示:

void order (int d[], int n){
    int i, j;
    for (i = 1; i < n; i++)
        for (j = 0; j < n-1; j++)
            if (d[j] > d[j+1])
                swap (d[j],d[j+1]);

    for (i = 0; i < n; i++)
        cout << d[i] << " ";
}

在排序之后,我想将我的向量放入梳子函数中。 我怎样才能使它成为可能?

【问题讨论】:

  • std::cout &lt;&lt; " " &lt;&lt; v[i]替换std::cout &lt;&lt; " " &lt;&lt; i?
  • 我的意图是让一个函数传递一个数字向量而不是数字,就像这样:void order(int d[], int K){}

标签: c++ combinations combinatorics


【解决方案1】:

这是一个使用 free, open source library 来完成工作的 C++14 解决方案:

#include "combinations"
#include <iomanip>
#include <iostream>
#include <vector>

int
main()
{
    std::vector<int> v{1, 2, 3, 4, 5, 6};
    int state = 0;
    for_each_combination(v.begin(), v.begin() + 4, v.end(),
        [&state](auto first, auto last)
        {
            std::cout << std::setw(2) << ++state << " : ";
            while (true)
            {
                std::cout << *first;
                if (++first == last)
                    break;
                std::cout << ' ';
            }
            std::cout << '\n';
            return false;
        });
}

这个输出:

 1 : 1 2 3 4
 2 : 1 2 3 5
 3 : 1 2 3 6
 4 : 1 2 4 5
 5 : 1 2 4 6
 6 : 1 2 5 6
 7 : 1 3 4 5
 8 : 1 3 4 6
 9 : 1 3 5 6
10 : 1 4 5 6
11 : 2 3 4 5
12 : 2 3 4 6
13 : 2 3 5 6
14 : 2 4 5 6
15 : 3 4 5 6

库的源代码可以从上面的链接复制/粘贴,并检查它是如何工作的。与使用 std::prev_permutation 的解决方案相比,此库非常高性能。此函数的实现相对简单,但该库还包含更多功能,这些功能的实现变得越来越复杂(但同样易于使用):

template <class BidirIter, class Function>
Function
for_each_combination(BidirIter first,
                     BidirIter mid,
                     BidirIter last,
                     Function f);

template <class BidirIter, class Function>
Function
for_each_permutation(BidirIter first,
                     BidirIter mid,
                     BidirIter last,
                     Function f);

template <class BidirIter, class Function>
Function
for_each_reversible_permutation(BidirIter first,
                                BidirIter mid,
                                BidirIter last,
                                Function f);

template <class BidirIter, class Function>
Function
for_each_circular_permutation(BidirIter first,
                              BidirIter mid,
                              BidirIter last,
                              Function f);

template <class BidirIter, class Function>
Function
for_each_reversible_circular_permutation(BidirIter first,
                                         BidirIter mid,
                                         BidirIter last,
                                         Function f);

该库有几个令人愉快的功能,包括:

  • 您的输入序列(vector 或其他)不需要排序。
  • 您可以随时通过返回 true 提前退出循环。
  • 如果您不及早跳出循环,则序列始终会返回到其原始状态。
  • 仿函数总是接收到序列的第一个 k 元素的迭代器,因此如果您告诉仿函数序列的总长度,也可以对元素未选择进行操作.

您可以按原样使用该库,或者学习并从其实现中获取您需要的内容。上面的链接包含类似教程的描述,以及每个功能的详细说明。

【讨论】:

  • 这是为了标准化而提出的,还是在您的待办事项清单上?
  • 尚未提出标准化。它大约在 5 年前提交给 LWG。我不确定它是否通过了“广泛需要”的测试。我也不确定如何指定它以使其 快速。如果不是比 next_permutation 快几个数量级,这些函数也毫无用处。
【解决方案2】:

从子集 S = {1,2,3,...,k} 开始,这是第一个子集。通过检查右边的元素(从最后一个开始)生成下一个子集,如果可以的话(如果它

{1,2,3}, {1,2,4}, {1,2,5}, {1,3,4}, {1,3,5}, {1,4,5} , {2,3,4}, {2,3,5}, {2,4,5}, {3,4,5}

#include <iostream>
#include <vector>

std::ostream& operator<<(std::ostream& o, std::vector<int>& a)
{
    o << "{";
    for (std::vector<int>::const_iterator it = a.begin(); it != a.end(); ++it) {
        o << *it;
        if (it + 1 < a.end()) o << ",";
    }
    return o << "}";
}

int main()
{
    const int N = 7;
    const int k = 4;

    std::vector<int> A(k);

    // initialize
    for (int i = 0; i < k; ++i) {
        A[i] = i + 1;
    }

    std::cout << A << std::endl;
    int h = 0;
    bool done = false;
    do {
        ++A[k-h-1];
        for (int t = k - h; t < k; ++t) {
            A[t] = A[t-1] + 1;
        }
        if (A[k-h-1] < N - h) {
            // last element can be incremented, stay there...
            h = 0;
        } else {
            // last element at max, look back ...
            ++h;
        }
        done = (A[0] == N - k + 1);
        std::cout << A << std::endl;    
    } while (!done);
}

【讨论】:

    【解决方案3】:

    非常简单的递归实现:

    struct Combs
    {
        vector<int> scombs;
    
        template <typename F>
        void call_combs(int n, int k, F f)
        {
            if (k == 0) {
                f();
            }
            else {
                scombs.push_back(n - 1);
                call_combs(n - 1, k - 1, f);
                scombs.resize(scombs.size() - 1);
                if (k < n) {
                    call_combs(n - 1, k, f);
                }
            }
        }
    };
    
    ...
    
        Combs combs;
        const auto& coco = combs.scombs;
        combs.call_combs(6, 4, [&coco](){
            copy(coco.cbegin(), coco.cend(), ostream_iterator<int>(cout));
            cout << endl;
        });
    

    【讨论】:

      猜你喜欢
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多