【问题标题】:Polymorphic filter iterator range多态过滤器迭代器范围
【发布时间】:2015-10-04 06:32:54
【问题描述】:

我有一个元素序列(通常是std::vector<T>)和一个方法overlap_range(Iterator, Iterator, T),用于获取这些元素的子集,这些元素是overlaped 和T

如果元素的序列满足某些条件,那么overlap_range 的结果将是连续的,并且可以在对数时间内确定。否则可能存在多个不连续的此类子范围,这些子范围只能在线性时间内确定。确定是否满足条件需要线性时间。

我想要以下内容:

  1. overlap_range 是多态的,从某种意义上说,如果已知满足条件,算法在对数时间内运行。
  2. 返回的子范围是多态的,如果满足条件,则能够利用子范围的已知属性。

我建议的解决方案是在overlap_range 中使用默认值标志参数,这可以解决 (1)。然后我想通过使用boost::filter_iterator<std::function<bool(T)>, Iterator> 来解决(2),如果已知满足条件,std::function<bool(T)> 只需返回true。然而,事实证明这是大约。在这两种情况下,都比简单地使用仿函数测试overlaped 慢 50 倍。 overlap 的逻辑并不是特别复杂,但该范围内的元素数量足够大,如果可能的话,不必对其进行不必要的评估,这是一个显着的优势。

还有其他方法可以帮助我解决这个问题吗?

更多详情

虽然我认为以上内容足以理解问题,但这里还有一些可能有用的背景知识。

  • T 是可以简化为 GenomicRegion 的任何对象,它只是在单个连续序列(即两个坐标)上定义一组坐标。任何这样的T 实际上是一个Mappable<T>,它基本上需要一个方法get_region
  • overlaps 然后定义为

    inline GenomicRegion::DifferenceType overlap_size(const GenomicRegion& lhs, const GenomicRegion& rhs) noexcept
    {
        return static_cast< GenomicRegion::DifferenceType>(std::min(lhs.get_end(), rhs.get_end())) -
                static_cast< GenomicRegion::DifferenceType>(std::max(lhs.get_begin(), rhs.get_begin()));
    }
    
    inline bool overlaps(const GenomicRegion& lhs, const GenomicRegion& rhs) noexcept
    {
        auto num_bases_overlaped = overlap_size(lhs, rhs);
        return (num_bases_overlaped == 0) ? !are_adjacent(lhs, rhs) || empty(std::min(lhs, rhs)) : num_bases_overlaped > 0;
    }
    
  • 任何Mappable 类都可以按开始坐标排序,然后是结束坐标(如果开始坐标相等)。
  • 对数重叠搜索(和连续的结果区域)的“标准”是:如果 a &lt;= b 则为 end(a) &lt;= end(b),例如,如果所有 T 大小相同,则为 true。
  • 朴素过滤迭代器定义为

    template <typename MappableType>
    class IsOverlapped
    {
    public:
        IsOverlapped() = delete;
        template <typename MappableType_>
        IsOverlapped(const MappableType_& mappable) : region_ {get_region(mappable)} {}
        bool operator()(const MappableType& mappable) { return overlaps(mappable, region_); }
    private:
        GenomicRegion region_;
    };
    
    template <typename Iterator>
    using OverlapIterator = boost::filter_iterator<IsOverlapped<typename Iterator::value_type>, Iterator>;
    
    template <typename Iterator>
    using OverlapRange = boost::iterator_range<OverlapIterator<Iterator>>;
    
  • overlap_range 然后可以声明为

    template <typename ForwardIterator, typename MappableType>
    OverlapRange<ForwardIterator>
    overlap_range(ForwardIterator first, ForwardIterator last, const MappableType& mappable, bool is_bidirectional=false)
    

当然,这可以简单地返回范围OverlapRange&lt;ForwardIterator&gt;(first, last),但我们可以通过有效地找到右边界以及如果范围is_bidirectional 的左边界来做得更好。

