Estoy tratando de modificar un número entero para obtener una posición de matriz para que se repita. Hacerlo i %
arrayLength
funciona bien para números positivos pero para números negativos todo sale mal.
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
así que necesito una implementación de
int GetArrayIndex(int i, int arrayLength)
tal que
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
He hecho esto antes, pero por alguna razón hoy me está derritiendo el cerebro :(
Respuestas:
Siempre uso mi propia
mod
función, definida comoPor supuesto, si le preocupa tener dos llamadas a la operación de módulo, puede escribirlo como
o variantes de los mismos.
La razón por la que funciona es que "x% m" siempre está en el rango [-m + 1, m-1]. Por lo tanto, si es negativo, agregar m lo colocará en el rango positivo sin cambiar su valor módulo m.
fuente
r = x%m
es-1
, después de lo cualr+m
es1
. El ciclo while no es necesario. El punto es que (como escribí en la respuesta),x%m
siempre es estrictamente mayor que-m
, por lo que debe agregarm
como máximo una vez para que sea positivo.r
paraa
módulob
, entonces es tal que 0 ≤ r <| b |.Tenga en cuenta que el operador% de C # y C ++ en realidad NO es un módulo, es el resto. La fórmula para el módulo que desea, en su caso, es:
Debe recodificar esto en C # (o C ++), pero esta es la forma en que obtiene el módulo y no un resto.
fuente
-21 mod 4 is 3 because -21 + 4 x 6 is 3.
Pero-21 divided by 4 gives -5
con aremainder of -1
. Para valores positivos, no hay diferencia. Entonces, infórmese sobre estas diferencias. Y no confíes en Wikipedia todo el tiempo :)%
resto?Implementación de
%
una sola línea usando solo una vez:fuente
mod(-10, 6)
a mano, sumas o restas 6 repetidamente hasta que la respuesta esté en el rango[0, 6)
. Esta notación significa "inclusivo a la izquierda y exclusivo a la derecha". En nuestro caso, agregamos 6 dos veces, dando 2. El código es bastante simple, y es fácil ver que es correcto: primero, hace el equivalente de sumar / restarn
como anteriormente, excepto que se detiene unon
corto, si se aproxima desde El lado negativo. En ese caso lo arreglamos. Allí: comentarios :)%
podría ser una buena idea. Consulte la tabla Cuánto cuestan las cosas en el código administrado en el artículo Escribir código administrado más rápido: Sepa lo que cuestan las cosas . El uso%
es similar al queint div
figura en la tabla: aproximadamente 36 veces más costoso que sumar o restar, y aproximadamente 13 veces más costoso que multiplicar. Por supuesto, no es gran cosa a menos que esto sea el núcleo de lo que está haciendo su código.%
más costoso que una prueba y un salto, especialmente si no se puede predecir fácilmente?La respuesta de ShreevatsaR no funcionará en todos los casos, incluso si agrega "if (m <0) m = -m;", si tiene en cuenta los dividendos / divisores negativos.
Por ejemplo, -12 mod -10 será 8, y debería ser -2.
La siguiente implementación funcionará para dividendos / divisores positivos y negativos y cumple con otras implementaciones (a saber, Java, Python, Ruby, Scala, Scheme, Javascript y la Calculadora de Google):
Conjunto de pruebas con xUnit:
fuente
mod
generalmente se llama a una función con módulo positivo (tengaarrayLength
en cuenta la variable en la pregunta original que se está respondiendo aquí, que presumiblemente nunca es negativa), por lo que no es necesario hacer que la función funcione para un módulo negativo. (Es por eso que menciono el tratamiento del módulo negativo en un comentario sobre mi respuesta, no en la respuesta en sí.) (r = a - b floor(a/b)
siempre es positivo). Incluso entre los sistemas informáticos, Pascal y Maple, por ejemplo, lo definen como siempre positivo.Agregando algo de comprensión.
Por definición euclidiana, el resultado del mod debe ser siempre positivo.
Ex:
Salida:
fuente
-1
?the positive remainder is always chosen
, pero los lenguajes de programación eligen dependiendo del lenguaje y los signos de a y / o n. [5] Pascal estándar y Algol68 dan un resto positivo (o 0) incluso para divisores negativos, y algunos lenguajes de programación, como C90, lo dejan a la implementación cuando n o a es negativo. 'Comparar dos respuestas predominantes
y
En realidad, nadie mencionó el hecho de que el primero puede tirar un
OverflowException
rato, mientras que el segundo no lo hará. Peor aún, con el contexto predeterminado sin marcar, la primera respuesta puede devolver la respuesta incorrecta (ver,mod(int.MaxValue - 1, int.MaxValue)
por ejemplo). Entonces, la segunda respuesta no solo parece ser más rápida, sino también más correcta.fuente
Simplemente agregue su módulo (arrayLength) al resultado negativo de% y estará bien.
fuente
Para los desarrolladores más conscientes del rendimiento
Una pequeña comparación de rendimiento
En cuanto al costo de rendimiento del elenco, mira aquí
fuente
-3 % 10
debería ser -3 o 7. Como se desea un resultado no negativo, 7 sería la respuesta. Su implementación devuelve 3. Debe cambiar ambos parámetrosuint
y eliminar el reparto.n
es una potencia de dos, en cuyo caso simplemente puede usar una lógica y ((uint)k & (n - 1)
) en su lugar, si el compilador ya no lo hace por usted (los compiladores suelen ser lo suficientemente inteligentes como para resolver esto).Me gusta el truco presentado por Peter N Lewis en este hilo : "Si n tiene un rango limitado, entonces puede obtener el resultado que desea simplemente agregando un múltiplo constante conocido de [el divisor] que es mayor que el valor absoluto de la mínimo."
Entonces, si tengo un valor d que está en grados y quiero tomar
y quiero evitar los problemas si d es negativo, entonces solo hago esto:
Esto supone que aunque d puede ser negativo, se sabe que nunca será más negativo que -720.
fuente
%
.Espera un comportamiento que es contrario al comportamiento documentado del operador% en c #, posiblemente porque espera que funcione de una manera que funcione en otro idioma al que está más acostumbrado. La documentación sobre los estados de c # (énfasis mío):
El valor que desea puede calcularse con un paso adicional:
fuente
Una implementación de una sola línea de la respuesta de dcastro (la más compatible con otros idiomas):
Si desea mantener el uso del
%
operador (no puede sobrecargar los operadores nativos en C #):Caso de uso, ambos trabajos:
fuente
Todas las respuestas aquí funcionan muy bien si su divisor es positivo, pero no está completo. Aquí está mi implementación que siempre regresa en un rango de
[0, b)
, de modo que el signo de la salida es el mismo que el del divisor, permitiendo divisores negativos como punto final para el rango de salida.PosMod(5, 3)
devoluciones2
PosMod(-5, 3)
devoluciones1
PosMod(5, -3)
devoluciones-1
PosMod(-5, -3)
devoluciones-2
(donde
real_t
puede ser cualquier tipo de número)fuente