【问题标题】:Arbitrary number of nested-loops? [duplicate]任意数量的嵌套循环? [复制]
【发布时间】:2010-08-21 07:49:17
【问题描述】:

我希望获取任意数量的列表(例如 [2, 1, 4 . . . .], [8, 3, ...], . . .)并从每个列表中选择数字以便生成所有排列。例如:

[2, 8, ...], [2, 3, ...], [1, 8, ...], [1, 3, ...], [4, 8, ...], [4, 3, ...], ...

这很容易使用嵌套的 for 循环来完成,但由于我希望它接受任意数量的列表,因此 for 循环似乎必须进行硬编码。每个列表一个。此外,由于我的程序可能会生成数万个排列,我想一次生成一个排列(而不是一次性计算它们并将结果存储到向量中)。有没有办法以编程方式完成此任务?

由于列表的数量在编译时是已知的,我想也许我可以使用基于模板的元编程。然而,这似乎很笨拙,也不符合“一次一个”的要求。有什么建议吗?

【问题讨论】:

  • 我相信,通常未知数量的 for 循环嵌套在简单的递归中。

标签: c++ nested metaprogramming permutation


【解决方案1】:

您可以使用计数的基本原理,例如递增最后一位直到达到最大值,然后递增倒数第二位,依此类推,就像倒计时一样 这是一个示例代码,假设 diff 列表的长度可能不同。

#include <iostream>
using namespace std;
int main() {
    int n;
    cin>>n;
    int a[n], len[n],i,j;
    for(i = 0 ; i < n ; i++)
    {
        cin>>len[i];
        a[i]=0;
    }
    while(1)
    {
        for(i = 0 ; i< n;i++)
            cout<<a[i]<<" ";
        cout<<endl;
        for(j = n-1 ; j>=0 ; j--)
        {
            if(++a[j]<=len[j])
                break;
            else
                a[j]=0;
        }
        if(j<0)
            break;
    }    
    return 0;
}

尝试使用4 1 1 1 1 运行代码,它将给出 0 和 1 的所有 4 位排列。

0 0 0 0 
0 0 0 1 
0 0 1 0 
0 0 1 1 
0 1 0 0 
0 1 0 1 
0 1 1 0 
0 1 1 1 
1 0 0 0 
1 0 0 1 
1 0 1 0 
1 0 1 1 
1 1 0 0 
1 1 0 1 
1 1 1 0 
1 1 1 1 

您可以使用二维数组来获取编号的组合。

【讨论】:

  • 这有一个无限循环。
【解决方案2】:

递归方式...

void Recurse(const vector<vector<int>>& v, 
             size_t listToTwiddle, 
             vector<int>& currentPermutation)
{
    // terminate recursion
    if (listToTwiddle == v.size())
    {
        for(auto i = currentPermutation.cbegin(); i != currentPermutation.cend(); ++i)
        {
            cout << *i << " ";
        }
        cout << endl;
        return;
    }

    for(size_t i = 0; i < v[listToTwiddle].size(); ++i)
    {
        // pick a number from the current list
        currentPermutation.push_back(v[listToTwiddle][i]);

        // get all permutations having fixed this number
        Recurse(v, listToTwiddle + 1, currentPermutation);

        // restore back to original state
        currentPermutation.pop_back();
    }
}

void Do(const vector<vector<int>>& v)
{
    vector<int> permutation;
    Recurse(v, 0, permutation);
}

