【问题标题】:std::lower_bound with skipping invalid elements跳过无效元素的 std::lower_bound
【发布时间】:2017-06-12 10:29:46
【问题描述】:

我有一个文件名列表,每个文件名代表一个时间点。该列表通常包含数千个元素。给定一个时间点,我想把这些文件名转换成时间对象(我用的是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;
}

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

【问题讨论】:

  • 你能实现你自己的Compare 并传递它吗?
  • @Incomputable 由于搜索方法的单调性,这是一个不切实际的解决方案。比较器将返回真/假。当文件名无效时,此比较器返回的答案是什么?
  • 所以无效的没有任何订购信息?
  • @Incomputable 不!想想看……我提供的列表示例中抛出的随机文件。它怎么可能与任何时间点相关联?它实际上是一个无效文件,应该完全忽略。

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


【解决方案1】:

您可以简单地按optional&lt;ptime&gt; 进行索引。如果要缓存转换后的值,请考虑将其设为 multimap&lt;optional&lt;ptime&gt;, File&gt;

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

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&lt; 或使用例如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

【讨论】:

  • 我无法全面了解如何使用该类。首先,你的意思是:multimap&lt;File, optional&lt;ptime&gt;&gt;,因为我有文件列表和一个时间点来比较?同样在该类中,您在构造时调用 parse 函数。如果所有文件都在一个数组中,那不会解析所有文件名吗?你能解释一下你想把它放在一起的食谱吗?
  • 我给了你一些配料。您的食谱将取决于您想冲泡哪种汤。
猜你喜欢
  • 2015-10-27
  • 2011-11-21
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
相关资源
最近更新 更多