c++中的递归生成器
Recursive generator in C++
我有一个大小为N的向量,其中每个元素I可以具有从0到possible_values[I]-1的值。我想用一个函数来遍历所有这些值
我可以在Python中使用递归生成器来完成:
def all_values(size,values,pos=0):
if pos == size:
yield []
else:
for v in xrange(values[pos]):
for v2 in all_values(size,values,pos+1):
v2.insert(0,v)
yield v2
possible_values=[3,2,2]
for v in all_values(3,possible_values):
print v
示例输出:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]
[2, 0, 0]
[2, 0, 1]
[2, 1, 0]
[2, 1, 1]
由于c++没有Python的yield,我不知道在c++中实现它的正确方法是什么。
可选的问题:有没有更好的方法在Python中实现这个?
这个问题让我想起了一些奇怪的混合模数。
我已经在Python中组合了一些东西。您应该能够在c++中轻松地重新实现它。我有时会使用输入流运算符operator>>(...)
,以便在c++中实现类似生成器的东西(惰性求值是Python生成器的一个非常好的特性)。否则,它就只是一个存储状态的对象,让你在需要的时候获得下一个值。
下面是一些示例代码:
class Digit:
def __init__(self, modulus):
self.modulus = modulus
self.value = 0
def __str__(self):
return str(self.value)
def __nonzero__(self):
return bool(self.value)
def increment(self):
self.value += 1
self.value %= self.modulus
return self.value == 0
class Number:
def __init__(self, moduli):
self.digits = [Digit(m) for m in moduli]
def __str__(self):
return "".join(str(d) for d in self.digits)
def __nonzero__(self):
return any(d for d in self.digits)
def increment(self):
carryover = True
for d in reversed(self.digits):
if carryover:
carryover = d.increment()
n = Number([3,2,2])
while True:
print n
n.increment()
if not n:
break
输出如下:
000
001
010
011
100
101
110
111
200
201
210
211
一些链接供进一步参考:
- 操作符重载
- 自定义类流
我在c++中设置了一个例子:
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
struct number {
struct digit {
int value;
int modulus;
digit(int modulus) : value(0), modulus(modulus) {}
bool increment() {
value = (value+1)%modulus;
return !value;
}
operator void*() {
return value ? this : 0;
}
std::string to_str() {
return std::to_string(value);
}
};
std::vector<digit> digits;
number(std::vector<int> const & moduli) {
for (auto i : moduli)
digits.push_back(digit(i));
}
void increment() {
bool carry = true;
for (auto d = digits.rbegin(); d != digits.rend(); d++)
if (carry)
carry = d->increment();
}
operator void*() {
for (digit & d : digits)
if (d) return this;
return 0;
}
std::string to_str() {
std::stringstream o;
for (auto & d : digits)
o << d.to_str();
return o.str();
}
};
int main() {
number n({3,2,2});
for(;;) {
std::cout << n.to_str() << 'n';
n.increment();
if (!n) break;
}
}
示例输出:
$ g++ test.cc -std=c++11 && ./a.out
000
001
010
011
100
101
110
111
200
201
210
211
c++中的生成器并非微不足道,但仍然可以使用一些黑魔法:
http://www.codeproject.com/Articles/29524/Generators-in-C你可以看看安全的跨平台协程的答案,因为尝试实际模拟python的"yield"(包括PEP 342)无论如何都会给你带来一些协程实现。
如果你想用c++的方式解决你的问题,更常见的是使用一个对象来存储你的"非生成器"方法的状态
又一个:
#include <vector>
#include <iostream>
typedef std::vector<unsigned int> uint_vector;
typedef std::vector<uint_vector> values_vector;
values_vector all_values (const uint_vector & ranges, unsigned int pos=0) {
values_vector result;
if (pos == ranges.size()) {
result.push_back (uint_vector());
}
else {
values_vector rem_result = all_values (ranges, pos+1);
for (unsigned int v = 0; v < ranges[pos]; ++v) {
for (auto it : rem_result) {
result.push_back (uint_vector(1,v));
result.back().insert (result.back().end(), it.begin(), it.end());
}
}
}
return result;
}
void print_values (const values_vector & combos) {
for (auto combo : combos) {
std::cout << "[ ";
for (auto num : combo) {
std::cout << num << ' ';
}
std::cout << "]n";
}
}
int main () {
uint_vector ranges {3,2,2};
print_values (all_values (ranges));
return 0;
}
ideone.com的实现
EDIT:更简洁的通用代码,有更好的注释和解释(我希望;))。
这是一个迭代,而不是一个递归,对于任意数目的具有任意最大值的位置。思路如下:
给出了每个位置的最大可能值。对于每个位置,我们生成一个包含该位置所有可能值的数组。我们找到了这些值如何被挑选出来填充位置的组合总数("排列数",等于所有可能值的乘积)。然后我们遍历所有组合,将每个当前组合存储在组合数组中,并更新当前索引以在下一次迭代中选择下一个组合。我们不需要担心边界检查,因为我们天生就受到组合数量的限制。遍历所有组合后,返回一个包含所有组合的2D数组(然后打印它们)。
希望它可能是有用的(代码在ideone.com):
#include <vector>
#include <iostream>
#include <algorithm>
namespace so {
using size = std::size_t;
using array_1d = std::vector<size>;
using array_2d = std::vector<array_1d>;
array_2d generate_combinations_all(array_1d const & _values_max) {
array_2d values_all_; // arrays of all possible values for each position
size count_combination_{1}; // number of possible combinations
for (auto i_ : _values_max) { // generate & fill in 'values_all_'
array_1d values_current_(i_);
size value_current_{0};
std::generate(values_current_.begin(), values_current_.end(), [&] {return (value_current_++);});
values_all_.push_back(std::move(values_current_));
count_combination_ *= i_;
}
array_2d combinations_all_; // array of arrays of all possible combinations
array_1d indices_(_values_max.size(), 0); // array of current indices
for (size i_{0}; i_ < count_combination_; ++i_) {
array_1d combinantion_current_; // current combination
for (size j_{0}; j_ < indices_.size(); ++j_) // fill in current combination
combinantion_current_.push_back(values_all_[j_][indices_[j_]]);
combinations_all_.push_back(std::move(combinantion_current_)); // append to 'combinations_all_'
for (size m_{indices_.size()}; m_-- > 0;) // update current indices
if (indices_[m_] < _values_max[m_] - 1) { // ++index at highest position possible
++indices_[m_];
break;
}
else indices_[m_] = 0; // reset index if it's alrady at max value
}
return (combinations_all_);
}
void print_combinations_all(array_2d const & _combinations_all) {
for (auto const & i_ : _combinations_all) { // "fancy" printing
std::cout << "[";
for (size j_{0}; j_ < i_.size(); ++j_)
std::cout << i_[j_] << ((j_ < i_.size() - 1) ? ", " : "]n");
}
}
} // namespace so
int main() {
so::array_1d values_max_a_{3, 2, 2};
so::array_1d values_max_b_{2, 1, 3, 2};
so::print_combinations_all(so::generate_combinations_all(values_max_a_));
std::cout << "***************" << std::endl;
so::print_combinations_all(so::generate_combinations_all(values_max_b_));
return (0);
}
程序的输出:
[0, 0, 0]
[0, 0, 1]
[0, 1, 0]
[0, 1, 1]
[1, 0, 0]
[1, 0, 1]
[1, 1, 0]
[1, 1, 1]
[2, 0, 0]
[2, 0, 1]
[2, 1, 0]
[2, 1, 1]
***************
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 2, 0]
[0, 0, 2, 1]
[1, 0, 0, 0]
[1, 0, 0, 1]
[1, 0, 1, 0]
[1, 0, 1, 1]
[1, 0, 2, 0]
[1, 0, 2, 1]
另一个实现。
PS:可以自定义值的打印,使其看起来像Python的输出,但我认为没有必要说明生成输出数据的算法。
#include <iostream>
#include <vector>
using namespace std;
void print_values(vector<vector<int> > const& values)
{
for ( auto v1 : values)
{
for ( auto v : v1 )
{
cout << v << " ";
}
cout << "n";
}
}
vector<vector<int> > get_all_values(int size,
vector<int>::const_iterator iter)
{
vector<vector<int> > ret;
if ( size == 1 )
{
for (int v = 0; v != *iter; ++v )
{
std::vector<int> a = {v};
ret.push_back(a);
}
return ret;
}
vector<vector<int> > prev = get_all_values(size-1, iter+1);
for (int v = 0; v != *iter; ++v )
{
for ( vector<int>& v1 : prev )
{
std::vector<int> a = {v};
a.insert(a.end(), v1.begin(), v1.end());
ret.push_back(a);
}
}
return ret;
}
vector<vector<int> > get_all_values(vector<int> const& in)
{
return get_all_values(in.size(), in.begin());
}
int main()
{
vector<int> a{2};
vector<int> b{2,3};
vector<int> c{2,3,2};
cout << "----------n";
print_values(get_all_values(a));
cout << "----------n";
print_values(get_all_values(b));
cout << "----------n";
print_values(get_all_values(c));
cout << "----------n";
return 0;
}
运行程序产生的输出:
----------
0
1
----------
0 0
0 1
0 2
1 0
1 1
1 2
----------
0 0 0
0 0 1
0 1 0
0 1 1
0 2 0
0 2 1
1 0 0
1 0 1
1 1 0
1 1 1
1 2 0
1 2 1
----------
- 通过递归进行因子分解
- 递归函数计算序列中的平方和(并输出过程)
- 使用递归的数组的最小值.这是怎么回事
- 递归列出所有目录中的C++与Python与Ruby的性能
- 递归计数给定目录的文件和所有目录
- 如何在BST的这个简单递归实现中消除警告
- C++:正在检查LinkedList中的回文-递归方法-错误
- 递归模板化函数不能分配给具有常量限定类型"const tt &"的变量"state"
- 递归无序映射
- TSP递归解的迭代形式
- 如何在Elixir中调用递归函数并行
- 返回递归调用和仅递归调用的区别
- 数组元素打印的递归方法
- 使用递归时获取变量的奇怪值
- 如何在C++中递归地按相反顺序打印集合
- 到连接组件算法的问题(递归)
- 如何使用递归打印修改后的星号三角形图案
- 使用递归模板动态分配的多维数组
- 递归函数有效,但无法记忆
- 包含模板文件的递归会导致编译失败