我有64位数字(但只使用了42个低位),需要计算n
,n+m
处 4 位的总和, n+m*2
和n+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&o1
,o2
,o1
和arr = [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。
快速纠正王牌。快速的王牌清晰。我希望能够运行数百万次。
答案 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));
等。 (转换发生在编译时。)