In Java quando lo fai
a % b
Se a è negativo, restituirà un risultato negativo, invece di avvolgere in b come dovrebbe. Qual è il modo migliore per risolvere questo problema? L'unico modo in cui posso pensare è
a < 0 ? b + a : a % b
In Java quando lo fai
a % b
Se a è negativo, restituirà un risultato negativo, invece di avvolgere in b come dovrebbe. Qual è il modo migliore per risolvere questo problema? L'unico modo in cui posso pensare è
a < 0 ? b + a : a % b
Risposte:
Si comporta come dovrebbe a% b = a - a / b * b; cioè è il resto.
Puoi fare (a% b + b)% b
Questa espressione funziona in quanto il risultato di (a % b)
è necessariamente inferiore a b
, non importa se a
è positivo o negativo. L'aggiunta si b
prende cura dei valori negativi di a
, poiché (a % b)
è un valore negativo tra -b
e 0
, (a % b + b)
è necessariamente inferiore a b
e positivo. L'ultimo modulo è presente nel caso in cui all'inizio a
fosse positivo, poiché se a
fosse positivo (a % b + b)
diventerebbe maggiore di b
. Pertanto, lo (a % b + b) % b
trasforma in più piccolo di b
nuovo (e non influisce sui a
valori negativi ).
(a % b)
è necessariamente inferiore a b
(non importa se a
è positivo o negativo), l'aggiunta si b
prende cura dei valori negativi di a
, poiché (a % b)
è inferiore b
e inferiore a 0
, (a % b + b)
è necessariamente inferiore a b
e positivo. L'ultimo modulo è presente nel caso in cui all'inizio a
fosse positivo, poiché se a
fosse positivo (a % b + b)
diventerebbe maggiore di b
. Pertanto, lo (a % b + b) % b
trasforma in più piccolo di b
nuovo (e non influisce sui a
valori negativi ).
a < 0
, forse potresti dare un'occhiata)
(a % b + b) % b
interrompe per valori molto grandi di a
e b
. Ad esempio, l'utilizzo di a = Integer.MAX_VALUE - 1
e b = Integer.MAX_VALUE
darà -3
come risultato, che è un numero negativo, che è ciò che si voleva evitare.
while
sarebbe più lento se ne hai davvero bisogno tranne che hai solo bisogno di un, if
nel qual caso è effettivamente più veloce.
A partire da Java 8, puoi usare Math.floorMod (int x, int y) e Math.floorMod (long x, long y) . Entrambi questi metodi restituiscono gli stessi risultati della risposta di Peter.
Math.floorMod( 2, 3) = 2
Math.floorMod(-2, 3) = 1
Math.floorMod( 2, -3) = -1
Math.floorMod(-2, -3) = -2
float
o double
argomenti. L'operatore binario Mod ( %
) funziona anche con gli operandi float
e double
.
Per coloro che non usano ancora (o non sono in grado di usare) Java 8, Guava è venuto in soccorso con IntMath.mod () , disponibile da Guava 11.0.
IntMath.mod( 2, 3) = 2
IntMath.mod(-2, 3) = 1
Un avvertimento: a differenza di Math.floorMod () di Java 8, il divisore (il secondo parametro) non può essere negativo.
Nella teoria dei numeri, il risultato è sempre positivo. Immagino che non sia sempre così nei linguaggi dei computer perché non tutti i programmatori sono matematici. I miei due centesimi, lo considererei un difetto di progettazione del linguaggio, ma non puoi cambiarlo ora.
= MOD (-4,180) = 176 = MOD (176, 180) = 176
perché 180 * (-1) + 176 = -4 equivale a 180 * 0 + 176 = 176
Usando l'esempio dell'orologio qui, http://mathworld.wolfram.com/Congruence.html non diresti duration_of_time mod cycle_length è -45 minuti, diresti 15 minuti, anche se entrambe le risposte soddisfano l'equazione di base.
-1
invece di n-1
per esempio) allora fallo.
Java 8 lo ha Math.floorMod
, ma è molto lento (la sua implementazione ha più divisioni, moltiplicazioni e un condizionale). È possibile che la JVM abbia uno stub ottimizzato intrinseco, tuttavia, che lo velocizzerebbe in modo significativo.
Il modo più veloce per farlo senza floorMod
è come alcune altre risposte qui, ma senza rami condizionali e solo una operazione lenta %
.
Supponendo che n sia positivo e x possa essere qualsiasi cosa:
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;
I risultati quando n = 3
:
x | result
----------
-4| 2
-3| 0
-2| 1
-1| 2
0| 0
1| 1
2| 2
3| 0
4| 1
Se hai solo bisogno di una distribuzione uniforme tra 0
e n-1
e non l'esatto operatore mod, e il tuo x
non si raggruppa vicino 0
, il seguente sarà ancora più veloce, poiché c'è più parallelismo a livello di istruzione e il %
calcolo lento avverrà in parallelo con l'altro parti in quanto non dipendono dal suo risultato.
return ((x >> 31) & (n - 1)) + (x % n)
I risultati per quanto sopra con 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
Se l'input è casuale nell'intero intervallo di un int, la distribuzione di entrambe le due soluzioni sarà la stessa. Se i cluster di input sono vicini allo zero, ci saranno troppi pochi risultati n - 1
in quest'ultima soluzione.
Ecco un'alternativa:
a < 0 ? b-1 - (-a-1) % b : a % b
Questo potrebbe o non potrebbe essere più veloce dell'altra formula [(a% b + b)% b]. A differenza dell'altra formula, contiene un ramo, ma utilizza un'operazione modulo in meno. Probabilmente una vittoria se il computer può prevedere correttamente uno <0.
(Modifica: corretta la formula.)