您将获得两个32位数字,N和M,以及两个位位置,i 和j。编写一种方法,将N和j之间的所有位设置为N等于M. (例如,M成为位于i并从j开始的N的子串)。 示例:输入:N = 10000000000,M = 10101,i = 2,j = 6输出:N = 10001010100
这个问题来自Cracking the Coding的采访。我能够使用以下O(j - i)算法解决它:
def set_bits(a, b, i, j):
if not b: return a
while i <= j:
if b & 1 == 1:
last_bit = (b & 1) << i
a |= last_bit
else:
set_bit = ~(1 << i)
a &= set_bit
b >>= 1
i += 1
return a
作者将此O(1)算法作为解决方案:
def update_bits(n, m, i, j):
max = ~0 # All 1s
# 1s through position j, then zeroes
left = max - ((1 << j) - 1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
我注意到,对于某些测试用例,作者的算法似乎输出了错误的答案。例如,对于N = 488,M = 5,i = 2,j = 6,它输出468.当输出应该是404时,就像我的O(j - i)算法那样。
问题:有没有办法获得适用于所有情况的恒定时间算法?
答案 0 :(得分:7)
我认为该算法的作者假定j
(在您的示例中为六个)的界限为独占;这可归结为2
到6
的范围是否应包含6
的问题(在Python中不是这种情况)。换句话说,如果算法被修改为:
def update_bits(n, m, i, j):
max = ~0 # All 1s
# 1s through position j, then zeroes
left = max - ((1 << (j+1)) - 1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
有效。
然而,您可以按照以下步骤加快速度:
def update_bits(n, m, i, j):
# 1s through position j, then zeroes
left = (~0) << (j+1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
在这个例子中,我们只是将它们移出寄存器。
请注意您在自己的算法中出错,如果b = 0
,这并不意味着您只能返回a
,因为对于该范围,位应该清除。分别说a = '0b1011001111101111'
和b = '0b0'
以及i
和j
分别为6
和8
,我们希望结果为'0b1011001000101111'
。因此算法应该是:
def set_bits(a, b, i, j):
while i <= j:
if b & 1 == 1:
last_bit = (b & 1) << i
a |= last_bit
else:
set_bit = ~(1 << i)
a &= set_bit
b >>= 1
i += 1
return a
如果我进行此修改并使用10&000; 000&#39; 000随机输入测试程序,则两种算法始终产生相同的结果:
for i in range(10000000):
m = randint(0,65536)
i = randint(0,15)
j = randint(i,16)
n = randint(0,2**(j-i))
if set_bits(m,n,i,j) != update_bits(m,n,i,j):
print((bin(m),bin(n),i,j,bin(set_bits(m,n,i,j)),bin(update_bits(m,n,i,j)))) #This line is never printed.
当然这不是证明这两种算法都是等价的(也许它们有一个很小的角落,但它们有所不同),但我对确保有效输入非常有信心({{1} }和i
肯定,j
等)都应该始终产生相同的结果。
答案 1 :(得分:4)
我认为提议的解决方案中存在一个错误。
应该是:
def update_bits(n, m, i, j):
max = ~0 # All 1s
# 1s through position j + 1, then zeroes
left = max - ((1 << (j + 1)) - 1)
# 1s after position i
right = ((1 << i) - 1)
# 1’s, with 0s between i and j
mask = left | right
# Clear i through j, then put m in there
return (n & mask) | (m << i)
因为它说我们应该从j
开始填充到i
,所以我们也需要清除位j
。结果是404符合预期。
为了更进一步,如果m
的位数超过(j - i + 1)
,我们需要更改return语句:
return (n & mask) | ((m << i) & ~mask)
答案 2 :(得分:2)
创建掩码m
,为<i,j>
你可以使用算术位左移来创建2的幂,利用2的幂减去1是所有设置位到指数-1的数字
所以设置所有位<0,j>
,然后将位清除到i-1
将位从M
复制到N
所以使用m
清除N
中的位,然后复制M
位而不是它们。不要忘记将M
左移i
以符合您的情况......
在 C ++ (抱歉不要使用python)是这样的O(1)
:
DWORD bitcopy(DWORD N,DWORD M,int i,int j)
{
DWORD m;
// set bits <0..j>
m =(2<<j)-1;
// clears <0..i)
if (i) m^=(2<<(i-1))-1;
// clear space for copied bits
N&=0xFFFFFFFF-m;
// copy bits M->N
N|=(M<<i)&m;
return N;
}
您也可以将 LUT 用于i,j
的{{1}}位部分...因为您有32位数字,如果您需要32位或64位数字对位移不太满意......
答案 3 :(得分:0)
此版本似乎也运行良好,提供i <= j
def set_bits(n, m, i, j):
mask = (1 << (j + 1)) - (1 << i)
return n & ~mask | (m << i) & mask
答案 4 :(得分:0)
这很容易,一旦您知道要做什么,就可以自己实施...
这里是32位示例:
给定:
n = 10000000000000000000000000000000000000
m = 10101
i = 2,j = 6
第1步:创建遮罩->
int x=(~0); //All Ones 11111111111111111111111111111111
int left=(1<<i)-1; // 11
int right=x-((1<<j)-1); // 11111111111111111111111111000000
int mask=left | right; // 11111111111111111111111111000011
第2步:清除给定数字“ n”中i和j之间的位->
int cleared = n & mask; // 10000000000000000000000000000000
第3步:将m放在i和j之间的n(清除位)中->
int ans= cleared | m<<i; 10000000000000000000000001010100