java-按位运算符可以简单地翻转整数中的所有位?

我必须翻转整数的二进制表示形式中的所有位。 鉴于:

10101

输出应为

01010

与整数一起使用时,按位运算符可以完成此操作? 例如,如果我正在编写类似int flipBits(int n);的方法,那么体内会发生什么? 我只需要翻转数字中已经存在的内容,而不是翻转整数中的所有32位。


~一元运算符是按位取反。 如果您所需的位数少于int所能容纳的位数,那么您需要在事后用&对其进行屏蔽。


只需使用按位非运算符(1 << k) - 1。

int flipBits(int n) {

return ~n;

}

要使用k个最低有效位,请将其转换为正确的掩码。

(我假设您当然至少需要1位,这就是为什么mask从1开始的原因)

int flipBits(int n, int k) {
int mask = 1;
for (int i = 1; i < k; ++i)
mask |= mask << 1;
return ~n & mask;
}

如LưuVĩnhPhúc所建议,可以将掩码创建为(1 << k) - 1,而不是使用循环。

int flipBits2(int n, int k) {
int mask = (1 << k) - 1;
return ~n & mask;
}


有多种方法可以使用操作翻转所有位

x = ~x; // has been mentioned and the most obvious solution.

x = -x - 1; or x = -1 * (x + 1);

x ^= -1; or x = x ^ ~0;


好吧,因为到目前为止,只有一种解决方案可以给出“正确的”结果,但这真的不是一个很好的解决方案(使用字符串来计数前导零吗?这会让我梦dream以求的;))

因此,这里我们提供了一个不错的干净解决方案,该解决方案应该可以工作-尽管尚未对其进行全面的测试,但是您会发现要点。 确实,不具有无符号类型的Java对于此类问题非常烦人,但它仍然应该非常高效(而且,如果我这么说,比从数字中创建字符串要优雅得多)

private static int invert(int x) {
if (x == 0) return 0; // edge case; otherwise returns -1 here
int nlz = nlz(x);
return ~x & (0xFFFFFFFF >>> nlz);
}
private static int nlz(int x) {
// Replace with whatever number leading zero algorithm you want - I can think
// of a whole list and this one here isn't that great (large immediates)
if (x < 0) return 0;
if (x == 0) return 32;
int n = 0;
if ((x & 0xFFFF0000) == 0) {
n += 16;
x <<= 16;
}
if ((x & 0xFF000000) == 0) {
n += 8;
x <<= 8;
}
if ((x & 0xF0000000) == 0) {
n += 4;
x <<= 4;
}
if ((x & 0xC0000000) == 0) {
n += 2;
x <<= 2;
}
if ((x & 0x80000000) == 0) {
n++;
}
return n;
}


更快,更简单的解决方案:

/* inverts all bits of n, with a binary length of the return equal to the length of n
k is the number of bits in n, eg k=(int)Math.floor(Math.log(n)/Math.log(2))+1
if n is a BigInteger : k= n.bitLength();
*/
int flipBits2(int n, int k) {
int mask = (1 << k) - 1;
return n ^ mask;
}


我必须确保看到一些示例,但是由于二进制补码运算,您可能会得到意外的值。 如果该数字具有前导零(如26),则〜运算符会将其翻转为前导零-得出负数。

一种可能的解决方法是使用Integer类:

int flipBits(int n){
String bitString = Integer.toBinaryString(n);
int i = 0;
while (bitString.charAt(i) != '1'){
i++;
}
bitString = bitString.substring(i, bitString.length());
for(i = 0; i < bitString.length(); i++){
if (bitString.charAt(i) == '0')
bitString.charAt(i) = '1';
else
bitString.charAt(i) = '0';
}
int result = 0, factor = 1;
for (int j = bitString.length()-1; j > -1; j--){
result += factor * bitString.charAt(j);
factor *= 2;
}
return result;
}

