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

Every combination of X numbers possible if we select one value from each of Y vectors

本文关键字:数字 组合 向量 我们 选择 如果 一个      更新时间:2023-10-16

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

我试图使问题尽可能简单,如果更多详细信息会有所帮助,请告诉我。

假设我们有四个 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

我陷入困境的地方是生成每一个可能的组合。我需要每个可能的 N 长度数字,可以通过组合 4 个源向量中的每一个中的一个 int 来创建。

谢谢!

你确实在描述你的集合的笛卡尔积。就像普通的数字乘积一样,这是对两个事物的操作,但它可以连续应用于两个以上的事物:

A x B x C x D

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

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

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

一个。。。

它计算两件事的笛卡尔积: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;
}
}

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

将索引配置视为数字,由不同数字系统中的数字组成。 然后考虑计算一组索引,就像在某个数字系统中计算常规数字一样。 例如,对于大小为 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机械师有点困扰我,看起来并不优雅。如果有人有改进这一点的想法,我很想听听。