用掩码搜索
Search with a mask
有以下类型的大数组项:
typedef struct {
int value;
int mask;
int otherData;
} Entry;
我想根据提供的int key;
尽快找到这个数组中的一个条目。该条目需要确保(key & mask) == value
.
这种数组组织的最佳方式是什么?处理它的相应算法是什么?
编辑:数组组织没有限制;它是静态的,可以在查找之前准备好。value
和mask
可以是任意值。
Edit2: value
和mask
可以有任意的值,但是在数组中的表项数大约是10000。因此,某些"模式"可以提前计算出来。
查找的次数很大。
每个比特都是独立的,所以在预处理阶段[*],您可以将每个条目分类32次(或者无论您的int
有多大)。每个分类存储2个集合:当key
为0时在该位匹配的集合和当key
为1时匹配的集合。
也就是说,如果value == 1且mask == 0,那么该分类根本不存储该条目,因为它与key
的任何值都不匹配(实际上,无论使用哪种方案,在任何预处理阶段都应该删除此类条目,因此no分类应该存储条目,即使只有一个比特是这样的)。如果都为0,则存储到两个集合中。否则存储到两个集合中的一个。
然后,给定你的键,你想找到32个集合的快速交集。
根据原始数组的大小,存储每个集合的最佳方式可能是一个巨位数组,表明数组中的每个项是否在集合中。然后找到交集可以一次做一个字- &
共32个字,每个位数组一个。如果结果是0,继续。如果结果不为0,就表示匹配,结果中设置的位告诉您哪个条目是匹配的。当然,这在数组的大小上仍然是线性的,实际上您要执行31个&
操作来检查32个条目是否匹配,这与通过原始数组进行简单的线性搜索大致相同。但是比较和分支更少,你看到的数据更压缩,所以你可能会得到更好的性能。
或者可能有更好的方法来做交叉。
如果键倾向于被重用,那么应该在从键到条目的映射中缓存查找结果。如果可能的键的数量相当小(也就是说,如果可能的输入键明显少于2^32个,并且/或者您有很多可用的内存),那么您的预处理阶段可以只是:
- 依次取每个条目
- 找出可能匹配的键
- 将这些键添加到地图
[*]在没有任何预处理的情况下,显然你所能做的就是检查每个数组成员,直到找到匹配项,或者检查所有内容。
由于您没有额外的信息(例如,数组已排序),您需要线性搜索—遍历数组并检查条件—伪代码:
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=0
和mask=0xfffe
,那么您将在表中添加key=0
和key=1
的条目。对于value=0
和mask=0xfeef
,在表中放入4个条目:key=0x0000
、key=0x0010
、key=0x0100
和key=0x0110
。现在您可以对条目进行排序并使用二进制搜索,或者使用二进制搜索结构,如std::map
。
- 位移操作和位掩码未检测到重复字符
- OpenCV - 带有掩码的absdiff
- 生成前缀位掩码
- 如何从__m64值的 lsb 创建 8 位掩码?
- 如何对无符号长 int 进行位掩码?
- 删除K的背景掩码-意味着Python或C++中的集群/
- 如何在C++中优雅地处理位掩码
- 将uint64_t位掩码转换为 std::布尔数组
- 使输入二进制掩码适应 ITK 网格生成器
- 如何从 getifaddr 读取子网掩码
- 优化从子位掩码生成父位掩码
- 基于模式创建位掩码作为 constexpr
- 使用二进制掩码 C++ ITK 获取感兴趣区域
- C++中的运行时位复制(位掩码)
- 根据 IP 和掩码C++打印所有 IP
- C++设置"blank"或重置 ifstrean (ios) 的异常掩码
- OpenCV 检测带有掩码的斑点
- OPENCV:如何创建多边形形状的掩码
- 如何知道从子网掩码中搜索哪些IP地址
- 用掩码搜索