有大量条目具有以下类型:
typedef struct {
int value;
int mask;
int otherData;
} Entry;
我想根据提供的int key;
尽可能快地找到此数组中的条目。需要输入以确保(key & mask) == value
。
这种阵列组织的最佳方式是什么,处理它的相应算法是什么?
编辑:阵列组织没有限制;它是静态的,可以在查找之前准备好。 value
和mask
可能包含任意值。
Edit2:value
和mask
可能有任意值,但数组中的条目数约为10000.因此,某些“paterns”可以提前计算。
查找次数很多。
答案 0 :(得分:4)
每个位都是独立的,因此在预处理阶段[*]中,您可以对每个条目32进行分类(或者int
是多大)次。每个分类存储2组:当key
为0时匹配该位的那些和key
为1时匹配的那些。
也就是说,如果该位的值== 1且掩码== 0,则该分类根本不存储该条目,因为它与key
的任何值都不匹配(事实上,无论你使用什么方案,这些条目都应该在任何预处理阶段被删除,所以 no 分类应该存储一个条目,即使有一个位是这样的)。如果两者都为0,则存储到两个集合中。否则存入两组中的一组。
然后,根据你的密钥,你想找到一个32集的快速交集。
根据原始数组的大小,可能是存储每个集合的最佳方法是一个巨型位数组,指示数组中的每个条目是否在集合中。然后找到交叉点可以一次一个字 - &
一起完成32个字,每个位数组一个。如果结果为0,继续。如果结果为非0,则表示匹配,结果中设置的位告诉您哪个条目匹配。当然,这仍然是数组大小的线性,实际上你正在进行31 &
次操作来检查匹配的32个条目,这与通过原始数组的简单线性搜索大致相同。但是比较和分支的数量较少,而且您正在查看的数据压缩程度更高,因此您可能会获得更好的性能。
或者可能有更好的方法来进行交叉。
如果密钥往往被重复使用,那么您应该将查找结果缓存在从密钥到条目的映射中。如果可能的键的数量相当小(即,如果可能的输入大大少于2 ^ 32个键,和/或您有大量可用内存),那么您的预处理阶段可能只是:
[*]没有任何预处理,显然你所能做的就是检查每个阵列成员,直到你找到匹配或者你已经检查了所有内容。
答案 1 :(得分:2)
由于您没有额外的信息(例如,数组已排序),您需要进行线性搜索 - 遍历数组并检查条件 - 伪代码:
for( size_t index = 0; index < arraySize; index++ ) {
if( ( array[index].mask & key ) == array[index].value ) ) {
return index;
}
}
return -1;
答案 2 :(得分:1)
如果您有条目的地图键,那么这将非常简单。
如果您的数组是按键排序的,那么您可以稍作努力进行词典二进制搜索。 [实际上,也许不是!]
实际上,您只需要遍历数组,直到找到您要查找的内容。也就是说,从开始到结束迭代,并在找到它时停止。
另外,这是一个很好的例子,说明数据结构的选择如何影响算法的可用性。如果您首先选择了错误的数据结构,那么您不能仅仅针对问题抛出算法!
答案 3 :(得分:0)
线性搜索当然可行,但如果您需要使用相同的键进行多次查找,则可以尝试根据(key & mask)
首先对范围进行排序。如果您只有几个固定密钥,则可以尝试使用boost.multi_index,每个密钥值都有一个索引。
答案 4 :(得分:0)
如果每个条目的mask
任意变化,我看不太多
替代线性搜索。如果有重大限制
mask
,这样只有少数几个值,可能会更好
对map
的每个值使用某种mask
,进行线性搜索
找到包含您要查找的值的第一个map
。
或者,如果mask
仅涉及几位,则可能值得
使用multimap
,按value
排序,屏蔽and
全部
mask
s和key
索引处理相同,然后是线性的
使用完整的key
进行搜索以找到完全匹配。
答案 5 :(得分:0)
如果掩码中的零位数很小,则可以复制掩码中每个“无关”位的条目。例如,如果value=0
和mask=0xfffe
,您可以在key=0
和key=1
的表格中输入一个条目。对于value=0
和mask=0xfeef
,请在表格中添加4个条目:key=0x0000
,key=0x0010
,key=0x0100
和key=0x0110
。现在,您可以对条目进行排序并使用二进制搜索,或使用二进制搜索结构,例如std::map
。