[〜#〜] so [〜#〜] 投稿で重複を見つけるためにこのコードを見つけました。しかし、この行がint mid = (low + high) >>> 1;
の意味を理解していない
private static int findDuplicate(int[] array) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
System.out.println(mid);
int midVal = array[mid];
if (midVal == mid)
low = mid + 1;
else
high = mid - 1;
}
return high;
}
>>>
演算子は Javaの符号なし右ビットシフト演算子 です。これは、オペランドを2
で正しいオペランドのべき乗、またはここで2
で効果的に分割します。
>>
と>>>
の違いは、負の数をシフトするときにのみ現れます。 >>
演算子は、1
であった場合に1
ビットを最上位ビットにシフトし、>>>
は0
に関係なくシフトします。
UPDATE:
1
と2147483647
(Integer.MAX_VALUE
)を平均してみましょう。簡単に計算できます:
(1 + 2147483647) / 2 = 2147483648 / 2 = 1073741824
さて、コード(low + high) / 2
で、これらは関係するビットです:
1: 00000000 00000000 00000000 00000001
+2147483647: 01111111 11111111 11111111 11111111
================================================
-2147483648: 10000000 00000000 00000000 00000000 // Overflow
/2
================================================
-1073741824: 11000000 00000000 00000000 00000000 // Signed divide, same as >> 1.
>>>
に「シフト」してみましょう:
1: 00000000 00000000 00000000 00000001
+2147483647: 01111111 11111111 11111111 11111111
================================================
-2147483648: 10000000 00000000 00000000 00000000 // Overflow
>>> 1
================================================
+1073741824: 01000000 00000000 00000000 00000000 // Unsigned shift right.
の意義
int mid = (low + high) >>> 1;
は符号なしシフトを使用することにより、負の数になるオーバーフローを回避します。 Javaはunsigned int
値をサポートしていません。(BTW char
は符号なし)として必要です。
これを書く伝統的な方法は
int mid = (low + high) / 2; // don't do this
ただし、これはより大きな合計でオーバーフローする可能性があり、中程度の負の数を取得します。
例えば.
int high = 2100000000;
int low = 2000000000;
System.out.println("mid using >>> 1 = " + ((low + high) >>> 1));
System.out.println("mid using / 2 = " + ((low + high) / 2));
プリント
mid using >>> 1 = 2050000000
mid using / 2 = -97483648
明らかに、2番目の結果は正しくありません。
そのビット単位の演算子..ビット値に作用します。 Aが60を保持している場合、A >>> 2が15を与えると仮定します(ビット値0000 1111)
その実際の名前は「Shift Zero right Operator」です。ここでは、左オペランド値は右オペランドによって指定されたビット数(この場合は2)だけ右に移動され、シフトされた値はゼロ(0000)で埋められます。