找到具有相同权重的最接近的整数O(1)

时间:2016-07-22 10:08:37

标签: algorithm runtime

我正在解决这个问题:

整数的二进制表示形式的计数称为该数字的权重。以下算法查找具有相同权重的最接近的整数。例如,对于123(0111 1011)2,最接近的整数是125(0111 1101)2。

O(n)的解决方案 其中n是输入数字的宽度,是通过交换不同的第一对连续位的位置。

有人可以在O(1)运行时和空间中给我一些解决方法吗?

由于

11 个答案:

答案 0 :(得分:8)

正如ajayv所评论的那样,在O(1)中无法真正完成,因为答案总是取决于输入的位数。但是,如果我们将O(1)解释为意味着我们将一些原始整数数据作为输入,并且我们对该整数执行的所有逻辑和算术运算都是O(1)(在位上没有循环),问题可以在恒定的时间内解决。当然,如果我们从32位整数更改为64位整数,则运算时间会增加,因为算术运算在硬件上需要更长的时间。

一种可能的解决方案是使用以下功能。第一个给出一个数字,其中只设置x的最低设置位

int lowestBitSet(int x){
  ( x & ~(x-1) ) 
}

和第二个未设置的最低位

int lowestBitNotSet(int x){
  return ~x & (x+1);
}

如果你在纸上做了很少的例子,你会看到它们是如何工作的。

现在,您可以使用这两个函数找到需要更改的位,然后使用您已描述的算法。

c ++实现(不检查没有答案的情况)

unsigned int closestInt(unsigned int x){
  unsigned int ns=lowestBitNotSet(x);
  unsigned int s=lowestBitSet(x);
  if (ns>s){
    x|=ns;
    x^=ns>>1;
  }
  else{
    x^=s;
    x|=s>>1;
  }
  return x;
}

答案 1 :(得分:2)

问题可以被视为" 哪些不同的位以交换数字的位表示形式,因此结果数字最接近原始数字?"

所以,如果我们要在指数k1&处交换位。 k2k2 > k1,数字之间的差异为2^k2 - 2^k1。我们的目标是尽量减少这种差异。假设位表示不是全0或全1,如果我们将|k2 - k1|保持为最小值,则简单的观察结果会产生差异。最小值可以是1.因此,如果我们能够从最低有效位(索引= 0)开始找到两个连续的不同位,我们的工作就完成了。

从最低有效位到最右边设置位的位均为1的情况

                                   k2
                                   |
                         7 6 5 4 3 2 1 0
                         ---------------
                      n: 1 1 1 0 1 0 1 1

        rightmostSetBit: 0 0 0 0 0 0 0 1

     rightmostNotSetBit: 0 0 0 0 0 1 0 0 rightmostNotSetBit > rightmostSetBit so,

             difference: 0 0 0 0 0 0 1 0 i.e. rightmostNotSetBit - (rightmostNotSetBit >> 1):  
                         ---------------
         n + difference: 1 1 1 0 1 1 0 1

从最低有效位到最右设置位的位均为0的情况

                                   k2
                                   |
                         7 6 5 4 3 2 1 0
                         ---------------
                      n: 1 1 1 0 1 1 0 0

        rightmostSetBit: 0 0 0 0 0 1 0 0

     rightmostNotSetBit: 0 0 0 0 0 0 0 1 rightmostSetBit > rightmostNotSetBit so,

             difference: 0 0 0 0 0 0 1 0 i.e. rightmostSetBit -(rightmostSetBit>> 1)
                         ---------------
         n - difference: 1 1 1 0 1 0 1 0

边缘情况,当然是我们拥有全0或全1的情况。

    public static long closestToWeight(long n){
        if(n <= 0 /* If all 0s */ || (n+1) == Integer.MIN_VALUE /* n is MAX_INT */)
           return -1;
       long neg = ~n;
       long rightmostSetBit = n&~(n-1);
       long rightmostNotSetBit = neg&~(neg-1);
       if(rightmostNotSetBit > rightmostSetBit){
           return (n + (rightmostNotSetBit - (rightmostNotSetBit >> 1)));
       }
       return (n - (rightmostSetBit - (rightmostSetBit >> 1)));
}

