非常有趣的问题,以及聪明的技巧。
让我们看一个简单的例子,如何操作一个字节。为了简单起见,使用无符号8位数。想象一下你的数字是xxaxxbxx
,你想要ab000000
。
解决方案由两步组成:位掩码,然后是乘法。位掩码是一个简单的与运算,将不感兴趣的位变成零。在上面的例子中,你的掩码将是00100100
,结果将是00a00b00
。
现在困难的部分:将其转换为ab......
。
乘法是一堆移位和加法操作。关键是允许溢出将我们不需要的位“移走”,并将我们想要的那些位放在正确的位置。
倍增4(00000100
)将把所有位向左移动2个单位,并使你达到a00b0000
。为了将b
向上移动,我们需要乘以1(保持a在正确的位置)+ 4(将b向上移动)。这个总和是5,加上之前的4,我们得到一个神奇的数字20,或00010100
。原始掩码后的值是00a00b00
;乘法产生:
000000a00b000000
00000000a00b0000 +
----------------
000000a0ab0b0000
xxxxxxxxab......
通过这种方法,您可以扩展到更大的数字和更多的比特位。
你问的一个问题是“是否可以对任意位数的数字进行这样的操作?” 我认为答案是“不行”,除非您允许进行多个掩码操作或多个乘法操作。问题在于“碰撞”的问题-例如上面问题中的“stray 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) 规则而言,它们计算为两位。
还有另一个洞见-受@Ternary的答案启发(请参见我的评论)。对于每个有趣的位,您只需要右侧需要放置位的空格数。但也需要左侧有和结果位数一样多的位数。因此,如果位 b 最终在 n 的位置 m 处,那么它需要在其左侧有 m-1 个零,并且在其右侧有 n-m 个零。特别是当位数在重新排序后与原始数字中的顺序不同时,这是对原始标准的重要改进。这意味着,例如,一个16位字
a...e.b...d..c..
可以被转换为
abcde...........
尽管 e 和 b 之间只有一个空格,d 和 c 之间有两个空格,其他单词之间有三个空格。N-1 发生了什么事?在这种情况下,a...e
变成了“一个块”——它们被乘以1放到正确的位置上,所以“我们免费得到了 e”。b 和 d 也是如此(b 需要向右移动三个空格,d 需要向左移动同样的三个空格)。因此,当我们计算魔术数字时,我们发现有重复的内容:
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最终出现在n的m位置,则其左侧需要有m-1个零,右侧需要有n-m个零。”
@Ternary指出,这个规则并不能完全适用,因为在目标区域右侧仅添加一位时就可能发生进位——即当我们要查找的位都是1时。继续上面给出的示例,在一个16位词中紧密包装了五个位:如果我们从以下数字开始:
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)。然后,如果我们满足以下条件,就会有进位的风险:
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 ,该操作就是安全的; 如果不起作用,请再增加一个标准,以此类推。
我认为遵循上述方法可以让您更接近答案......