如何获得跨步模式的校验和

时间:2009-02-28 18:42:16

标签: language-agnostic bit-manipulation checksum

我有64位数字(但只使用了42个低位),需要计算nn+m 4 位的总和, n+m*2n+m*3(注意:任何可以产生总和> 4的内容都无效)对于某些固定的m和n的每个值,它将所有位都放在数字中

作为使用m=3并给出16位数字

的示例
0010 1011 0110 0001

我需要计算

2, 3, 1, 2, 3, 0, 3

有没有人有任何(酷)想法来做到这一点?我很好,有点笨拙。


我目前的想法是将输入的位移副本对齐以对齐要求的值,然后构建一个逻辑树来执行4x 1位加法器。

v1 = In;
v2 = In<<3;
v3 = In<<6;
v4 = In<<9;

a1 = v1 ^ v2;
a2 = v1 & v2;
b1 = v3 ^ v4;
b2 = v3 & v4;
c2 = a1 & b1;
d2 = a2 ^ b2;

o1 = a1 ^ b1;
o2 = c2 ^ d2;
o4 = a2 & b2;

这最终会导致结果的位分布在3个不同的整数中,但很好。

编辑:碰巧我需要总和的直方图,这样做o4 o2&o1o2o1arr = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]; for(int i = 0; i < N; i++) { out[i] = arr[(In & 0b1001001001) % 30]; In >>= 1; } 给了我想要的东西。


第二种解决方案使用完美的哈希函数

{{1}}

这是通过注意到4个选择的比特只能采用16个模式并且(通过猜测和检查)它们可以使用mod 30被散列为0-15。从那里,计算值的表格给出了所需的总和。因为它只发生了4个步骤中的3个,我需要以这种方式工作。


P.S。

快速纠正王牌。快速的王牌清晰。我希望能够运行数百万次。

2 个答案:

答案 0 :(得分:2)

也许我疯了,但我很开心:D 此解决方案基于数据并行性的使用和伪造矢量cpu而不实际使用SSE内在函数或任何类似的东西。

unsigned short out[64];
const unsigned long long mask      = 0x0249024902490249ul;
const unsigned long long shiftmask = 0x0001000100010001ul;

unsigned long long t = (unsigned short)(in >> 38) | (unsigned long long)(unsigned short)(in >> 39) > 40) > 41) << 48;
t &= mask;
*((unsigned long long*)(out + 38)) = (t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask);

[... snipsnap ...]

t = (unsigned short)(in >> 2) | (unsigned long long)(unsigned short)(in >> 3) > 4) > 5) << 48;
t &= mask;
*((unsigned long long*)(out + 2)) = (t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask);

t = (unsigned short)in | (unsigned long long)(unsigned short)(in >> 1) << 16;
t &= mask;
*((unsigned int*)out) = (unsigned int)((t & shiftmask) + (t >> 3 & shiftmask) + (t >> 6 & shiftmask) + (t >> 9 & shiftmask));

<小时/> 通过重新排序计算,我们可以进一步显着减少执行时间,因为它大大减少了某些内容加载到QWORD中的次数。其他一些优化非常明显而且相当轻微,但总结另一个有趣的加速。

unsigned short out[64];
const unsigned long long Xmask = 0x249024902490249ull;
const unsigned long long Ymask = 0x7000700070007u;

unsigned long long x = (in >> 14 & 0xFFFFu) | (in >> 20 & 0xFFFFu) > 26 & 0xFFFFu) > 32) << 48;
unsigned long long y;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[32] = (unsigned short)(y >> 48);
out[26] = (unsigned short)(y >> 32);
out[20] = (unsigned short)(y >> 16);
out[14] = (unsigned short)(y      );

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[33] = (unsigned short)(y >> 48);
out[27] = (unsigned short)(y >> 32);
out[21] = (unsigned short)(y >> 16);
out[15] = (unsigned short)(y      );

[snisnap]

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[37] = (unsigned short)(y >> 48);
out[31] = (unsigned short)(y >> 32);
out[25] = (unsigned short)(y >> 16);
out[19] = (unsigned short)(y      );

x >>= 1;
x &= 0xFFFF000000000000ul;
x |= (in & 0xFFFFu) | (in >> 5 & 0xFFFFu) > 10 & 0xFFFFu) << 32;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[38] = (unsigned short)(y >> 48);
out[10] = (unsigned short)(y >> 32);
out[ 5] = (unsigned short)(y >> 16);
out[ 0] = (unsigned short)(y      );

[snipsnap]

x >>= 1;
y = x & Xmask;
y += y >> 6;
y += y >> 3;
y &= Ymask;
out[ 9] = (unsigned short)(y >> 16);
out[ 4] = (unsigned short)(y      );

在我的电脑上编译为64位二进制文​​件的本机c ++中的5000万次执行的运行时间(所有输出已验证匹配^^): 基于阵列的解决方案:~5700 ms
天真的硬编码解决方案:~4200毫秒
第一种解决方案:~2400 ms
第二种解决方案:~1600 ms

答案 1 :(得分:1)

我现在不想编码的建议是使用一个循环,一个数组来保存部分结果,并使用常数来一次拾取m个位。

loop 
   s[3*i] += x & (1 << 0);
   s[3*i+1] += x & (1 << 1);
   s[3*i+2] += x & (1 << 2);
   x >> 3;

这将在每个总和中选择太多位。但是你也可以跟踪中间结果,并在你去的时候从总和中减去,以便考虑那些可能不再存在的位。

loop 
   s[3*i] += p[3*i]   = x & (1 << 0);
   s[3*i+1] += p[3*i+1] = x & (1 << 1);
   s[3*i+2] += p[3*i+2] = x & (1 << 2);

   s[3*i] -= p[3*i-10];
   s[3*i+1] -= p[3*i-9];
   s[3*i+2] -= p[3*i-8];
   x >> 3;
当然,

使用适当的边界检查。

最快的方法是自己硬编码总和。

s[0] = (x & (1<<0)) + (x & (1<<3)) + (x & (1<<6)) + (x & (1<<9));

等。 (转换发生在编译时。)