【问题标题】:Is Fletchers16 checksum suitable for small data?Fletchers16校验和适合小数据吗?
【发布时间】:2017-08-11 10:37:46
【问题描述】:

使用wikipedia Fletcher's checksum 上的直接实现,我们可以得到相同的校验和,例如“BCA”和“CAB”以及“BAC”和“ACB”。

这是预期的吗?
Fletcher16 校验和不应该考虑块的顺序吗?

可以通过将索引与数据进行 OR' 运算来轻松修复缺陷,如下面的代码所示......

uint16_t fletcher16( uint8_t *data, int count )
{
   uint16_t sum1 = 0;
   uint16_t sum2 = 0;
   int index;

   for( index = 0; index < count; ++index )
   {
      //sum1 = (sum1 + data[index]) % 255; // Original
      sum1 = (sum1 + index | data[index]) % 255; // The "fix"
      sum2 = (sum2 + sum1) % 255;
   }

   return (sum2 << 8) | sum1;
}

【问题讨论】:

  • “OR'ing the index”会丢弃信息。 “对索引进行异或”或“添加”会更有意义。
  • 用 XOR 代替 OR 也会改变问题..
  • @MichaelFoukarakis 更改为 XOR 不会更改 3 个问题的答案,因为它们都引用了缺少 (X)OR index 的发起者算法。它确实会影响建议改进的质量。

标签: c checksum


【解决方案1】:

...我们得到相同的校验和...这是预期的吗?

是的,因为这是可能的。校验和是 16 位的(511 种组合永远不会出现:0x..FF0xFF..)所以 3 个 24 位的字符串肯定会发生冲突。 Pigeonhole principle

Fletcher16 校验和不应该考虑块的顺序吗?

确实如此。只是该算法很容易与选择的相似输入发生冲突。另见Hamming distance

顺便说一句,如果使用字符串的长度或大小(还要检查 null 字符),原始算法会给出不同的结果。此外,4 个输入字符串给出了一对不同的结果。

  printf("%x\n", fletcher16("BCA",3)); // 8ec6
  printf("%x\n", fletcher16("CAB",3)); // 8ec6 same
  printf("%x\n", fletcher16("BAC",3)); // 8cc6 
  printf("%x\n", fletcher16("ACB",3)); // 8cc6 same

  printf("%x\n", fletcher16("BCA",4)); // 55c6
  printf("%x\n", fletcher16("CAB",4)); // 55c6 same
  printf("%x\n", fletcher16("BAC",4)); // 53c6
  printf("%x\n", fletcher16("ACB",4)); // 53c6 same

OP 的建议改进也削弱了校验和,就像在索引中进行 or-ing 一样,它忽略了每个阶段的选择位。建议进行异或或添加。


轻微尼特:

// return (sum2 << 8) | sum1;
return (1u*sum2 << 8) | sum1;

此更改不会对所有 int/unsigned 大小产生影响,但可以避免 int/unsigned 为 16 位时实现定义的行为。最好确保代码不会左移到符号位。

some_int % 255 执行有符号余数。在设备上,例如简单的嵌入式设备,无符号余数肯定同样快或更快。 % 255u 不会丢失任何东西,但可能会有所改进。

[编辑]

虽然 OP 对短字符串有“固定”代码,但它破坏了 fletcher16() 的设计参数,即执行速度。


详细信息:如果我们把%255sum1data[0] + ... + data[count-1]) 和sum2data[0]*(count) + data[0]*(count-1) + ... + data[count-1]*(1) 放在一边,很容易创建1、2、3 等长字符串,它们的低值很少引起,如果有的话,%255 操作。

请注意,sum2 是根据顺序有效创建不同校验和的部分。如果数组元素的总和永远不会达到 255(这发生在 OP 的 4 个测试用例中),sum1 将与任何 2 个仅在顺序上不同的字符串相同。

为了有效地“混合/散列”具有低值的短字符串,需要一种不同的算法。

可能只在count &lt; 8时使用变体:

sum1 = (sum1 + index + data[index]) % 255;

【讨论】:

  • 感谢您的详细解答。对我来说,听起来更简单的解决方案是在输入数据中添加一些“种子”。例如只需使用 data[0]=254, data[1]='A', data[2]='B'....等增加输入数据...当然接收端必须处理额外的字节就像它需要处理修改后的 fletcher 校验和一样。
  • @Waxhead 也许uint16_t sum1 = SEED1; uint16_t sum2 = SEED2; 而不是 .fletcher16() 是速度、代码占用和结果质量的折衷。最佳答案取决于典型数据流(ASCII、随机二进制等)、处理器能力(是否内置 %、本机整数大小)等。祝你好运。
猜你喜欢
  • 2021-01-09
  • 2014-10-24
  • 2013-06-21
  • 1970-01-01
  • 2011-04-21
  • 2019-01-30
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多