答案 2 :(得分:2)

要在 O(1)时间复杂度下解决此问题,可以考虑存在两种主要情况:

  

1)当 LSB '0'时:

在这种情况下,第一个'1'必须向右移动一个位置。

Input : "1000100**0**" 

Out ::: "10000100" 
  

2)当 LSB '1'时:

在这种情况下,必须将第一个“ 0”设置为“ 1”,并且必须将第一个“ 1”设置为“ 0”。

Input : "1000011**1**" 

Out ::: "10001110" 

Java中的下一个方法代表一个解决方案。

private static void findClosestInteger(String word) { // ex: word = "10001000"

    System.out.println(word);           // Print initial binary format of the number
    int x = Integer.parseInt(word, 2);  // Convert String to int

    if((x & 1) == 0) {                  // Evaluates LSB value
        // Case when LSB = '0':
        // Input:  x = 10001000

        int firstOne = x & ~(x -1);     // get first '1' position (from right to left)
        // firstOne = 00001000
        x = x & (x - 1);                // set first '1' to '0'
        // x = 10000000
        x = x | (firstOne >> 1);        // "shift" first '1' with one position to right
        // x = 10000100

    } else {
        // Case when LSB = '1':
        // Input: x = 10000111

        int firstZero = ~x & ~(~x - 1); // get first '0' position (from right to left)
        // firstZero = 00001000
        x = x & (~1);                   // set first '1', which is the LSB, to '0'
        // x = 10000110
        x = x | firstZero;              // set first '0' to '1'
        // x = 10001110

    }

    for(int i = word.length() - 1; i > -1  ; i--) {       // print the closest integer with same weight
        System.out.print("" + ( ( (x & 1 << i) != 0) ? 1 : 0) );
    }
}

答案 3 :(得分:2)

尝试使用Python解决该问题。可以看作是Ari's solution的译文,并处理了小写字母:

def closest_int_same_bit_count(x):
    # if all bits of x are 0 or 1, there can't be an answer
    if x & sys.maxsize in {sys.maxsize, 0}:
        raise ValueError("All bits are 0 or 1")

    rightmost_set_bit = x & ~(x - 1)
    next_un_set_bit = ~x & (x + 1)

    if next_un_set_bit > rightmost_set_bit:
        # 0 shifted to the right e.g 0111 -> 1011
        x ^= next_un_set_bit | next_un_set_bit >> 1
    else:
        # 1 shifted to the right 1000 -> 0100
        x ^= rightmost_set_bit | rightmost_set_bit >> 1

    return x

下面类似地提供了拼图游戏的solution

def closest_int_same_bit_count(x):
    # if all bits of x are 0 or 1, there can't be an answer
    if x & sys.maxsize in {sys.maxsize, 0}:
        raise ValueError("All bits are 0 or 1")

    rightmost_set_bit = x & ~(x - 1)
    next_un_set_bit = ~x & (x + 1)

    if next_un_set_bit > rightmost_set_bit:
        # 0 shifted to the right e.g 0111 -> 1011
        x += next_un_set_bit - (next_un_set_bit >> 1)
    else:
        # 1 shifted to the right 1000 -> 0100
        x -= rightmost_set_bit - (rightmost_set_bit >> 1)

    return x

答案 4 :(得分:1)

Java解决方案:

public function update(Request $request, $id){
      PropertyAdvert::where('id', $id)->where('user_id', Auth::id()->update
      ([
        "photo"       => base64_encode(file_get_contents($request->photo->path())),
        "address"     => $request->address,
        "county"      => $request->county,
        "town"        => $request->town,
        "type"        => $request->type,
        "rent"        => $request->rent,
        "date"        => $request->date,
        "bedrooms"    => $request->bedrooms,
        "bathrooms"   => $request->bathrooms,
        "furnished"   => $request->furnished,
        "description" => $request->description,
        "user_id" => Auth::id(),
      ]));

      return back();
    }

