我在密码本中有一堆 8 位值(大约 200 个)。
我的程序将生成一个 8 位值以响应输入,我需要在码本中找到所有(甚至第一个是有帮助的)具有相同位集的匹配项。未设置的位无关紧要。
您能想出一种最佳方法来 a) 存储和 b) 搜索码本以查找所有匹配项吗?我有一个标准的线性搜索,但当然效率很低。
非常感谢...
阿克万
当然,虽然空间效率不是很高,但您当然可以预先计算所有 256 位模式的匹配。您将拥有一个由 256 个列表组成的数组,每个列表将包含码本中设置了这些位的每个代码。
您可以获得 256 个字节(11 个内存字)的第一个匹配项。
初始化:
u_int8_t bitpatterns[256];
memset(bitpatterns,0,sizeof(bitpatterns));
for(x=sizeof(codebook)-1;x>=0;x--)
for(y=0;y<256;y++)
if (y&codebook[x] == y)
bitpatterns[y] = x;
抬头:
codeword = codebook[bitpatterns[input]];
如果您只进行 8 位查找,预先计算所有答案然后将它们存储在 256 条目表中将是微不足道的。这样,您将获得恒定的时间查询,并且内存存储量仅为 256 个条目。
一种优化可能是将代码存储在不同的存储桶中,具体取决于设置的位数。当您查找代码时,您只需查看 1/2 的代码(平均而言,如果代码分布均匀)。这是一个非常简单的优化,但算法的复杂度保持不变(O(n))。根据设置的位数对单个数组进行排序将使您能够进行类似的优化,而无需将代码存储在存储桶中。
旁注:我认为 200 是一个非常小的数字,并且我认为无论您如何优化它,除非您进行大量查找,否则您不会看到线性方法的性能有太大变化。但我猜这不是这个练习的重点......
如果我理解,您是说如果响应只设置了 1、3、5 位,那么您希望码本中的所有代码都设置了标志 1、3、5 并且您不关心 2、4、6 位,7,8。
如果是这样,这是您的伪代码:
matchingCodes = new List<Code>
foreach(code in codebook)
if((response & code) == response) matchingCodes.add(code);
我发布了另一个答案,因为我得到了一个新的(更好的建议):
该算法仍然是线性的,但是通过 O(log N) 查找来截断(希望)大多数值。查找小值仍然很昂贵,查找大值会更便宜。
您也可以使用布隆过滤器进行初始搜索以截断大多数情况,并对其余情况进行线性搜索。过滤器可能有误报,因此当过滤器返回 true 时,您必须进行线性搜索。这种数据结构要求您拥有大量独立的散列函数(例如,基于设置的位数、所有设置位的乘积、奇数与偶数、数字本身等)。如果您希望仅偶尔找到代码,这可能是一个很好的优化(如果过滤器返回 false,则保证代码不在代码簿中)。但是,我怀疑这比实际优化更具理论意义。