C ++中的递归生成器

时间:2014-03-12 16:39:53

标签: python c++ recursion iterator generator

我有一个size = N的向量,其中每个元素的值可以从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的产量,我不知道在C ++中实现它的正确方法是什么。

可选问题: 有没有更好的方法在Python中实现它?

5 个答案:

答案 0 :(得分:2)

这个问题让我想起了一些奇怪的混合模数算术

我已经在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

答案 1 :(得分:1)

编辑:更简洁的一般代码,更好的评论和解释(我希望;))。

对于具有任意最大可能值的任意数量的位置,这是 迭代 ,而不是递归方法。这个想法如下。

我们在每个位置获得最大可能值。对于每个位置,我们生成一个包含该位置的所有可能值的数组。我们找到了如何挑选这些值以填充位置的组合总数(&#34;排列数&#34; ,等于所有可能值的乘积)。然后,我们遍历所有组合,将每个当前组合存储在组合数组中,并更新当前索引以在下一次迭代中选择下一个组合。我们不需要担心边界检查,因为我们固有地受到组合数量的限制。在遍历所有组合之后,我们返回一个包含所有组合的2D数组(然后打印它们)。

希望它可能有用(code on 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]

答案 2 :(得分:1)

C ++中的生成器并非易事,但仍有可能带有一些黑魔法:

http://www.codeproject.com/Articles/29524/Generators-in-C

您可以查看Safe cross platform coroutines的答案,因为尝试实际模拟python“yield”(包括PEP 342)将会带您进行一些协程实现。

如果要以C ++方式解决问题,使用对象存储“非生成器”方法的状态更为常见。

答案 3 :(得分:1)

又一个:

#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;
}      

Implementation at ideone.com

答案 4 :(得分:0)

另一种实施方式。

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
----------