答案 5 :(得分:0)

static void findClosestIntWithSameWeight(uint x)
{
    uint xWithfirstBitSettoZero = x & (x - 1);
    uint xWithOnlyfirstbitSet = x & ~(x - 1);
    uint xWithNextTofirstBitSet = xWithOnlyfirstbitSet >> 1;

    uint closestWeightNum = xWithfirstBitSettoZero | xWithNextTofirstBitSet;

    Console.WriteLine("Closet Weight for {0} is {1}", x, closestWeightNum);

}

答案 6 :(得分:0)

python中的代码:

def closest_int_same_bit_count(x):
    if (x & 1) != ((x >> 1) & 1):
        return x ^ 0x3

    diff = x ^ (x >> 1)
    rbs = diff & ~(diff - 1)
    i = int(math.log(rbs, 2))

    return x ^ (1 << i | 1 << i + 1)

答案 7 :(得分:0)

有关此问题的详细说明,请参见EPI中的问题4.4。
(节目采访的要素)

如果您不拥有这本书,另一个地方是geeksforgeeks.org上的此链接。
(时间复杂度在此链接上可能是错误的)

您应该记住两件事(如果您想自己解决这个问题,请提示):
您可以使用x & (x - 1)来清除最低的设置位(不要与LSB混淆-最低有效位)
您可以使用x & ~(x - 1)来获取/提取最低设置位

如果您知道O(n)解决方案,那么您知道我们需要找到与LSB不同的第一位的索引。
如果您不知道LBS是什么:

0000 0000
        ^ // it's bit all the way to the right of a binary string.

采用以2为底的数字1011 1000(十进制为184)
与LSB不同的第一位:

1011 1000
     ^ // this one

我们将其记录为K1 = 0000 1000
然后,我们需要将其与右边的下一个交换它:

0000 1000
      ^ // this one

我们将其记录为K2 = 0000 0100
按位或K1和K2在一起,您将得到一个掩码
mask = K1 | k2 // 0000 1000 | 0000 0100 -> 0000 1100
将掩码与原始数字按位异或,您将获得正确的输出/交换
number ^ mask // 1011 1000 ^ 0000 1100 -> 1011 0100

现在,在将所有内容组合在一起之前,我们必须考虑LSB可能为0001的事实,在此之后的1000 1111可能还会有一堆比特。因此,我们必须处理第一位与LSB不同的两种情况。可能是1或0。

首先,我们有一个条件将LSB测试为1或0:x & 1
IF 1 return x XORed with the return of a helper function
该辅助函数具有第二个参数,其值取决于条件是否为真。 func(x, 0xFFFFFFFF) // if true // 0xFFFFFFFF 64 bit word with all bits set to 1
否则,我们将跳过if语句,并返回相似的表达式,但为第二个参数提供不同的值。
return x XORed with func(x, 0x00000000) // 64 bit word with all bits set to 0. You could alternatively just pass 0 but I did this for consistency

我们的辅助函数返回一个掩码,该掩码将与原始数字进行XOR运算以获取输出。 该表达式使用两个参数,我们的原始数字和掩码:
(x ^ mask) & ~((x ^ mask) - 1)
这给我们一个新的数字,索引K1的位始终设置为1。
然后将第1位右移(即索引K2),然后对其进行“或”运算以创建最终的掩码
0000 1000 >> 1 -> 0000 0100 | 0001 0000 -> 0000 1100

全部用C ++实现的样子:

unsigned long long int closestIntSameBitCount(unsigned long long int n)
{
  if (n & 1)
    return n ^= getSwapMask(n, 0xFFFFFFFF);
  return n ^= getSwapMask(n, 0x00000000);
}

// Helper function
unsigned long long int getSwapMask(unsigned long long int n, unsigned long long int mask)
{
  unsigned long long int swapBitMask = (n ^ mask) & ~((n ^ mask) - 1);
  return swapBitMask | (swapBitMask >> 1);
}

留意表达式(x ^ mask) & ~((x ^ mask) - 1) 现在,我将使用示例1011 1000遍历此代码:

