我正在解决这个问题:
整数的二进制表示形式的计数称为该数字的权重。以下算法查找具有相同权重的最接近的整数。例如,对于123(0111 1011)2,最接近的整数是125(0111 1101)2。
O(n)的解决方案 其中n是输入数字的宽度,是通过交换不同的第一对连续位的位置。
有人可以在O(1)运行时和空间中给我一些解决方法吗?
由于
答案 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
&处交换位。 k2
,k2 > 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