我现在没有设置Java环境来对其进行测试,但这是一般的想法。 基本上只是将数字转换为字符串,切掉前导零,翻转位,然后将其转换回数字。 Integer类甚至可以通过某种方式将字符串解析为二进制数。 我不知道这是否是解决问题的方式,这可能不是解决问题的最有效方法,但它会产生正确的结果。

编辑:polygenlubricants对这个问题的答案可能也有帮助


我有另一种解决这种情况的方法,

public static int complementIt(int c){
return c ^ (int)(Math.pow(2, Math.ceil(Math.log(c)/Math.log(2))) -1);
}

它使用XOR来获取补码位,要对其进行补码,我们需要将数据与1进行XOR,例如:

101 XOR 111 = 010

(111是“关键字”,它是通过搜索数据的“ n”平方根生成的)

如果使用〜(补码),则结果将取决于其变量类型,如果使用int,则它将作为32位处理。


由于我们只需要翻转整数所需的最小位(例如50为110010,而取反后变为001101,即13),我们可以一次将单个位从LSB转换为MSB,并保持移位 右边的位,并相应施加2的幂。下面的代码完成所需的工作:

int invertBits (int n) {
int pow2=1, int bit=0;
int newnum=0;
while(n>0) {
bit = (n & 1);
if(bit==0)
newnum+= pow2;
n=n>>1;
pow2*=2;
}
return newnum;
}
import java.math.BigInteger;
import java.util.Scanner;
public class CodeRace1 {
public static void main(String[] s) {
long input;
BigInteger num,bits = new BigInteger("4294967295");
Scanner sc = new Scanner(System.in);
input = sc.nextInt();
sc.nextLine();
while (input-- > 0) {
num = new BigInteger(sc.nextLine().trim());
System.out.println(num.xor(bits));
}
}
}


来自openJDK的Integer.reverse()实现:

public static int More ...reverse(int i) {
i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
i = (i << 24) | ((i & 0xff00) << 8) |
((i >>> 8) & 0xff00) | (i >>> 24);
return i;
}

根据我在笔记本电脑上的实验,以下实现速度更快:

public static int reverse2(int i) {
i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
i = (i & 0x00ff00ff) << 8 | (i >>> 8) & 0x00ff00ff;
i = (i & 0x0000ffff) << 16 | (i >>> 16) & 0x0000ffff;
return i;
}

不知道其背后的原因是什么-因为它可能取决于将Java代码解释为机器代码的方式...


如果您只想翻转整数中“已使用”的位,请尝试以下操作:

public int flipBits(int n) {
int mask = (Integer.highestOneBit(n) << 1) - 1;
return n ^ mask;
}
xwb1989 answered 2020-07-21T22:44:22Z
0 votes
public static int findComplement(int num) {
return (~num & (Integer.highestOneBit(num) - 1));
}


您可以尝试以下方法:

/**
* Flipping bits of a decimal Integer.
*/
public class FlipBits {
public static final char ONE_CHAR = '1';
public static final char ZERO_CHAR = '0';
static int flipBits(int n) {
String nBinary = Integer.toBinaryString(n);
System.out.println("Original number is decimal " + n + ", and binary " + nBinary);
char[] result = new char[nBinary.length()];
char[] nBinaryChars = nBinary.toCharArray();
for (int i = 0; i < nBinaryChars.length; i++) {
result[i] = nBinaryChars[i] == ONE_CHAR ? ZERO_CHAR : ONE_CHAR;
}
int resultDecimal = Integer.parseInt(String.valueOf(result), 2);
System.out.println("Flipped number in decimal is " + resultDecimal
+ ", and in binary is " + String.valueOf(result));
return resultDecimal;
}
public static void main(String[] args) {
int input = 21;
int flippedInteger = flipBits(input);
System.out.println(input + " becomes " + flippedInteger + " after flipping the bits.");
}
}

样本输出:

原始数字为十进制21,二进制为10101

十进制的翻转数字是10,二进制的是01010

翻转位后21变为10。