【问题标题】:Implementing an iterator over combinations of many vectors在许多向量的组合上实现迭代器
【发布时间】:2018-03-16 01:11:12
【问题描述】:

我正在解决一个问题,该问题需要遍历 K 向量的所有元素组合,一次取一个。例如,对于K=2 向量v1 = [0 1]v2 = [3 4],我将遍历(0,3), (0,4), (1,3), (1,4)

由于K 是在运行时确定的,因此我不能使用显式 for 循环。我目前的方法是基于this solution,它实现了一个“里程表”,为每个向量增加一个索引。

#include <vector>
#include <iostream>

int main(int argc, char * argv[])
{
    std::vector<int> v1( {1, 2, 3} );
    std::vector<int> v2( {-2, 5} );
    std::vector<int> v3( {0, 1, 2} );
    std::vector<std::vector<int> > vv( {v1, v2 ,v3} );

    // Iterate combinations of elems in v1, v2, v3, one at a time
    std::vector<std::vector<int>::iterator> vit;
    for (auto& v : vv)
        vit.push_back(v.begin());
    int K = vv.size();
    while (vit[0] != vv[0].end()) 
    {
        std::cout << "Processing combination: ["; 
        for (auto& i : vit)
            std::cout << *i << " ";
        std::cout << "]\n";

        // increment "odometer" by 1
        ++vit[K-1];
        for (int i = K-1; (i > 0) && (vit[i] == vv[i].end()); --i) 
        {
        vit[i] = vv[i].begin();
        ++vit[i-1];
        }
    }

    return 0;
}

输出:

Processing combination: [1 -2 0 ]
Processing combination: [1 -2 1 ]
Processing combination: [1 -2 2 ]
Processing combination: [1 5 0 ]
Processing combination: [1 5 1 ]
Processing combination: [1 5 2 ]
Processing combination: [2 -2 0 ]
Processing combination: [2 -2 1 ]
Processing combination: [2 -2 2 ]
Processing combination: [2 5 0 ]
Processing combination: [2 5 1 ]
Processing combination: [2 5 2 ]
Processing combination: [3 -2 0 ]
Processing combination: [3 -2 1 ]
Processing combination: [3 -2 2 ]
Processing combination: [3 5 0 ]
Processing combination: [3 5 1 ]
Processing combination: [3 5 2 ]

但是,这有点混乱,需要大量样板代码,为了清楚起见,我宁愿将其移到其他地方。理想情况下,我希望有一个自定义迭代器类,比如my_combination_iterator,它可以让我做的事情更干净,例如:

for (my_combination_iterator it = vv.begin(); it != vv.end(); ++it)
    // process combination

到目前为止,我已经查看了Boost iterator_facade。但是我的情况似乎比教程中的情况更复杂,因为我需要一个迭代器来处理 Values 的向量,而不是使用单个值类型来定义自定义迭代器所需的运算符。 如何实现这样的迭代器?

【问题讨论】:

  • 我终于有空来尝试实现一个合适的双向组合迭代器。你可以找到它here。我没有使用 boost,所以代码比它可能的更冗长。

标签: c++ vector boost iterator


【解决方案1】:

为什么要使用自定义迭代器? 可以改为实现一个非常简单的类,它将遍历所有组合:

class Combinator
{
public:
    Combinator(std::vector<std::vector<int> >& vectors)
        : m_vectors(vectors)
    {
        m_combination.reserve(m_vectors.size());
        for(auto& v : m_vectors)
            m_combination.push_back(v.begin());
    }

    bool next()
    {
        // iterate through vectors in reverse order
        for(long long i = m_vectors.size() - 1; i >= 0; --i)
        {
            std::vector<int>& v = m_vectors[i];
            std::vector<int>::iterator& it = m_combination[i];

            if(++it != v.end())
                return true;
            it = v.begin();
        }
        return false;
    }

    std::vector<std::vector<int>::iterator> combination() const
    {
        return m_combination;
    }

private:
    std::vector<std::vector<int> >& m_vectors; // reference to data
    std::vector<std::vector<int>::iterator> m_combination;
};

Live Demo

更新: 如果您仍想使用迭代器,我建议对组合进行迭代。可以将Combinator 中的所有组合放入一个容器中,然后使用容器自己的迭代器。在我看来,这是一个更清洁的解决方案。唯一的缺点是显式存储所有组合需要额外的内存。

【讨论】:

  • 这实际上是一个迭代器。添加缺少的运算符,添加默认构造函数,从std::iterator 继承,然后就可以了。
  • 这是一个合理的建议。我对使用自定义迭代器很感兴趣,因为它允许直接应用适用于迭代器范围的 STL 算法。我认为在表明如何使用代码的意图方面也会更加清晰。
  • @mikkola 您可以将Combinator 中的所有组合放入容器中,然后使用容器的迭代器。 IMO 这是一个更清洁的解决方案。唯一的缺点是显式存储所有组合所需的内存开销。
  • @AMA 最后我对此略有不同,所以我接受了这个作为答案。感谢您的宝贵时间!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-30
  • 1970-01-01
  • 2018-09-10
  • 2020-10-24
  • 1970-01-01
  • 2011-03-26
  • 1970-01-01
相关资源
最近更新 更多