给定两个32位数字,N和M,以及两个位,i和j。编写一种方法,将N和j之间的所有位设置为N等于M.

时间:2017-01-05 09:00:16

标签: python algorithm bit-manipulation bitwise-operators

  

您将获得两个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)算法那样。

问题:有没有办法获得适用于所有情况的恒定时间算法?

5 个答案:

答案 0 :(得分:7)

我认为该算法的作者假定j(在您的示例中为六个)的界限为独占;这可归结为26的范围是否应包含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'以及ij分别为68,我们希望结果为'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)

  1. 创建掩码m,为<i,j>

    之间的所有位设置位

    你可以使用算术位左移来创建2的幂,利用2的幂减去1是所有设置位到指数-1的数字 所以设置所有位<0,j>,然后将位清除到i-1

  2. 将位从M复制到N

    所以使用m清除N中的位,然后复制M位而不是它们。不要忘记将M左移i以符合您的情况......

  3. 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