【问题标题】:What are some checksum implementations that allow for incremental computation?有哪些允许增量计算的校验和实现?
【发布时间】:2015-06-03 19:15:33
【问题描述】:

在我的程序中,我有一组存储在专有哈希表中的集合。像所有哈希表一样,我需要为每个元素提供两个函数。首先,我需要用于插入的哈希值。其次,当有冲突时我需要一个比较功能。在我看来,校验和功能将是完美的。我可以在这两个函数中使用该值。不乏校验和函数,但我想知道是否有任何常用的函数我不需要引入库(在这方面我的公司是 PIA)。系统库就可以了。

但我还有一个额外的、更复杂的要求。我需要校验和是可增量计算的。也就是说,如果一个集合包含 A B C D E F 并且我从集合中减去 D,它应该能够返回一个新的校验和值,而无需再次遍历集合中的所有元素。这样做的原因是为了防止我的代码出现非线性。理想情况下,我希望校验和是独立的,但如果需要,我可以先对它们进行排序。有这样的算法吗?

【问题讨论】:

  • 您可以只保留一个单独的 uint32_t 计数器 - 将其初始化为零,然后每当您将项目添加到集合中时,计算项目的校验和并将其添加到您的计数器;并且每当您从集合中删除一个项目时,计算该项目的校验和并将其从您的计数器中减去。 (缓存每个项目的校验和以便您不必多次计算它是可选的)

标签: c++ checksum


【解决方案1】:

只需在您的集合中存储一个项目字典,以及它们对应的哈希值。集合的散列值是项的串联、排序散列的散列值。在 Python 中:

 hashes = '''dictionary of hashes in string representation'''
 # e.g.
 hashes = { item: hashlib.sha384(item) for item in items }

 sorted_hashes = sorted(hashes.values())
 concatenated_hashes = ''.join(sorted_hashes)
 hash_of_the_set = hashlib.sha384(concatenated_hashes)

我会使用 sha384 作为哈希函数,但您可能想尝试 Keccak-384。


因为(当然)没有长度仅为 32 位的加密哈希函数,所以您必须改用校验和,例如 Adler-32 或 CRC32。这个想法保持不变。最好在项目上使用 Adler32,在级联哈希上使用 crc32:

 hashes = { item: zlib.adler32(item) for item in items }

 sorted_hashes = sorted(hashes.values())
 concatenated_hashes = ''.join(sorted_hashes)
 hash_of_the_set = zlib.crc32(concatenated_hashes)

在 C++ 中,您可以使用 Adler-32CRC-32Botan

【讨论】:

  • 我应该提到,哈希键需要是 32 位。您将如何处理计算的增量性质?
【解决方案2】:

CRC 是根据输入计算的一组位。

如果您的输入与 CRC 大小相同(或更小)(在您的情况下 - 32 位),您可以找到创建此 CRC 的输入 - 实际上是反转它。

如果您的输入大于 32 位,但您知道除 32 位之外的所有输入,您仍然可以反转 CRC 以找到丢失的位。

但是,如果输入的未知部分大于 32 位,则无法找到它,因为存在不止一种解决方案。

我为什么要告诉你这个?想象一下你有集合的 CRC

{A,B,C}

假设您知道B 是什么,您现在可以轻松计算集合的CRC

{A,C}

(我的意思是“轻松” - 无需遍历整个 AC 输入 - 如您所愿)

现在您有 64 位描述 AC!而且由于我们不必遍历AC 的全部内容来做到这一点——这意味着即使我们缺少关于AC 的信息,我们也可以做到这一点。

所以看起来如果存在这样的方法,如果我们有它的 CRC,我们可以神奇地从输入中修复超过 32 个未知位。

这显然是错误的。这是否意味着没有办法做你想做的事?当然不是。但它确实限制了我们如何做到这一点:

选项 1:我们无法从 CRC({A,C}) 获得更多我们在 CRC({A,B,C}) 中没有的信息。这意味着AC 对CRC 的(相对)影响不会随着B 的移除而改变。基本上 - 这意味着在计算 CRC 时,我们在添加新元素时使用了一些“顺序不重要”的函数:

例如,我们可以使用CRC({A,B,C}) = CRC(A) ^ CRC(B) ^ CRC(C)(不是很好,好像 A 出现两次它是相同的 CRC,就好像它从未出现过一样),或 CRC({A,B,C}) = CRC(A) + CRC(B) + CRC(C)CRC({A,B,C}) = CRC(A) * CRC(B) * CRC(C)(确保 CRC(X)很奇怪,所以它实际上只是 31 位 CRC)或CRC({A,B,C}) = g^CRC(A) * g^CRC(B) * g^CRC(C)(其中^ 是电源 - 如果您想要加密安全,则很有用)等等。

选项 2: 我们确实需要所有 AC 来计算 CRC({A,C}),但是我们有一个数据结构,如果我们这样做的话,它会在时间上变得不是线性的已经计算好了CRC({A,B,C})

如果您特别想要 CRC32,并且不介意在计算后记住除 CRC 之外的更多信息(CRC 仍然是 32 位,但您记得数据结构为 O(len(A,B ,C)),您稍后将使用它来更有效地计算 CRC{A,C})

这将如何运作?许多 CRC 只是在输入上应用多项式。

基本上,如果您将输入划分为 n 每个 32 位的块 - X_1...X_n - 有一个矩阵 M 这样

CRC(X_1...X_n) = M^n * X_1 + ... + M^1 * X_n

(这里^是力量)

这有什么帮助?这个总和可以用树状方式计算:

CRC(X_1...X_n) = M^(n/2) * CRC(X_1...X_n/2) + CRC(X_(n/2+1)...X_n)

因此,您从树的叶子上的所有 X_i 开始,首先计算每个连续对的 CRC,然后将它们成对组合,直到获得所有输入的组合 CRC。

如果您记得节点上的所有部分 CRC,则只需执行 O(log(n)) 计算即可轻松删除(或添加)列表中任何位置的项目!


所以-据我所知,这是您的两个选择。我希望这不会太混乱:)

我个人会选择选项 1,因为它更简单……但生成的 CRC 不是标准的,而且不太……好。不像“CRC”。

干杯!

【讨论】:

  • 有趣的想法。如何计算 CRC(A)?你能从 32 位数字中创建一个 32 位数字吗?直到现在我一直在做选项(1),但它的缺点是大多数指针值不是随机数,而是分布在一个小范围内,所以一组中的 (X+128) 和 (X-128) 具有如果有意义的话,与 (X-16) 和 (X+16) 相同的哈希码。
  • @balor123 如果您在 (1) 中使用 ^ 作为您的操作,这是有道理的 - 因为异或相似的值会抵消大部分位。如果您使用+* 作为您的操作 - 这应该不是问题(但您会遇到其他问题)。在 (2) 中,我指的是任何线性 CRC(使用线性多项式) - 查看 CRC 的维基百科页面。除非您非常熟悉线性代数,否则选项 (2) 很难实现。
  • @balor123 另外 - 我刚刚注意到我使用 CRC 作为“校验和函数”的通用名称 - 很抱歉造成混淆 :) 所以当我写 CRC(a) 时,我并不是指任何具体的东西,而不是“你想要的任何校验和功能”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多