web-dev-qa-db-ja.com

Javaのモジュラスを負の数値で動作させる最適な方法は?

In Javaするとき

a % b

Aが負の場合、bにラップするのではなく、負の結果を返します。これを修正する最良の方法は何ですか?私が考えることができる唯一の方法は

a < 0 ? b + a : a % b
93
fent

A%b = a-a/b * bのように動作します。つまり、残りの部分です。

あなたはできる(a%b + b)%b


この式は、bが正でも負でも、_(a % b)_がaよりも必ず低い結果として機能します。 _(a % b)_は_-b_と_0_の間の負の値であるため、bの追加はaの負の値を処理します。_(a % b + b)_は必然的にbより低く、正。 aが正の場合_(a % b + b)_はaより大きくなるため、bが最初から正であった場合に最後のモジュロがあります。したがって、_(a % b + b) % b_は再びbより小さくなります(負のa値には影響しません)。

126
Peter Lawrey

Java 8の時点で、 Math.floorMod(int x、int y) および Math.floorMod(long x、long y))を使用できます 。これらのメソッドは両方とも、ピーターの答えと同じ結果を返します。

Math.floorMod( 2,  3) =  2
Math.floorMod(-2,  3) =  1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
81
John Krueger

使用していない(または使用できない)Java 8、まだGuavaは IntMath.mod() で救助に来ました。Guava11.0以降で使用可能です。

IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1

1つの注意:Java 8のMath.floorMod()とは異なり、除数(2番目のパラメーター)は負にできません。

11
Ibrahim Arief

数論では、結果は常に正です。すべてのプログラマーが数学者であるとは限らないので、これはコンピューター言語では必ずしも当てはまらないと思います。私の2セントは、それを言語の設計上の欠陥と考えていますが、今では変更できません。

= MOD(-4,180)= 176 = MOD(176、180)= 176

180 *(-1)+ 176 = -4は180 * 0 + 176 = 176と同じであるため

ここで時計の例を使用すると、 http://mathworld.wolfram.com/Congruence.html duration_of_time mod cycle_lengthが-45分であるとは言えません。両方の答えが基本方程式。

7
Chris Golledge

Java 8には_Math.floorMod_がありますが、非常に低速です(その実装には複数の除算、乗算、および条件があります)。ただし、JVMに固有の最適化されたスタブがある可能性があります。これにより、JVMが大幅に高速化されます。

floorModなしでこれを行う最速の方法は、他のいくつかの回答と同様ですが、条件分岐がなく、遅い_%_ opのみです。

Nが正であり、xが任意であると仮定します。

_int remainder = (x % n); // may be negative if x is negative
//if remainder is negative, adds n, otherwise adds 0
return ((remainder >> 31) & n) + remainder;
_

_n = 3_の場合の結果:

_x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
_

正確なmod演算子ではなく、_0_と_n-1_の間の均一な分散のみが必要であり、xが_0_の近くにクラスター化しない場合、以下はさらに高速になります。 、より多くの命令レベルの並列性があり、遅い_%_計算が結果に依存しないため、他の部分と並行して発生するためです。

return ((x >> 31) & (n - 1)) + (x % n)

_n = 3_を使用した上記の結果:

_x | result
----------
-5| 0
-4| 1
-3| 2
-2| 0
-1| 1
 0| 0
 1| 1
 2| 2
 3| 0
 4| 1
 5| 2
_

入力がintの全範囲でランダムな場合、両方の2つのソリューションの分布は同じになります。入力がゼロに近い場合、後者のソリューションでは_n - 1_で結果が少なすぎます。

1
Scott Carey

以下が代替案です。

a < 0 ? b-1 - (-a-1) % b : a % b

これは、他の式[(a%b + b)%b]より速いかもしれないし、そうでないかもしれません。通常、最新のプロセッサでは問題のあるブランチが含まれていますが、モジュロ演算が1つ少なくなります。

実際、それは間違いなく遅いかもしれません。

(編集:数式を修正しました。)

0
Stefan Reich