std :: lower_bound,跳过无效元素

时间:2017-06-12 10:29:47

标签: c++ algorithm performance boost time-complexity

我有一个文件名列表,每个文件名代表一个时间点。该列表通常包含数千个元素。给定一个时间点,我想将这些文件名转换为时间对象(我正在使用boost::ptime),然后根据文件名找到此时间点的std::lower_bound的值

示例:

文件名(日期+时间,分钟增加,每个文件都有一分钟):

station01_20170612_030405.hdf5
station01_20170612_030505.hdf5
station01_20170612_030605.hdf5
station01_20170612_030705.hdf5
station01_20170612_030805.hdf5
station01_20170612_030905.hdf5

如果我有一个时间点2017-06-12 03:06:00,那么它适合这里:

station01_20170612_030405.hdf5
station01_20170612_030505.hdf5
                             <--- The lower bound I am looking for is here
station01_20170612_030605.hdf5
station01_20170612_030705.hdf5
station01_20170612_030805.hdf5
station01_20170612_030905.hdf5

到目前为止,一切都很简单。现在问题是文件列表可能掺杂了一些无效的文件名,这将使转换到一个时间点失败。

目前,我这样做是简单/低效的方式,我想优化它,因为这个程序将在服务器上运行并且操作成本很重要。所以,愚蠢的方法是:创建一个包含时间点的新列表,并且只推送有效的时间点:

vector<ptime> filesListTimePoints;
filesListTimePoints.reserve(filesList.size());
ptime time;
for(long i = 0; i < filesList.size(); i++) {
    ErrorCode error = ConvertToTime(filesList[i], time);
    if(error.errorCode() == SUCCESS)
        filesListTimePoints.push_back(time);
}
//now use std::lower_bound() on filesListTimePoints

你知道,问题在于我使用的是线性解决方案,其问题可以通过O(log(N))复杂度来解决。我不需要转换所有文件,甚至不需要查看所有文件!

我的问题:我如何将其嵌入std::lower_bound,以保持最佳复杂性?

我对可能解决方案的想法:

cppreference上,有std::lower_bound的基本实现。我正在考虑修改它以获得一个有效的解决方案。但是我不确定当一个转换失败时该怎么做,因为这个算法高度依赖于单调行为。这个问题是否有解决方案,即使在数学上也是如此?

这是我最初想到的版本:

template<class ForwardIt, class T>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value)
{
    ForwardIt it;
    typename std::iterator_traits<ForwardIt>::difference_type count, step;
    count = std::distance(first, last);

    while (count > 0) {
        it = first; 
        step = count / 2; 
        std::advance(it, step);
        ErrorCode error = ConvertToTime(*it, time);
        if(error.errorCode() == SUCCESS)
        {
            if (*it < value) {
                first = ++it; 
                count -= step + 1; 
            }
            else
                count = step;
            }
        else {
            // skip/ignore this point?
        }
    }
    return first;
}

我的终极解决方案(可能听起来很愚蠢)是让这个方法成为列表的变异器,并擦除无效的元素。有更清洁的解决方案吗?

1 个答案:

答案 0 :(得分:2)

您只需按optional<ptime>索引即可。如果要缓存转换后的值,请考虑将其设为multimap<optional<ptime>, File>

更好的是,创建一个表示文件的数据类型,并计算其构造函数中的时间点:

struct File {
     File(std::string fname) : _fname(std::move(fname)), _time(parse_time(_fname)) { }

      boost::optional<boost::posix_time::ptime> _time;
      std::string _fname;

      static boost::optional<boost::posix_time::ptime> parse_time(std::string const& fname) {
            // return ptime or boost::none
    }
};

现在,只需适当地定义operator<或使用例如boost :: multi_index_container索引_time

补充说明:

  1. 如果它不清楚,这样的地图/集合将拥有它自己的lower_boundupper_boundequal_range操作,并且显然也可以工作与std::lower_bound和朋友们合作。
  2. 总是filter_iterator适配器:http://www.boost.org/doc/libs/1_64_0/libs/iterator/doc/filter_iterator.html