使用boost或STL对C ++中的压缩(locking)容器进行sorting
我想做什么:我想sorting2或3,或N个向量,locking在一起, 而不复制到一个元组中。 也就是说,把冗长留在一边,就像:
vector<int> v1 = { 1, 2, 3, 4, 5}; vector<double> v2 = { 11, 22, 33, 44, 55}; vector<long> v3 = {111, 222, 333, 444, 555}; typedef tuple<int&,double&,long&> tup_t; sort(zip(v1,v2,v3),[](tup_t t1, tup_t t2){ return t1.get<0>() > t2.get<0>(); }); for(auto& t : zip(v1,v2,v3)) cout << t.get<0>() << " " << t.get<1>() << " " << t.get<2>() << endl;
这应该输出:
5 55 555 4 44 444 ... 1 11 111
我现在怎么做:我已经实现了我自己的快速sorting,其中我传递的第一个数组用于比较,排列应用于所有其他数组。 我只是不知道如何重用std :: sort为我的问题(如提取排列)。
我已经 尝试 : boost :: zip_iterator和boost :: zip_range (与boost :: combine范围),但std :: sort和boost :: range :: algorithm :: sort抱怨迭代器/范围是只读的而不是随机访问…
问题:如何对locking步骤(压缩)中的N个向量进行sorting? 这个问题看起来很普通,很普遍,所以我想通过一个可能非常复杂的库必须有一个简单的解决scheme,但我找不到它…
备注:是的,在stackoverflow中有类似的问题,这个问题被不同的forms问了很多。 但是,他们总是closures以下答案之一:
- 将你的向量复制到一个pair / tuple中并对这个元组进行sorting
- 将你的向量复制到每个向量一个成员的结构中,并对结构向量进行sorting…
- 实现你自己的分类function,为您的特定问题…
- 使用索引的辅助数组…
- 没有示例或使用产生不良结果的示例,请使用boost :: zip_iterator。
提示:
- 我发现这个线程在提供邮件列表 ,指向安东尼威廉斯本文 。 虽然这似乎只对成对,他们也讨论TupleIteratorType,但我一直无法find它。
- user673679发现这个职位包含两个容器的情况很好的解决scheme。 它也指出了这个问题(重点是我的):
[…]根本问题是数组引用的行为不像我们应该只是决定滥用迭代器的符号,写一些有用的东西。 这涉及到写一个不合格的迭代器,其中值types的引用与引用types不一样。
答: 见interjay下面的评论 (这也部分回答了未来的问题 ):
#include "tupleit.hh" #include <vector> #include <iostream> #include <boost/range.hpp> #include <boost/range/algorithm/sort.hpp> #include <boost/range/algorithm/for_each.hpp> template <typename... T> auto zip(T&... containers) -> boost::iterator_range<decltype(iterators::makeTupleIterator(std::begin(containers)...))> { return boost::make_iterator_range(iterators::makeTupleIterator(std::begin(containers)...), iterators::makeTupleIterator(std::end(containers)...)); } int main() { typedef boost::tuple<int&,double&,long&> tup_t; std::vector<int> a = { 1, 2, 3, 4 }; std::vector<double> b = { 11, 22, 33, 44 }; std::vector<long> c = { 111, 222, 333, 444 }; auto print = [](tup_t t){ std::cout << t.get<0>() << " " << t.get<1>() << " " << t.get<2>() << std::endl; }; boost::for_each( zip(a, b, c), print); boost::sort( zip(a, b, c), [](tup_t i, tup_t j){ return i.get<0>() > j.get<0>(); }); for ( auto tup : zip(a, b, c) ) print(tup); return 0; }
未来的问题:以前的答案适用于序列容器。 我们是否也可以使用可sorting的容器(例如序列和列表)? 这将需要random_access和双向TupleIterator以及一个在双向迭代器上工作的sortingalgorithm。
更新:这适用于类似序列的容器的组合。 然而,混合一个列表需要std :: sort支持BidirectionalIterators(不)。
下面是一个基于已经被提议用于标准化的range-v3 Library的工作示例
#include <range/v3/all.hpp> #include <iostream> using namespace ranges; int main() { std::vector<int> a1{15, 7, 3, 5}; std::vector<int> a2{ 1, 2, 6, 21}; sort(view::zip(a1, a2), std::less<>{}, &std::pair<int, int>::first); std::cout << view::all(a1) << '\n'; std::cout << view::all(a2) << '\n'; }
现场示例 (要求最新的编译器具有良好的C ++ 14支持,而不是VS 2015)。
对于两个容器的情况,这里是根据上面提到的论文在gcc 4.4.6上编译的一个版本。 在后来的gcc版本中,你可以换出boost :: tuple作为std :: tuple
#include <iostream> #include <vector> #include <iterator> #include <algorithm> # include <boost/iterator/iterator_facade.hpp> # include <boost/tuple/tuple.hpp> using namespace std; template <class T, class T2> struct helper_type { typedef boost::tuple<typename iterator_traits<T>::value_type, typename iterator_traits<T2>::value_type> value_type; typedef boost::tuple<typename iterator_traits<T>::value_type&, typename iterator_traits<T2>::value_type&> ref_type; }; template <typename T1, typename T2> class dual_iterator : public boost::iterator_facade<dual_iterator<T1, T2>, typename helper_type<T1, T2>::value_type, boost::random_access_traversal_tag, typename helper_type<T1, T2>::ref_type> { public: explicit dual_iterator(T1 iter1, T2 iter2) : mIter1(iter1), mIter2(iter2) {} typedef typename iterator_traits<T1>::difference_type difference_type; private: void increment() { ++mIter1; ++mIter2; } void decrement() { --mIter1; --mIter2; } bool equal(dual_iterator const& other) const { return mIter1 == other.mIter1; } typename helper_type<T1, T2>::ref_type dereference() const { return (typename helper_type<T1, T2>::ref_type(*mIter1, *mIter2)); } difference_type distance_to(dual_iterator const& other) const { return other.mIter1 - mIter1; } void advance(difference_type n) { mIter1 += n; mIter2 += n; } T1 mIter1; T2 mIter2; friend class boost::iterator_core_access; }; template <typename T1, typename T2> dual_iterator<T1, T2> make_iter(T1 t1, T2 t2) { return dual_iterator<T1, T2>(t1, t2); } template <class T1, class T2> struct iter_comp { typedef typename helper_type<T1, T2>::value_type T; bool operator()(const T& t1, const T& t2) { return get<0>(t1) < get<0>(t2); } }; template <class T1, class T2> iter_comp<T1, T2> make_comp(T1 t1, T2 t2) { return iter_comp<T1, T2>(); } template<class T> void print(T& items) { copy(items.begin(), items.end(), ostream_iterator<typename T::value_type>(cout, " ")); cout << endl; } int main() { vector<double> nums1 = {3, 2, 1, 0}; vector<char> nums2 = {'D','C', 'B', 'A'}; sort(make_iter(nums1.begin(), nums2.begin()), make_iter(nums1.end(), nums2.end()), make_comp(nums1.begin(), nums2.begin())); print(nums1); print(nums2); }
创build一个包含索引0..N-1的辅助数组。 使用自定义比较器对该数组进行sorting,该比较实际返回比较其中一个主数组的元素的结果。 然后使用sorting的辅助数组以正确的顺序打印出您的主数组。
很高兴认识一个互联网考古学家!
如何在锁步(压缩)中对N个向量sorting? 这个问题看起来很普通,很普遍,所以我想通过一个可能非常复杂的库必须有一个简单的解决scheme,但我找不到它。
有时候,我也进行了类似的假设寻宝。
从来没有find宝贝:(
我跟着你一样:
- 经过通常的嫌疑人boost.iterator / boost.range / boost.fusion / boost.oven,经过相当多的实验和研究意识到,他们不能解决这个特殊的问题。
- 经过许多关于SO的问题,只有意识到每一个都被closures了,或者是不正确的回答(例如,推荐boost :: zip_iterator,在这种情况下,你不能指出这个问题),或者避免使用一些解决方法事情的心脏。
- 通过许多博客post,邮件列表,才发现没有人真正解决了这个问题,除了….
- 经过大量研究,终于find了Antonius Wilhelm的旧手抄本,该手册声称已经制作了一个通用解决scheme“TupleIterator”,并将其locking在“tupleit.zip”的一个存档中。 这个历史资源非常稀less,我还不确定这个档案是一个神话,一个传说,还是它仍然埋在互联网的一个丢失层中的某个地方:)
好吧,更重要的是,安东尼·威廉姆斯(Anthony Williams)的论文提出这个问题实际上很难,所以发现现有的像boost这样的库并不能解决这个问题。
我很高兴地说,我已经find了一个类似的寻宝之后的解决scheme。 range-v3是一个好主意,如果你可以使用它,但是如果你确实需要一个迭代器的话 ,HPX项目已经创build了一个 ,它可以很好的完成。
由于疏忽,希望能得到修复,它仍然需要你连接到HPX库,但这对我来说是好的,因为整个过程是使用C ++ 17并行algorithm,HPX提供了一个实现。
#include <hpx/util/zip_iterator.hpp> using zip_it = hpx::util::zip_iterator<std::vector<std::size_t>::iterator, std::vector<std::size_t>::iterator, std::vector<double>::iterator>; int main() { std::vector<std::size_t> rows{3, 2, 1}; std::vector<std::size_t> cols{1, 2, 3}; std::vector<double> values{4.0, 5.0, 6.0}; auto start = hpx::util::make_zip_iterator(rows.begin(), cols.begin(), values.begin()); auto stop = hpx::util::make_zip_iterator(rows.end(), cols.end(), values.end()); std::sort(start, stop); for ( int i = 0; i < 3; ++i ) { std::cerr << rows[i] << ", " << cols[i] << ", " << values[i] << "\n"; } }