【问题讨论】:

    标签: c++ boost iterator


    【解决方案1】:

    最大的减速可能是因为std::function&lt;&gt; 中的动态调度。您可以尝试将其替换为自定义的、非类型擦除的谓词类型(函数对象?)。


    我可能应该问 "be overlappedT" 范围内的元素意味着什么。 将被称为量词的数字映射 但是,让我跳出一点结论,因为这是炫耀 Boost Interval Container 库的好借口。

    请注意,该演示取决于 Boost ICL 内置 Combining Styles 和用于许多功能的比较器这一事实。来自Map concept 的文档:

    • Maps of Sets 将被称为 Collectors
    • 数字地图,将被称为量词

    为了更好地了解它开箱即用地实现了什么,我选择使用Collectors 进行演示,即您的术语中的T 恰好是std::set&lt;int&gt;

    Live On Coliru

    #include <boost/icl/interval_map.hpp>
    #include <set>
    
    namespace icl = boost::icl;
    
    using Container = icl::interval_map<
        size_t,          // mirrors the vector index
        std::set<int> >; // set intersection models our "overlap"
    
    Container generate_random_data();
    
    #include <iostream>
    
    int main() {
        auto haystack = generate_random_data();
    
        // let's find all ranges where the T overlaps with -2:
    
        using Seive = Container::value_type;
    
        std::cout << "Haystack: " << haystack << "\n";
        std::cout << "Overlaps -2: \n\t-> " << (haystack & Seive {{ 0, 1024 }, { -2 }}) << "\n";
        std::cout << "Overlaps +7: \n\t-> " << (haystack & Seive {{ 0, 1024 }, { +7 }}) << "\n";
    
        // you can even do a "multi-sieve" in one pass:
        std::cout << "Overlaps any of +7 or -2: \n\t-> " << (haystack & Seive {{ 0, 1024 }, { -2, +7 }}) << "\n";
    
        // and more interesting, you can narrow any of the "overlap" targets to a certain input range:
        Container combined;
        combined += Seive {{ 128, 512 }, { -2 }};
        combined += Seive {{ 512, 768 }, { +7 }};
        std::cout << "Complex query " << combined << ": \n\t-> " << (haystack & combined) << "\n";
    }
    
    #include <random>
    #include <functional>
    
    Container generate_random_data() {
        using namespace std;
    
        mt19937 prng {random_device{}()};
        // mt19937 prng {3236629322};
        auto card     = bind(uniform_int_distribution<>(1,       5),    ref(prng));
        auto domain   = bind(uniform_int_distribution<size_t>(0, 1024), ref(prng));
        auto codomain = bind(uniform_int_distribution<>(-10,     10),   ref(prng));
        auto gen_val  = [&] { 
            Container::value_type::second_type v;
            generate_n(inserter(v, end(v)), card(), codomain);
            return v;
        };
    
        auto gen_range = [&]() -> Container::value_type { 
            auto left = domain(), right = domain();
            if (left>right) swap(left, right);
    
            return { 
                Container::interval_type::right_open ( left, right ),
                gen_val()
            };
        };
    
        {
            Container data;
            generate_n(icl::inserter(data, end(data)), prng() % 1024, gen_range);
            return data;
        }
    }
    

    哪些打印(针对特定种子)

    Haystack: {([0,1)->{-10 3 })([1,5)->{-10 })([5,13)->{-9 -1 3 7 10 })([13,17)->{-4 1 6 10 })([17,69)->{0 2 4 8 })([69,81)->{-7 -4 9 })([81,97)->{1 8 10 })([97,102)->{6 })([102,701)->{-10 -2 1 3 7 })([701,987)->{3 })([987,1002)->{-9 6 7 8 10 })([1002,1003)->{2 3 4 5 9 })([1003,1004)->{-9 -7 3 9 })([1004,1024)->{-10 -2 2 5 })}
    Overlaps -2: 
        -> {([102,701)->{-2 })([1004,1024)->{-2 })}
    Overlaps +7: 
        -> {([5,13)->{7 })([102,701)->{7 })([987,1002)->{7 })}
    Overlaps any of +7 or -2: 
        -> {([5,13)->{7 })([102,701)->{-2 7 })([987,1002)->{7 })([1004,1024)->{-2 })}
    Complex query {([128,512)->{-2 })([512,768)->{7 })}: 
        -> {([128,512)->{-2 })([512,701)->{7 })}
    

    您可以多次运行该示例以查看其他随机生成的数据集的结果。

    【讨论】:

    • 添加了实时样本。 live coding session 仍然可用。
    • “非类型擦除谓词类型”是指简单地以函数对象中的谓词为条件吗?我想这是我所能期望的最好的了。 ICL 看起来像一个不错的库。我很想看看它是如何处理我的数据的。
    • 是的。你没有显示函数对象,所以我不能显示它
    • 我添加了更多(可能是不必要的)关于该问题的详细信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-19
    • 2014-09-08
    • 2014-10-14
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    相关资源
    最近更新 更多