【讨论】:

    【解决方案3】:

    有趣。

    您似乎想要的实际上是一种迭代器,它将在给定的范围内进行迭代,并在每一步为您提供一个排列。

    它通常可以在没有元编程的情况下编写,特别是因为自 C++0x 起才支持可变参数模板。尽管如此,我觉得这是一个非常有趣的挑战。

    我们在这里的第一个助手将是小tuple 类。我们还需要一些元模板编程技巧来将一个元组转换为另一个元组,但我将把它作为练习让读者编写必要的元模板函数和执行转换的实际函数(阅读:今天下午太热了,我去不了)。

    这里有一些东西可以帮助你前进。

    template <class... Containers>
    class permutation_iterator
    {
    public:
      // tuple of values
      typedef typename extract_value<Containers...>::type value_type;
    
      // tuple of references, might be const references
      typedef typename extract_reference<Containers...>::type reference;
    
      // tuple of pointers, might be const pointers
      typedef typename extract_pointer<Containers...>::type pointer;
    
      permutation_iterator(Containers&... containers) { /*extract begin and end*/ }
    
      permutation_iterator& operator++()
      {
        this->increment<sizeof...(Containers)-1>();
        return *this;
      }
    
    private:
      typedef typename extract_iterator<Containers...>::type iterator_tuple;
    
      template <size_t N>
      typename std::enable_if_c<N < sizeof...(Containers) && N > 0>::type
      increment()
      {
        assert(mCurrent.at<N>() != mEnd.at<N>());
        ++mCurrent.at<N>();
        if (mCurrent.at<N>() == mEnd.at<N>())
        {
          mCurrent.at<N>() = mBegin.at<N>();
          this->increment<N-1>();
        }
      }
    
      template <size_t N>
      typename std::enable_if_c<N == 0>::type increment()
      {
        assert(mCurrent.at<0>() != mEnd.at<0>());
        ++mCurrent.at<0>();
      }
    
      iterator_tuple mBegin;
      iterator_tuple mEnd;
      iterator_tuple mCurrent;
    };
    

    如果您不知道如何使用 meta,更简单的方法是使用递归,然后要求用户通过 at 方法指示她希望访问的容器,该方法以 N 作为参数来指示容器索引。

    【讨论】:

      【解决方案4】:

      STL 没有现成的函数,但你可以通过修改next_permutation 的某些部分来编写自己的实现。

      这个问题类似于实现一个二进制数字加法器。增加array[0]。如果array[0] 的新值溢出(意味着它的值大于您拥有的列表数),则将array[0] 设置为零并增加array[1]。以此类推。

      【讨论】:

        【解决方案5】:

        使用递归,您可能可以用当前位置、剩余列表等“养活自己”。这有可能溢出,但通常可以将递归函数制作成非递归函数(如 for 循环),尽管大部分优雅都消失了。

        【讨论】:

          【解决方案6】:

          非递归方式:

          #include <vector>
          #include <iostream>
          
          // class to loop over space
          // no recursion is used
          template <class T>
          class NLoop{
          
          public:
          
              // typedefs for readability
              typedef std::vector<T> Dimension;
              typedef std::vector< Dimension > Space;
              typedef std::vector< typename Dimension::iterator > Point;
          
              // the loop over space and the function-pointer to call at each point
              static void loop(Space space, void (*pCall)(const Point&))
              {
          
                  // get first point in N-dimensional space
                  Point current;
                  for ( typename Space::iterator dims_it = space.begin() ; dims_it!=space.end() ; ++dims_it )
                  {
                      current.push_back( (*dims_it).begin() );
                  }
          
                  bool run = true;
                  while ( run )
                  {
          
                      // call the function pointer for current point
                      (*pCall)(current);
          
                      // go to next point in space
                      typename Space::iterator dims_it = space.begin();
                      typename Point::iterator cur_it = current.begin();
                      for (  ; dims_it!=space.end() ; ++dims_it, ++cur_it )
                      {
                          // check if next in dimension is at the end
                          if ( ++(*cur_it) == (*dims_it).end() )
                          {
                              // check if we have finished whole space
                              if ( dims_it == space.end() - 1 )
                              {
                                  // stop running now
                                  run = false;
                                  break;
                              }
                              // reset this dimension to begin
                              // and go to next dimension
                              *cur_it = (*dims_it).begin();
                          }
                          else
                          {
                              // next point is okay
                              break;
                          }
                      }
          
                  }
              }
          };
          
          
          // make typedef for readability
          // this will be a loop with only int-values in space
          typedef NLoop<int> INloop;
          
          // function to be called for each point in space
          // do here what you got to do
          void call(const INloop::Point &c)
          {
              for ( INloop::Point::const_iterator it = c.begin() ; it!=c.end() ; ++it)
              {
                  std::cout << *(*it) << " ";
              }
              std::cout << std::endl;
          }
          
          int main()
          {
          
              // fill dimension
              INloop::Dimension d;
              d.push_back(1);
              d.push_back(2);
              d.push_back(3);
          
              // fill space with dimensions
              INloop::Space s;
              s.push_back(d);
              s.push_back(d);
              s.push_back(d);
          
              // loop over filled 'space' and call 'call'
              INloop::loop(s,call);
          
              return 0;
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-10
            • 1970-01-01
            • 1970-01-01
            • 2021-12-25
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多