【问题标题】:Finding pair values in a given range在给定范围内查找对值
【发布时间】:2019-12-31 13:24:31
【问题描述】:

我有一个数组或 N 对 (v1, v2) 其中v1 <= v2。这些应该代表从v1 开始到v2 结束的时间事件。它们可以相等,则事件是瞬时的。数组按开始时间排序,v1

对于给定的范围(L, R),我想找到L <= v1 <= R or L <= v2 <= R 的任何对。这里的想法是让事件在给定范围内开始、发生或结束。

我的主要问题是效率。该数组可能包含数十万个事件。因此,仅对所有对进行线性搜索不是一种选择。

我阅读了一些关于 kd-tree 的信息,但它的问题是它排除了范围的边界并且只会返回 L <= v1 <= R AND L <= v2 <= R。也就是说,只会返回范围内实际发生的事件(开始和结束),而我需要开始或结束(或显然两者)。

我还考虑过保留 2 个查找表(我用双精度表示时间)

std::map<double, Event*> startPoints;
std::map<double, Event*> endPoints;

并在两者中使用std::find算法并合并结果。

只是寻找一个建议,这是一个好的解决方案还是有更聪明的方法。

编辑:

重新考虑,它更复杂。这是预期结果的示例

  • L
|---Ev1---|     |---Ev3---|     |---Ev5---|
        |---Ev2---|     |---Ev4---|
             |               |
             L               R

在这里,我想获得 Ev2(在范围内结束)、Ev3(在范围内发生)和 Ev4(在范围内开始)

  • L
|---Ev1---|     |---Ev3---|     |---Ev5---|
        |---Ev2---|     |---Ev4---|
                    |    |
                    L    R

在这里,我想获得 Ev3,因为它当前在该范围内运行,而 Ev4 在该范围内开始运行

  • L == R:如果我想知道某个时间点发生了什么
|---Ev1---|     |---Ev3---|     |---Ev5---|
        |---Ev2---|     |---Ev4---|
             |
             LR

这里我只想要 Ev2,因为它是当前唯一运行的。

【问题讨论】:

  • 看看 Boost.ICL。
  • 您仍然可以查看 Boost 以了解它们如何实现您的目标。然后使用这些想法提出/研究您自己的实现。
  • 你写L &lt;= v1 &lt;= R or L &lt;= v2 &lt;= R,然后开始、发生或结束,但这不一样。对于发生,你需要v1 &lt;= R and L &lt;= v2
  • @Yola:考虑到这一点,刚刚编辑了第一篇文章
  • 您是否在寻找区间树:en.wikipedia.org/wiki/Interval_tree?

标签: c++ algorithm c++11 c++14


【解决方案1】:

由于您需要处理三种情况 - 在给定范围内开始、发生或结束,我们可以将其分为三个部分。

  1. 开始:v1 位于[L,R]
  2. 结尾:v2 位于[L,R]

第三种情况可以表述为v1 &lt;= R and L &lt;= v2,但前两种情况部分覆盖了这种情况,所以我们将使用不同的表述来避免冲突:

  1. 正在发生:v1 &lt; L and R &lt; v2

好吧,如果我们可以按v1 对事件数组进行排序,则很容易以对数加上报告事件数的时间处理第一种情况。同样的技巧适用于第二种情况。

第三种情况比较棘手。来画吧:

粉色区域代表所有区间L &lt;= R。红点是一个间隔,绿色区域代表我们想要捕获的所有可能事件。要进行这样的捕获,可以使用k2-tree

【讨论】:

【解决方案2】:

使用索引方法很好 - 例如 Boost.ICL 解决方案。

话虽如此,您可以轻松地为此使用std::vector - 即使未排序 - 我认为只要您在大约 100.000 甚至 1.000.000 的范围内,您应该没问题(只要您存储实际值 - 不是向量中的指针,因为这可能很慢) - 确切的数字当然取决于您的阈值。

struct MyEvent {
  double v1;//you use double for time
  double v2;
};


std::vector<MyEvent> events;

这是一个使用 1.000.000 个元素的示例:

http://coliru.stacked-crooked.com/a/9a6d90348f6915e1

搜索在 42 毫秒内运行,其中包括一个比较副本和可选副本,而您的情况可能有点不同,但它是可比较的。

更进一步,您可以通过使用例如以某种方式并行化您的搜索来获得更多功能。 std::for_each.

【讨论】:

  • 42 ms 在某些情况下很多
  • @fjardon 确实,它可能是 - 这就是为什么我写它取决于借口更进一步。据说生成索引等也会产生成本,这就是数据库存在的原因等等。
【解决方案3】:

std::map -->求元素复杂度为O(logn) 如果您的密钥是唯一的并且您没有内存问题,则可以使用 std::unordered_map 摊销复杂性 (O1)。 此外,您不需要创建 2 个地图。 std::unordered_map&lt;double, std::pair&lt;Event*, Event*&gt;&gt; StartEndPoints;。 如果您的密钥不是唯一的,您可以使用std::unordered_multimap,但如果您的密钥会重复很多次,则查找复杂性可能会变为(开启)。 我建议不要将密钥类型作为double 传递。

std::hash<double> hashing.
auto temp = hashing(key). // decltype of temp will be size_t
std::unordered_map<std::size_t, std::pair<Event*, Event*>> StartEndPoints;

【讨论】:

  • std::unordered_map 在您想查找一系列元素时并不适合。
  • 从问题中,我了解到需要找到起点和终点,而不是该点中的所有元素。如果需要该范围内的所有元素,最好按照您的建议使用std::vector
猜你喜欢
  • 1970-01-01
  • 2014-03-29
  • 2015-07-28
  • 2016-10-29
  • 1970-01-01
  • 2020-10-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多