我需要对从1
到的所有可能的数字集进行编程,以N
获取任意数量m
的整数而无需置换。
由于我不知道如何更好地解释它,因此这里有一些示例:
为了 m = 2
vector<vector<int>> box;
int N = 5;
for(int i = 1; i <= N; i++) {
for(int j = N; j >= i; j--) {
vector<int> dummy;
dummy.push_back(i);
dummy.push_back(j);
box.push_back(dummy);
}
}
为了 m = 3
vector<vector<int>> box;
int N = 5;
for(int i = 1; i <= N; i++) {
for(int j = N; j >= i; j--) {
for(int k = N; k >= j; k--) {
vector<int> dummy;
dummy.push_back(i);
dummy.push_back(j);
dummy.push_back(k);
box.push_back(dummy);
}
}
}
这工作得很好,结果就是我所需要的。但是就像已经提到的那样,它m
可以是任意的,我也不会为实现此目的而烦恼m = 37
。N
而m
在程序运行时已知值,但变化。m = 37
与实现一行37个循环的情况相比,必须有一种更好的方法来实现此目的。有人能帮我吗?我很无知:\
编辑:为了更好地解释我在寻找什么,这里有更多示例。
假设N = 5
和m = 4
,1223
这对我来说是一个可行的解决方案,124
不是因为它太短了。假设我已经找到1223
了一个不需要的解决方案2123
,2213
或者这个数字的任何其他排列。
edit2:或者,如果您更喜欢一种视觉上的(数学上的)问题表述,则可以。
考虑m
尺寸。有了m
2,您剩下一个N
大小矩阵。我正在寻找此矩阵(包括对角线)的上(或下)三角形。让我们转到m = 3
,矩阵变成一个3维立方体(或者,如果您愿意,则为Tensor),现在我正在寻找包括对角平面的上(或下)四面体。对于大于3的尺寸,我正在寻找超立方体(包括超对角平面)的超四面体。
http://howardhinnant.github.io/combinations.html
以下通用算法允许客户一次访问长度为N,r个项目的序列的每种组合或排列。
用法示例:
std::vector<std::vector<int>> box;
std::vector<int> v(N);
std::iota(begin(v), end(v), 1);
for_each_combination(begin(v), begin(v) + M, end(v), [](auto b, auto e) {
box.emplace_back(b, e);
return false;
});
上面的代码仅以插入每个组合box
为例,但是您实际上可能不想这样做:假设这box
只是一个中介,而您的实际工作然后在其他地方使用它,则可以避免使用中介,而只是做您直接需要在函子体内进行的任何工作。
这是使用提供的链接中的代码的完整的可行示例。
由于您想要的是具有重复的组合,而不仅仅是组合。这是在之上实现此示例for_each_combination()
:
template<typename Func>
void for_each_combination_with_repetition(int categories, int slots, Func func) {
std::vector<int> v(slots + categories - 1);
std::iota(begin(v), end(v), 1);
std::vector<int> indices;
for_each_combination(begin(v), begin(v) + slots, end(v), [&](auto b, auto e) {
indices.clear();
int last = 0;
int current_element = 0;
for(;b != e; ++last) {
if (*b == last+1) {
indices.push_back(current_element);
++b;
} else {
++current_element;
}
}
func(begin(indices), end(indices));
return false;
});
}
维基百科有关组合的文章很好地说明了这种方法的作用:正在获取数字[0,N + M-1)的所有组合(无重复),然后在结果中寻找“缺口”。间隙表示从一个元素的重复到另一个元素的重复的过渡。
您传递给此算法的函子将获得一个范围,该范围包含索引的集合,该集合包含要合并的元素。例如,如果您要从{x,y}的集合中获取所有三个元素的集合,则结果是{{x,x,x},{x,x,y},{x,y, y},{y,y,y}},此算法通过将索引范围返回到(有序)集合{x,y}中来表示:{{0,0,0},{0,0,1} ,{0,1,1},{1,1,1}}。
因此,通常使用此向量,您可以拥有一个向量或包含您的元素的东西,并将此算法产生的范围用作该容器的索引。但是,在您的情况下,由于元素只是从1到N的数字,因此可以通过在每个索引上加一个直接使用索引:
for_each_combination_with_repetition(N, M, [&](auto b, auto e) {
for(; b != e; ++b) {
int index = *b;
std::cout << index + 1 << " ";
}
std::cout << '\n';
});
一种替代实现可以返回表示每个类别的计数的向量。例如,较早的{{x,x,x},{x,x,y},{x,y,y},{y,y,y}}结果可以表示为:{{3,0} {2 ,1},{1,2},{0,3}}。修改实现以产生此表示形式的方法如下所示:
template<typename Func>
void for_each_combination_with_repetition(int categories, int slots, Func func) {
std::vector<int> v(slots + categories - 1);
std::iota(begin(v), end(v), 1);
std::vector<int> repetitions(categories);
for_each_combination(begin(v), begin(v) + slots, end(v), [&](auto b, auto e) {
std::fill(begin(repetitions), end(repetitions), 0);
int last = 0;
int current_element = 0;
for(;b != e; ++last) {
if (*b == last+1) {
++repetitions[current_element];
++b;
} else {
++current_element;
}
}
func(begin(repetitions), end(repetitions));
return false;
});
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句