Sto provando a modare un numero intero per ottenere una posizione dell’array in modo che giri in loop. Fare i % arrayLength
funziona bene per i numeri positivi ma per i numeri negativi tutto va storto.
4 % 3 == 1 3 % 3 == 0 2 % 3 == 2 1 % 3 == 1 0 % 3 == 0 -1 % 3 == -1 -2 % 3 == -2 -3 % 3 == 0 -4 % 3 == -1
quindi ho bisogno di una implementazione di
int GetArrayIndex(int i, int arrayLength)
così
GetArrayIndex( 4, 3) == 1 GetArrayIndex( 3, 3) == 0 GetArrayIndex( 2, 3) == 2 GetArrayIndex( 1, 3) == 1 GetArrayIndex( 0, 3) == 0 GetArrayIndex(-1, 3) == 2 GetArrayIndex(-2, 3) == 1 GetArrayIndex(-3, 3) == 0 GetArrayIndex(-4, 3) == 2
L’ho fatto prima, ma per qualche motivo mi sta sciogliendo il cervello oggi 🙁
Uso sempre la mia funzione mod
, definita come
int mod(int x, int m) { return (x%m + m)%m; }
Naturalmente, se ti preoccupi di avere due chiamate all’operazione modulo, puoi scriverlo come
int mod(int x, int m) { int r = x%m; return r<0 ? r+m : r; }
o sue varianti.
Il motivo per cui funziona è che "x% m" è sempre nell'intervallo [-m + 1, m-1]. Quindi se è negativo, aggiungendo m lo inserirà nell'intervallo positivo senza modificarne il valore modulo m.
Si noti che l’operatore% di C # e C ++ NON è in realtà un modulo, è resto. La formula per modulo che vuoi, nel tuo caso, è:
float nfmod(float a,float b) { return a - b * floor(a / b); }
Devi ricodificarlo in C # (o C ++) ma questo è il modo in cui ottieni modulo e non un resto.
Implementazione su una sola riga usando %
una sola volta:
int mod(int k, int n) { return ((k %= n) < 0) ? k+n : k; }
La risposta di ShreevatsaR non funzionerà per tutti i casi, anche se si aggiunge “if (m <0) m = -m;", se si rappresentano dividendi / divisori negativi.
Ad esempio, -12 mod -10 sarà 8 e dovrebbe essere -2.
La seguente implementazione funzionerà sia per dividendi / divisori positivi che negativi e sarà conforms ad altre implementazioni (vale a dire, Java, Python, Ruby, Scala, Scheme, Javascript e Calcolatrice di Google):
internal static class IntExtensions { internal static int Mod(this int a, int n) { if (n == 0) throw new ArgumentOutOfRangeException("n", "(a mod 0) is undefined."); //puts a in the [-n+1, n-1] range using the remainder operator int remainder = a%n; //if the remainder is less than zero, add n to put it in the [0, n-1] range if n is positive //if the remainder is greater than zero, add n to put it in the [n-1, 0] range if n is negative if ((n > 0 && remainder < 0) || (n < 0 && remainder > 0)) return remainder + n; return remainder; } }
Test suite usando xUnit:
[Theory] [PropertyData("GetTestData")] public void Mod_ReturnsCorrectModulo(int dividend, int divisor, int expectedMod) { Assert.Equal(expectedMod, dividend.Mod(divisor)); } [Fact] public void Mod_ThrowsException_IfDivisorIsZero() { Assert.Throws(() => 1.Mod(0)); } public static IEnumerable
Per gli sviluppatori più attenti alle prestazioni
uint wrap(int k, int n) ((uint)k)%n
Un piccolo confronto delle prestazioni
Modulo: 00:00:07.2661827 ((n%x)+x)%x) Cast: 00:00:03.2202334 ((uint)k)%n If: 00:00:13.5378989 ((k %= n) < 0) ? k+n : k
Per quanto riguarda il costo delle prestazioni del cast di uint, dai un'occhiata qui
Aggiungendo un po ‘di comprensione
Per definizione di Euclide il risultato mod deve essere sempre positivo.
Ex:
int n = 5; int x = -3; int mod(int n, int x) { return ((n%x)+x)%x; }
Produzione:
-1
Basta aggiungere il modulo (arrayLength) al risultato negativo di% e starai bene.
Mi piace il trucco presentato da Peter N Lewis su questo thread : “Se n ha un intervallo limitato, puoi ottenere il risultato desiderato semplicemente aggiungendo un multiplo costante noto di [il divisore] che è maggiore del valore assoluto del minimo.”
Quindi se ho un valore d che è in gradi e voglio prendere
d % 180f
e voglio evitare i problemi se d è negativo, allora invece faccio solo questo:
(d + 720f) % 180f
Ciò presuppone che sebbene d possa essere negativo, è noto che non sarà mai più negativo di -720.
Confrontando due risposte predominanti
(x%m + m)%m;
e
int r = x%m; return r<0 ? r+m : r;
Nessuno ha effettivamente menzionato il fatto che il primo potrebbe lanciare un OverflowException
mentre il secondo no. Ancora peggio, con il contesto deselezionato predefinito, la prima risposta può restituire la risposta sbagliata (vedi mod(int.MaxValue - 1, int.MaxValue)
per esempio). Quindi la seconda risposta non solo sembra più veloce, ma anche più corretta.