Recursive generator in C++

前端 未结 5 1201
星月不相逢
星月不相逢 2021-02-06 11:13

I have a vector of size = N where each element i can have values from 0 to possible_values[i]-1. I want to do a function that iterates me through all those values.

I was

相关标签:
5条回答
  • 2021-02-06 11:22

    Generators in C++ aren't trivial but still possible with a bit of black magic:

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

    You can look at answers to Safe cross platform coroutines because attempts to actually emulate python "yield" (including PEP 342) will bring you to some coroutine implementation anyway.

    If you want to solve your problem in C++ way, it's more common to use an object for storing state of your "non-generator" method.

    0 讨论(0)
  • 2021-02-06 11:25

    This problem reminded me of some strange mixed-modulus arithmetic numbers.

    I've put something together in Python. You should be able to reimplement this easily in C++. I sometimes used the input stream operator operator>>(...) in order to implement something like a generator in C++ (lazy evaluation is a really nice feature of Python's generators). Otherwise it'd be just an object that stores the state and let's you get the next value when you need it.

    Here's some example code:

    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
    

    Here's the output:

    000
    001
    010
    011
    100
    101
    110
    111
    200
    201
    210
    211
    

    Some links for further reference:

    • Operator overloading
    • Custom stream-like classes

    I've set up an example in 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;
        }
    }
    

    Example output:

    $ g++ test.cc -std=c++11 && ./a.out
    000
    001
    010
    011
    100
    101
    110
    111
    200
    201
    210
    211
    
    0 讨论(0)
  • 2021-02-06 11:26

    Another implementation.

    PS: The printing of the values can be customized to look like the output from Python but I didn't think that was necessary to illustrate the algorithm to generate the output data.

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

    The output generated from running the program:

    ----------
    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
    ----------
    
    0 讨论(0)
  • 2021-02-06 11:30

    EDIT: A bit more concise general code with better comments and explanation (I hope ;)).

    This is an iterative, not a recursive, approach for an arbitrary number of positions with arbitrary maximum possible values. The idea is as follows.

    We are given maximum possible value in each position. For each position we generate an array containing of all possible values for this position. We find total number of combinations of how these values can be picked out to fill in positions ("number of permutations", equal to product of all possible values). We then iterate through all combinations, storing each current combination in an array of combinations, and updating current indices to select next combination on the next iteration. We don't need to worry about boundary checks, because we are inherently limited by number of combinations. After iterated through all combinations, we return a 2D array that holds all of them (and then print them).

    Hope it might be useful (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);
    }
    

    Program's output:

    [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]
    
    0 讨论(0)
  • 2021-02-06 11:37

    Yet another:

    #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

    0 讨论(0)
提交回复
热议问题