// start of closestIntSameBitCount
if (0) // 1011 1000 & 1 -> 0000 0000
// start of getSwapMask
getSwapMask(1011 1000, 0x00000000)
swapBitMask = (x ^ mask) & ~1011 0111  // ((x ^ mask) - 1) = 1011 1000 ^ .... 0000 0000 -> 1011 1000 - 1 -> 1011 0111
swapBitMask = (x ^ mask) & 0100 1000 // ~1011 0111 -> 0100 1000
swapBitMask = 1011 1000 & 0100 1000 // (x ^ mask) = 1011 1000 ^ .... 0000 0000 -> 1011 1000
swapBitMask = 0000 1000 // 1011 1000 & 0100 1000 -> 0000 1000
return swapBitMask | 0000 0100 // (swapBitMask >> 1) = 0000 1000 >> 1 -> 0000 0100
return 0000 1100 // 0000 1000 | 0000 0100 -> 0000 11000
// end of getSwapMask
return 1011 0100 // 1011 1000 ^ 0000 11000 -> 1011 0100
// end of closestIntSameBitCount

如果您想自己编译并运行它,这是一个完整的示例:

#include <iostream>
#include <stdio.h>
#include <bitset>

unsigned long long int closestIntSameBitCount(unsigned long long int n);
unsigned long long int getSwapMask(unsigned long long int n, unsigned long long int mask);

int main()
{
  unsigned long long int number;
  printf("Pick a number: ");
  std::cin >> number;
  std::bitset<64> a(number);
  std::bitset<64> b(closestIntSameBitCount(number));
  std::cout << a
            << "\n"
            << b
            << std::endl;
}

unsigned long long int closestIntSameBitCount(unsigned long long int n)
{
  if (n & 1)
    return n ^= getSwapMask(n, 0xFFFFFFFF);
  return n ^= getSwapMask(n, 0x00000000);
}

// Helper function
unsigned long long int getSwapMask(unsigned long long int n, unsigned long long int mask)
{
  unsigned long long int swapBitMask = (n ^ mask) & ~((n ^ mask) - 1);
  return swapBitMask | (swapBitMask >> 1);
}

答案 8 :(得分:0)

这是我解决问题的方法。我猜@jigsawmnc很好地解释了为什么我们需要| k2 -k1 |最小。因此,为了找到权重相同的最接近的整数,我们希望找到连续位被翻转的位置,然后再次翻转它们以获得答案。为此,我们可以将数字移动1个单位。用相同的数字进行XOR。这将在发生翻转的所有位置设置位。找到异或的最低有效位。这将为您提供最小的翻转位置。为该位置和下一个位置创建一个掩码。进行异或运算,这应该是答案。如果数字全为0或全为1,则无法使用 这是它的代码。

    def variant_closest_int(x: int) -> int:
        if x == 0 or ~x == 0:
            raise ValueError('All bits are 0 or 1')
        x_ = x >> 1
        lsb = x ^ x_
        mask_ = lsb & ~(lsb - 1)
        mask = mask_ | (mask_ << 1)
        return x ^ mask

答案 9 :(得分:0)

我的解决方案利用整数的奇偶校验。我认为我可以简化LSB口罩的方式

def next_weighted_int(x):
    
    if x % 2 == 0:
        lsb_mask = ( ((x - 1) ^ x) >> 1 ) + 1 # Gets a mask for the first 1
        x ^= lsb_mask
        x |= (lsb_mask >> 1)
        
        return x
    
    lsb_mask = ((x ^ (x + 1)) >> 1 ) + 1 # Gets a mask for the first 0
    x |= lsb_mask
    x ^= (lsb_mask >> 1)

    return x

答案 10 :(得分:0)

只需共享我的python解决方案即可解决此问题:

def same closest_int_same_bit_count(a):
    x = a + (a & 1)  # change last bit to 0
    bit = (x & ~(x-1)) # get last set bit
    return a ^ (bit | bit >> 1) # swap set bit with unset bit