Algorithm 从ulong中删除位的快速方法
我想从64位字符串(由无符号长字符串表示)中删除位。我可以通过一系列掩码和移位操作来实现这一点,或者像下面的代码那样迭代每一位。有没有什么巧妙的方法可以让这个动作更快Algorithm 从ulong中删除位的快速方法,algorithm,bit-manipulation,Algorithm,Bit Manipulation,我想从64位字符串(由无符号长字符串表示)中删除位。我可以通过一系列掩码和移位操作来实现这一点,或者像下面的代码那样迭代每一位。有没有什么巧妙的方法可以让这个动作更快 public ulong RemoveBits(ulong input, ulong mask) { ulong result = 0; ulong readbit = 1; ulong writebit =1; for (int i = 0; i < 64; i++) {
public ulong RemoveBits(ulong input, ulong mask)
{
ulong result = 0;
ulong readbit = 1;
ulong writebit =1;
for (int i = 0; i < 64; i++)
{
if ((mask & readbit) == 0) //0 in the mask means retain that bit
{
if ((input & readbit) > 0)
{
result+= writebit;
}
writebit*=2;
}
readbit *= 2;
}
return result;
}
public ulong RemoveBits(ulong输入,ulong掩码)
{
ulong结果=0;
ulong readbit=1;
ulong writebit=1;
对于(int i=0;i<64;i++)
{
如果((mask&readbit)==0)//0在掩码中表示保留该位
{
如果((输入和读取位)>0)
{
结果+=可写息税前利润;
}
账面息税前利润*=2;
}
readbit*=2;
}
返回结果;
}
在性能关键的场景中,我需要执行RemoveBits
数百万次
它可能过于抽象而无济于事,但使用的不同掩码的数量虽然在编译时未知,但在运行时早期(在性能关键位之前)就确定了,并且可能少于100个。基本上,我使用位字符串来表示一个
n元组
,并且RemoveBits
投影到一个m元组
(mbool f; // conditional flag
unsigned int m; // the bit mask
unsigned int w; // the word to modify: if (f) w |= m; else w &= ~m;
w ^= (-f ^ w) & m;
// OR, for superscalar CPUs:
w = (w & ~m) | (-f & m);
在某些体系结构上,分支的缺乏可以弥补
这似乎是两倍的操作。例如,非正式的
AMD Athlon上的速度测试™ XP 2100+表明速度提高了5-10%。
Intel Core 2 Duo运行超标量版本的速度比
第一个。Glenn Slayden告诉我关于
2003年12月11日。Marco Yu与我分享了超标量版本
2007年4月3日,并在两天后提醒我输入错误
虽然有这种特殊的操作,但比特旋转黑客网站并没有 这个想法是离线计算一个可以放入以下模板的幻数列表。该模板由一个重复6=lg 64次的基本步骤组成:对k=1、2、…、6的输出位mod 2**k的索引进行校正,假设在每个步骤开始时索引是正确的mod 2**(k-1) 例如,假设我们希望转换
x = a.b..c.d
76543210
进入
位a
位于位置7
,需要转到3
(正确位置mod 2)。位b
位于位置5
,需要转到2
(mod 2位置不正确)。位c
位于位置2
,需要转到1
(mod 2位置不正确)。位d
位于位置0
,需要保持(正确位置mod 2)。第一个中间步骤是这样移动b
和c
a..b..cd
76543210
这是通过以下方式实现的:
x = (x & 0b10000001) | ((x >>> 1) & 0b00010010);
//76543210 //76543210
这里,>>
表示逻辑移位,0bxxxxxxxx
表示大端二进制文字。现在我们剩下两个问题:一个是奇数索引位,另一个是偶数-。这个算法之所以快速,是因为现在可以并行处理这些问题
为完整起见,其他两个操作如下所示。位a
现在位于位置7
,需要转到3
(正确位置mod 4)。位b
现在位于位置6
,需要转到4
(mod 4位置不正确)。位c
和d
需要保留(mod 4的正确位置)。得到
是的
位a
现在位于位置7
,需要转到3
(8模位置不正确)。位b
、c
和d
需要保留(正确位置mod 8)。得到
是的
这里有一些Python概念的证明(对不起)
def计算掩码对(保留索引):
掩码对=[]
保留索引=已排序(保留索引)
移位=1
while(保留的索引!=列表(范围(len(保留的索引))):
mask0=0
mask1=0
对于枚举(保留索引)中的(i,j):
断言(我这被称为。不幸的是,没有特殊的硬件支持(作为一种新的硬件支持存在),没有真正的好方法来实现它。以下是黑客喜悦中给出的一些方法,修改为C#和64位ulong
s,但未经测试:
ulong compress(ulong x, ulong m) {
ulong r, s, b; // Result, shift, mask bit.
r = 0;
s = 0;
do {
b = m & 1;
r = r | ((x & b) << s);
s = s + b;
x = x >> 1;
m = m >> 1;
} while (m != 0);
return r;
}
ulong压缩(ulong x,ulong m){
ulong r,s,b;//结果,移位,掩码位。
r=0;
s=0;
做{
b=m&1;
r=r |((x&b)>1;
m=m>>1;
}而(m!=0);
返回r;
}
这样做的好处是分支比问题中的代码少得多
还有一种方法,循环迭代次数要少得多,但步骤要复杂得多:
ulong compress(ulong x, ulong m) {
ulong mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0's to right.
for (i = 0; i < 6; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mp = mp ^ (mp << 32);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
ulong压缩(ulong x,ulong m){
乌隆mk、mp、mv、t;
int i;
x=x&m;//清除不相关的位。
mk=~m什么语言?如果这太抽象而不能局限于一种语言,那么在“计算机科学”中可能会更好SE站点。所以你想删除input
中设置在mask
中的所有位吗?然后试试input&~mask
@Gumbo-我不只是想置零一点,我还想右移任何位到它的左边。@conductor。我使用的是C#。我发布的或多或少是第二个版本,带有x
独立的计算机我相信你的话,它看起来很复杂
x = (x & 0b10000011) | ((x >>> 2) & 0b00000100);
//76543210 //76543210
....abcd
76543210,
x = (x & 0b00000111) | ((x >>> 4) & 0b00001000);
//76543210 //76543210
def compute_mask_pairs(retained_indexes):
mask_pairs = []
retained_indexes = sorted(retained_indexes)
shift = 1
while (retained_indexes != list(range(len(retained_indexes)))):
mask0 = 0
mask1 = 0
for (i, j) in enumerate(retained_indexes):
assert (i <= j)
assert ((i % shift) == (j % shift))
if ((i % (shift * 2)) != (j % (shift * 2))):
retained_indexes[i] = (j - shift)
mask1 |= (1 << j)
else:
mask0 |= (1 << j)
mask_pairs.append((mask0, mask1))
shift *= 2
return mask_pairs
def remove_bits_fast(mask_pairs, x):
for (log_shift, (mask0, mask1)) in enumerate(mask_pairs):
x = ((x & mask0) | ((x >> (2 ** log_shift)) & mask1))
return x
def remove_bits_slow(retained_indexes, x):
return sum(((((x // (2 ** j)) % 2) * (2 ** i)) for (i, j) in enumerate(sorted(retained_indexes))))
def test():
k = 8
for mask in range((2 ** k)):
retained_indexes = {i for i in range(k) if (((mask // (2 ** k)) % 2) == 0)}
mask_pairs = compute_mask_pairs(retained_indexes)
for x in range((2 ** k)):
assert (remove_bits_fast(mask_pairs, x) == remove_bits_slow(retained_indexes, x))
test()
ulong compress(ulong x, ulong m) {
ulong r, s, b; // Result, shift, mask bit.
r = 0;
s = 0;
do {
b = m & 1;
r = r | ((x & b) << s);
s = s + b;
x = x >> 1;
m = m >> 1;
} while (m != 0);
return r;
}
ulong compress(ulong x, ulong m) {
ulong mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0's to right.
for (i = 0; i < 6; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mp = mp ^ (mp << 32);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}