非常有趣的问题,聪明的技巧。
让我们看一个处理单个字节的简单示例。为了简单起见,使用无符号8位。假设你的数字是xxaxxbxx,你想要ab000000。
解决方案包括两个步骤:位掩码,然后是乘法。位掩码是一个简单的AND操作,将无意义的位转换为零。在上面的例子中,你的掩码是00100100,结果是00a00b00。
现在最难的部分是:把它变成ab.......
乘法是一堆移位加运算。关键是允许溢出“移走”我们不需要的位,并将我们想要的位放在正确的位置。
乘以4(00000100)会将所有元素向左移动2,得到a00b0000。为了让b上移,我们需要乘以1(让a保持在正确的位置)+ 4(让b上移)。这个和是5,加上前面的4,我们得到一个神奇的数字20,或00010100。掩模后原为00a00b00;乘法得到:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
通过这种方法,您可以扩展到更大的数字和更多的位。
你问的其中一个问题是"这能用任意数量的比特来完成吗?"我认为答案是“不”,除非你允许多次屏蔽操作,或者多次乘法。问题是“碰撞”的问题——例如,上面问题中的“杂散b”。想象一下,我们需要对xaxxbxxcx这样的数做这个运算。按照前面的方法,您可能认为我们需要{x 2, x {1 + 4 + 16}} = x 42(哦-所有问题的答案!)结果:
00000000a00b00c00
000000a00b00c0000
0000a00b00c000000
-----------------
0000a0ababcbc0c00
xxxxxxxxabc......
正如你所看到的,它仍然有效,但“只是刚刚”。这里的关键是在我们想要的比特之间有“足够的空间”,我们可以把所有东西都挤起来。我不能在c后面加第四个位d,因为我得到的实例是c+d,位可能会。
所以在没有正式的证明的情况下,我会这样回答你问题中更有趣的部分:“不,这对任何数量的比特都不适用。要提取N位,你需要在你想提取的位之间有(N-1)个空格,或者有额外的掩码乘法步骤。”
我能想到的“比特之间必须有(N-1)个零”规则的唯一例外是:如果你想提取原始数据中彼此相邻的两个比特,并且你想让它们保持相同的顺序,那么你仍然可以这样做。为了(N-1)规则的目的,它们被算作两位。
There is another insight - inspired by the answer of @Ternary below (see my comment there). For each interesting bit, you only need as many zeros to the right of it as you need space for bits that need to go there. But also, it needs as many bits to the left as it has result-bits to the left. So if a bit b ends up in position m of n, then it needs to have m-1 zeros to its left, and n-m zeros to its right. Especially when the bits are not in the same order in the original number as they will be after the re-ordering, this is an important improvement to the original criteria. This means, for example, that a 16 bit word
a...e.b...d..c..
可以转换成
abcde...........
尽管e和b之间只有一个空格,d和c之间只有两个空格,其他之间只有三个空格。N-1怎么了??在这种情况下,一个…E变成了“一个方块”——它们被乘以1,最终到达正确的位置,所以“我们免费得到了E”。对于b和d也是如此(b向右需要3个空格,d向左也需要3个空格)。所以当我们计算这个神奇的数字时,我们发现有重复的地方:
a: << 0 ( x 1 )
b: << 5 ( x 32 )
c: << 11 ( x 2048 )
d: << 5 ( x 32 ) !! duplicate
e: << 0 ( x 1 ) !! duplicate
显然,如果你想让这些数字以不同的顺序排列,你就必须把它们间隔得更远。我们可以重新表述(N-1)规则:“如果比特之间至少有(N-1)个空格,它总是有效的;或者,如果最终结果中比特的顺序是已知的,那么如果比特b最终位于第m (n)位,那么它的左边需要有m-1个零,右边需要有n-m个零。”
@Ternary指出,这个规则并不完全有效,因为可以从位中添加“刚好在目标区域的右侧”——也就是说,当我们要查找的位都是1时。继续上面的例子,在一个16位的字中有5个紧密排列的位:如果我们从
a...e.b...d..c..
为简单起见,我将位位置命名为ABCDEFGHIJKLMNOP
我们要做的计算是
ABCDEFGHIJKLMNOP
a000e0b000d00c00
0b000d00c0000000
000d00c000000000
00c0000000000000 +
----------------
abcded(b+c)0c0d00c00
到目前为止,我们认为任何低于abcde(位置abcde)的内容都无关紧要,但事实上,正如@Ternary指出的那样,如果b=1, c=1, d=1,那么位置G的(b+c)将导致位进位到位置F,这意味着位置F的(d+1)将位进位到E -,我们的结果被破坏了。注意,最不重要的位(本例中为c)右侧的空格无关紧要,因为乘法运算将导致从最不重要的位以外填充零。
所以我们需要修改(m-1)/(n-m)规则。如果有一个以上的位在右边有恰好(n-m)个未使用的位(不包括模式中的最后一位——上面的例子是“c”),那么我们需要加强规则——我们必须迭代地这样做!
我们不仅要看满足(n-m)标准的比特数,还要看(n-m+1)的比特数,等等。我们称它们的数字为Q0(正好是n-m到下一位),Q1 (n-m+1),直到Q(N-1) (N-1)。然后我们冒carry if的风险
Q0 > 1
Q0 == 1 && Q1 >= 2
Q0 == 0 && Q1 >= 4
Q0 == 1 && Q1 > 1 && Q2 >=2
...
如果你看这个,你会发现如果你写一个简单的数学表达式
W = N * Q0 + (N - 1) * Q1 + ... + Q(N-1)
,结果为W > 2 * N,则需要将RHS准则增加一位至(N -m+1)。此时,只要W < 4,操作就是安全的;如果这不起作用,就再增加一个标准,等等。
我认为遵循上面的方法会让你找到答案。