整数のバイナリ表現のすべてのビットを反転する必要があります。与えられた:
_10101
_
出力は
_01010
_
整数で使用するときにこれを達成するためのビット演算子は何ですか?たとえば、int flipBits(int n);
のようなメソッドを記述している場合、本文には何が入りますか?整数の32ビットすべてではなく、数値に既に存在するものだけを反転する必要があります。
~
単項演算子はビットごとの否定です。必要なビットがint
に収まるビットより少ない場合は、事後、&
でマスクする必要があります。
ビット単位のNOT演算子~
を使用するだけです。
int flipBits(int n) {
return ~n;
}
K個の最下位ビットを使用するには、適切なマスクに変換します。
(もちろん、少なくとも1ビットが必要だと思います。そのため、マスクは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;
これまでのところ、「正しい」結果を与えるソリューションは1つしかありません。それは本当に素晴らしいソリューションではありません(先頭のゼロをカウントするために文字列を使用しますか?それは夢の中で私を悩ませます;))
したがって、ここでは、うまく機能するニースのクリーンなソリューションを使用します。ただし、完全にテストしたわけではありませんが、要点はわかります。本当に、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;
}
いくつかの例を確認する必要がありますが、2の補数演算のために予期しない値を取得する可能性があります。番号に先行ゼロがある場合(26の場合のように)、〜演算子はこれらを反転して先行ゼロにし、負の数になります。
可能な回避策の1つは、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環境を今すぐテストするためにセットアップしていませんが、それは一般的な考え方です。整数クラスには、文字列を2進数に解析する方法さえあるかもしれません。それが問題の解決方法であるかどうかはわかりませんが、おそらく最も効率的ではないでしょう。それを行う方法が、それは正しい結果を生成します。
編集: この質問 に対するpolygenlubricantsの回答も役立つかもしれません
このケースを解決する別の方法があります、
public static int complementIt(int c){
return c ^ (int)(Math.pow(2, Math.ceil(Math.log(c)/Math.log(2))) -1);
}
XOR=を使用して補数ビットを取得します。補数するには、XOR 1のデータを使用する必要があります。
101 XOR 111 = 010
(111は「キー」であり、データの「n」平方根を検索して生成されます)
〜(補数)を使用している場合、結果はその変数の型に依存します。intを使用している場合、結果は32ビットとして処理されます。
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));
}
}
}
整数に必要な最小ビットのみを反転する必要があるため(たとえば、50は110010で、反転すると001101になり、13)、LSBからMSBに個々のビットを1つずつ反転し、シフトを続けることができますビットを右に移動し、それに応じて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;
}
整数で「使用されている」ビットを単に反転させたい場合は、これを試してください:
public int flipBits(int n) {
int mask = (Integer.highestOneBit(n) << 1) - 1;
return n ^ mask;
}
これを試すことができます:
/**
* 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.");
}
}
サンプル出力:
元の数は10進数21、2進数10101です。
10進数の反転数は10、2進数の場合は01010
21は、ビットを反転した後に10になります。
public static int findComplement(int num) {
return (~num & (Integer.highestOneBit(num) - 1));
}
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コードがマシンコードに解釈される方法に依存する可能性があるため...