用掩码搜索

Search with a mask

本文关键字:搜索 掩码      更新时间:2023-10-16

有以下类型的大数组项:

typedef struct {
    int value;
    int mask;
    int otherData;
} Entry;

我想根据提供的int key;尽快找到这个数组中的一个条目。该条目需要确保(key & mask) == value .

这种数组组织的最佳方式是什么?处理它的相应算法是什么?

编辑:数组组织没有限制;它是静态的,可以在查找之前准备好。valuemask可以是任意值。

Edit2: valuemask可以有任意的值,但是在数组中的表项数大约是10000。因此,某些"模式"可以提前计算出来。

查找的次数很大。

每个比特都是独立的,所以在预处理阶段[*],您可以将每个条目分类32次(或者无论您的int有多大)。每个分类存储2个集合:当key为0时在该位匹配的集合和当key为1时匹配的集合。

也就是说,如果value == 1且mask == 0,那么该分类根本不存储该条目,因为它与key的任何值都不匹配(实际上,无论使用哪种方案,在任何预处理阶段都应该删除此类条目,因此no分类应该存储条目,即使只有一个比特是这样的)。如果都为0,则存储到两个集合中。否则存储到两个集合中的一个。

然后,给定你的键,你想找到32个集合的快速交集。

根据原始数组的大小,存储每个集合的最佳方式可能是一个巨位数组,表明数组中的每个项是否在集合中。然后找到交集可以一次做一个字- &共32个字,每个位数组一个。如果结果是0,继续。如果结果不为0,就表示匹配,结果中设置的位告诉您哪个条目是匹配的。当然,这在数组的大小上仍然是线性的,实际上您要执行31个&操作来检查32个条目是否匹配,这与通过原始数组进行简单的线性搜索大致相同。但是比较和分支更少,你看到的数据更压缩,所以你可能会得到更好的性能。

或者可能有更好的方法来做交叉。

如果键倾向于被重用,那么应该在从键到条目的映射中缓存查找结果。如果可能的键的数量相当小(也就是说,如果可能的输入键明显少于2^32个,并且/或者您有很多可用的内存),那么您的预处理阶段可以只是:

  1. 依次取每个条目
  2. 找出可能匹配的键
  3. 将这些键添加到地图

[*]在没有任何预处理的情况下,显然你所能做的就是检查每个数组成员,直到找到匹配项,或者检查所有内容。

由于您没有额外的信息(例如,数组已排序),您需要线性搜索—遍历数组并检查条件—伪代码:

for( size_t index = 0; index < arraySize; index++ ) {
   if( ( array[index].mask & key ) == array[index].value ) ) {
      return index;
   }
}
return -1;
  • 如果你有一个键到条目的映射,那么这将非常容易。

  • 如果您的数组是按键排序的,那么您可以做一个字典排序的二进制搜索。 [实际上,也许不是!)

  • 实际上,您只需要遍历数组,直到找到要查找的。也就是说,从开始到结束迭代,当你找到它时停止。

作为题外话,这是一个很好的例子,说明数据结构的选择如何影响算法的可用性。如果你一开始就选择了错误的数据结构,你就不能把算法扔给问题!

线性搜索当然可以,但是如果您需要使用相同的键进行多次查找,您可以尝试首先根据(key & mask)对范围进行排序。如果你只有几个固定的键,你可以尝试使用boost。Multi_index,每个键值有一个索引。

如果每个条目的mask任意变化,我看不到太多替代线性搜索。如果有明显的限制mask,这样只有几个值是可能的,可能会更好对mask的每个值使用某种map,进行线性搜索查找包含您正在查找的值的第一个map。或者,如果mask只涉及几个位,那么它可能是值得的使用multimap, value用所有的and掩码排序mask s,与key索引处理相同,则为线性使用完整的key搜索以找到精确匹配。

如果掩码中的零位数很小,则可以为掩码中的每个"不关心"位复制条目。例如,如果value=0mask=0xfffe,那么您将在表中添加key=0key=1的条目。对于value=0mask=0xfeef,在表中放入4个条目:key=0x0000key=0x0010key=0x0100key=0x0110。现在您可以对条目进行排序并使用二进制搜索,或者使用二进制搜索结构,如std::map