如果我们从每个Y向量中选择一个值,则X数的每个组合都是可能的

时间:2017-07-25 22:49:22

标签: c++ cartesian-product

如果这个问题有正式的名称(或者这是重复的,我还没有找到正确的问题),那么指出我应该搜索的内容也将不胜感激!但是,我还没有找到任何关于这个特定问题的讨论或方法。

我试图让问题尽可能简单,如果有更多细节可以让我知道。

假设我们有四个int的随机长度向量:

std::vector<int> v1 {1, 7, 5, 2};
std::vector<int> v2 {4, 2, 1};
std::vector<int> v3 {1, 9, 4, 6, 4, 1, 2};
std::vector<int> v4 {9, 4};

在这四个向量中,我需要生成每个可能的组合,其中我们一次只从每个源向量(v1-v4)中选择一个int。结果应该是我们从源向量中生成每个可能的N长度数,其中N =源向量的数量(在这种情况下为4)。

很容易生成一些可能的组合,例如只选择每个源向量中的第一个数字:

1 4 1 9

选择每个源向量的最后一个数字:

2 1 2 4

我被困的地方正在产生每一种可能的组合。我需要通过组合来自4个源向量中的每一个的一个int来创建每个可能的N长度数。

谢谢!

2 个答案:

答案 0 :(得分:1)

以下是任意数量的输入向量的答案:

将索引配置视为数字,由不同数字系统中的数字组成。 然后考虑计算一组索引,就像在某个数字系统中计算常规数字一样。 对于四个大小为3 5 2 5的向量,例如,索引集0 2 1 4将导致0 3 0 0。

对于下面的代码,我假设你的向量是在另一个向量中给出的,即vector<vector<int> >

这里有一个索引类:

class Indices {

private:
    vector<size_t> indices;
    vector<size_t> maximal_sizes;
    bool _max_reached;

public:

    Indices(const vector<vector<int> >& vectors) : indices(vectors.size(), 0), maximal_sizes(vectors.size()), _max_reached(false) {

        for(size_t i = 0; i < vectors.size(); i++){
            maximal_sizes[i] = vectors[i].size();
        }
    }

    const size_t& operator[] (const size_t i) const { return indices[i]; }
    bool max_reached() const { return max_reached; }

    void count_up() {

        size_t current = 0;
        while(current < indices.size()){
            indices[current]++;
            if(indices[current] >= maximal_sizes[current]){
                indices[current] = 0;
                current++;
            } else {
                return;
            }
        }
        _max_reached = true;
    }

}

(将其拆分为标题和来源)

并像

一样使用它
inline void print(const vector<vector<int> >& vectors, const Indices& indices){

    for(size_t i = 0; i < vectors.size(); i++){
        cout << vectors[i][Indices[i]] << " ";
    }
    cout << endl;
}

void all_combinations(const vector<vector<int> >& vectors){

    Indices indices(vectors);

    while(not indices.max_reached()){
        print(vectors, indices);
        indices.count_up();
    }
}

(可能不是完全没有这样的功能,但是你明白了。另外,Indices我认为不够具有描述性,但现在想不出一个好的短名称.max_reached机制是有点烦我,看起来并不优雅。如果有人有改进的想法,我很乐意听到。)

答案 1 :(得分:1)

您确实在描述集合的Cartesian product。就像普通的数字产品一样,这是对两件事的操作,但它可以连续应用于两件以上的事情:

  

A x B x C x D = A x(B x(C x D))

这种结构是理解问题的关键。您可以将其分解为每个仅涉及两组的递归子问题。这样你就可以处理任意数量的集合。

解决问题的程序也可以是递归的,也可以是迭代的。以下是使用由vector<int>表示的lists集合的半优化递归解决方案。给出一组集

  

A,......

它计算两件事的笛卡尔乘积:A和其余集合的笛卡尔乘积:

  

A x(...)

您可以重新排列此过程以从基本情况开始向上,即((D x C)x B)x A.这样您就可以使用循环而不是递归调用。但是考虑问题的递归结构是组织思想的好方法。

void cartesian_product( list< vector<int> > sets, list< vector<int> >& product) {
        if ( sets.empty() )
                return;

        // get first set
        auto& s = sets.front();

        // base case
        if ( ++begin( sets ) == end( sets ) ) {
                for ( auto k : s ) {
                        product.push_back( { k } );
                }
                return;
        }

        // get cartesian product of the rest of the sets
        cartesian_product( list< vector<int> >( ++begin( sets ), end( sets ) ), product );

        // make additional copies of product and append each element of first set to them
        list< vector<int> > additions;
        for ( auto k = ++begin( s ); k != end( s ); ++k ) {
                list< vector<int> > ad = product;
                for ( auto& x : ad ) {
                        x.push_back( *k );
                }
                additions.splice( end( additions ), ad );
        }

        // append the first element of the first set to the original product
        for ( auto& x : product ) {
                x.push_back( s.front() );
        }

        // append the additions to the final product
        product.splice( end( product ), additions );

        return;
}

以下是该计划的其余部分:

#include <cassert>
#include <iostream>
#include <list>
#include <vector>

using std::vector;
using std::list;
using std::cout;
using std::endl;

void cartesian_product( list< vector<int> > sets, list< vector<int> > &product);

int main( int argc, char *argv[] ) {
        list< vector<int> > sets = {
                {1, 7, 5, 2},
                {4, 2, 1},
                {1, 9, 4, 6, 4, 1, 2},
                {9, 4}
        };

        list< vector<int> > product;
        cartesian_product( sets, product );

        for ( auto& x : product ) {
                cout << "{ ";
                for ( auto k : x ) {
                        cout << k << " ";
                }
                cout << "}" << endl;
        }
}