【问题标题】:Fast partial matching with multi-component keys使用多分量键进行快速部分匹配
【发布时间】:2016-04-13 09:00:29
【问题描述】:

我有一大串由以下组成的多组件键 定义了比较运算符的 poi 数据类型:

typedef boost::tuple<int, char, unsigned long> Key;

我想将这些键与固定的集合模式进行匹配, 它由相同的组件组成,但特别是 模式某些组件可以省略:

typedef boost::tuple<
    boost::optional<int>
    , boost::optional<char>
    , boost::optional<unsigned long> > Pattern;

boost::optional 值未设置表示 星号,“匹配所有内容”:

Key(1, 2, 3) match Pattern(1, 2, *)
Key(1, 2, 3) match Pattern(*, 2, 3)

我想比 O(N) 更快地执行匹配,其中 N是模式的数量。

我从自定义比较运算符 1 开始,用于模式 将它们存储在排序向量中。 Operator1 只是排序 星号在其他所有内容之后。然后执行查询 使用 std::lower_bound 和自定义比较运算符2。 Operator2 在比较期间省略了未设置的关键组件。 但我想我无法摆脱单一排序的向量 因为如果第二个组件是 * 我省略了它 不保证对第三个组件的“切片”进行排序 我得到了一些对 std::lower_bound 有用的东西。

【问题讨论】:

  • 您需要每个“形状”搜索一个索引。构建索引的成本很高,但您可以使用 unordered_map 来获得搜索的平均恒定时间。

标签: c++ algorithm data-structures stl


【解决方案1】:

按某种顺序对键进行排序。为每个组件创建一个索引,保持相同的排序顺序。

使用索引查找每个组件的下一项。如果索引指向同一项目,则您有匹配项。如果不选择指向最小项目的组件(按排序顺序)并跳过,直到您至少处于最大项目(std::lower_bound 会这样做)。

这与排序列表相交的算法相同。

速度取决于数据的密集程度。如果您搜索 (*, *, true) 并且 95% 的数据匹配您将是 O(N)。如果数据足够稀疏,这可能会非常快。

【讨论】:

  • 最好为每个索引使用多个键,并尝试组织它们,以便这些有序键列表的 所有前缀 涵盖尽可能多的(无序)尽可能多的键集。例如。假设有 4 个字段,A、B、C 和 D,那么您可以创建 4 个索引:ABCD、BCD、CDA、DBA。现在,任何指定所有 4 个或 4 个字段中的任何 3 个字段的查询都可以用一个二进制搜索来回答,并且唯一需要您描述的那种连接的查询是那些只指定 AC 或只指定 AD 的查询。 (您当然可以再添加 2 个索引来处理这个问题。)
  • 我一次将一个键与一组模式匹配。比如说,我有 K(1,2,3) 和 [P(1,2,4),P(1,2,*)]。我应该使用什么复合索引?正如 Sorin 所提到的,我应该为每个组件创建索引,查询每个索引的组件值和 * 并与结果(六个)列表相交。
  • @j_random_hacker 你是对的,但我会把它更多地作为一种优化。性能取决于您可以多快跳过正在相交的列表,这取决于条目的不同程度。如果您制作复合键,您会显着增加这些字段的熵(速度如此之快),但它也会占用更多空间(特别是如果您尝试覆盖不同的字段组合)。除非您进行基准测试并注意到您需要更快,否则我不会打扰。
  • @Sorin:我很困惑;如果您将索引简单地表示为指向原始数据记录(即其中的整数位置)的指针数组,则任意数量键上的索引占用相同数量的空间。在 k 个键上构建索引需要(最多)k 倍于在 1 个键上构建一个,当然,但您只需要这样做一次。另外,我不确定您在这里所说的“熵”是什么意思。
  • 对于 n 个指定的字段,您的算法所花费的时间与每个字段的匹配并集大小的 n 倍成正比,再加上 n 个二进制搜索。如果你有正确的 n 键索引可用,我的时间与 intersection 的大小成正比,再加上一次二进制搜索。如果您没有所有 n 个键的单个索引,但您有 i
猜你喜欢
  • 2023-03-29
  • 2011-09-23
  • 1970-01-01
  • 2014-08-07
  • 2020-10-19
  • 2011-07-27
  • 2013-10-14
  • 2020-10-16
  • 2018-10-13
相关资源
最近更新 更多