más eficiente la prueba del año bisiesto:
if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0))
{
/* leap year */
}
Este código es válido en C, C++, C#, Java y muchos otros lenguajes como C.El código utiliza una sola expresión VERDADERO/FALSO que consiste en tres pruebas separadas:
- cuarta prueba del año:
year & 3
- prueba de 100 años:
year % 25
- prueba el año 400o:
year & 15
A La discusión completa de cómo funciona este código aparece a continuación, pero primero se necesita una discusión sobre el algoritmo de Wikipedia:
El algoritmo de Wikipedia es INEFFICIENTE/NO RESPONSABLE
Wikipedia ha publicado un algoritmo de pseudocódigo (Ver: Wikipedia: Leap year - Algorithm) que ha sido sometido a constante edición, opinión y vandalismo.
NO IMPLEMENTE WIKIPEDIA ALGORITHM!
Uno de los más antiguos (e ineficientes) algoritmos Wikipedia apareció como sigue:
if year modulo 400 is 0 then
is_leap_year
else if year modulo 100 is 0 then
not_leap_year
else if year modulo 4 is 0 then
is_leap_year
else
not_leap_year
El algoritmo anterior es ineficiente porque siempre realiza las pruebas para el año 400o y 100o año, incluso durante años que fallaría rápidamente la "prueba del 4 ° año" (la prueba del módulo 4) — que es el 75% del tiempo. Al reordenar el algoritmo para realizar la prueba del 4 ° año primero, aceleramos significativamente las cosas.
"más eficiente" pseudo-código del algoritmo
me proporcionó el siguiente algoritmo para Wikipedia (más de una vez):
if year is not divisible by 4 then not leap year
else if year is not divisible by 100 then leap year
else if year is divisible by 400 then leap year
else not leap year
Este "más eficientes" pseudo-código simplemente cambia el orden de las pruebas para que la división por 4 se lleve a cabo primero, seguido de las pruebas que ocurren menos frecuentemente. Debido a que "año" no se divide en cuatro el 75 por ciento de las veces, el algoritmo finaliza después de una sola prueba en tres de cuatro casos.
NOTA: He luchado varios editores de Wikipedia para mejorar el algoritmo publicado allí, argumentando que muchos novatos — y profesionales — programadores llegan rápidamente a la página de Wikipedia (debido a los mejores anuncios de motor de búsqueda) y poner en práctica la Wikipedia pseudo-código sin más investigación. Los editores de Wikipedia repudiaron y eliminaron todos los intentos que hice para mejorar, anotar o incluso meramente a pie de página el algoritmo publicado. Aparentemente, sienten que encontrar eficiencias es el problema del programador. Eso puede ser cierto, ¡pero muchos programadores están demasiado apurados para realizar investigaciones sólidas!
discusión de "más eficiente" LEAP TEST AÑO
Bitwise-Y en lugar del módulo:
He substituido dos de las operaciones de módulo en el algoritmo de Wikipedia con bit a bit-AND operaciones. ¿Porque y como?
Realizar un cálculo de módulo requiere división.A menudo no se lo piensa dos veces cuando se programa una PC, pero cuando se programan microcontroladores de 8 bits incrustados en dispositivos pequeños, es posible que la CPU no pueda realizar una función de división de forma nativa. En tales CPU, la división es un proceso arduo que implica bucle repetitivo, cambio de bit y operaciones de suma/resta que es muy lento. Es muy deseable evitar.
Resulta que el módulo de potencias de dos puede conseguirse alternativamente mediante un bit a bit-operación AND (véase: Wikipedia: Modulo operation - Performance Issues):
x% 2^n == x & (2^n - 1)
Muchos compiladores de optimización convertirán tales operaciones de módulo a bitwise-Y para usted, pero los compiladores menos avanzados para CPU más pequeñas y menos populares pueden no hacerlo. Bitwise-AND es una instrucción única en cada CPU.
Mediante la sustitución de los modulo 4
y modulo 400
pruebas con & 3
y & 15
(ver más abajo: 'Factoring para reducir matemáticas') podemos asegurar que los resultados de código más rápido sin necesidad de utilizar una operación de división mucho más lento.
No existe una potencia de dos igual a 100. Por lo tanto, estamos obligados a seguir utilizando la operación de módulo para la prueba de 100 años, sin embargo, 100 se reemplaza por 25 (ver a continuación).
Factoring para simplificar los cálculos:
Además de utilizar bit a bit-Y para reemplazar operaciones de módulo, es posible tener en cuenta dos diferencias adicionales entre el algoritmo de Wikipedia y la expresión optimizada:
modulo 100
se sustituye por modulo 25
modulo 400
se sustituye por & 15
La prueba de 100 años utiliza modulo 25
en lugar de modulo 100
. Podemos hacer esto debido a 100 factores de hasta 2 x 2 x 5 x 5. Debido a que la prueba del 4to año ya verifica factores de 4, podemos eliminar ese factor de 100, dejando 25. Esta optimización es probablemente insignificante para casi todas las implementaciones de CPU (ya que tanto 100 como 25 caben en 8 bits).
La prueba del año 400 utiliza & 15
que es equivalente a modulo 16
. De nuevo, podemos hacer esto porque 400 factores salen a 2 x 2 x 2 x 2 x 5 x 5. Podemos eliminar el factor de 25 que se prueba con la prueba de 100 años, dejando 16. No podemos seguir reduciendo 16 porque 8 es un factor de 200, por lo que eliminar más factores produciría un positivo no deseado por 200 años.
La optimización de 400 años es muy importante para las CPU de 8 bits, primero, porque evita la división; pero, más importante, porque el valor 400 es un número de 9 bits que es mucho más difícil de tratar en una CPU de 8 bits.
de cortocircuito lógico y/o operadores:
El final, y más importante, la optimización utilizados son el cortocircuito operadores ('& & ') y OR (' ||') Y lógico (ver: Wikipedia: Short-circuit evaluation), que se implementan en la mayoría de los lenguajes tipo C. Los operadores de cortocircuito se llaman así porque no se molestan en evaluar la expresión del lado derecho si la expresión del lado izquierdo, por sí misma, dicta el resultado de la operación.
Por ejemplo: si el año es 2003, entonces year & 3 == 0
es falso. No hay forma de que las pruebas en el lado derecho de la Y lógica puedan hacer que el resultado sea verdadero, por lo que no se evalúa nada más.
Al realizar la prueba del cuarto año primero, solo la prueba del cuarto año (una simple Y a nivel de bits) se evalúa tres cuartas partes (75 por ciento) del tiempo. Esto acelera enormemente la ejecución del programa, especialmente porque evita la división necesaria para la prueba de 100 años (la operación del módulo 25).
NOTA DE COLOCACIÓN PARÉNTESIS
Un comentarista sintió paréntesis, estaban fuera de lugar en mi código y sugirió las sub-expresiones serán reagrupados alrededor del operador lógico AND (en lugar de alrededor de la lógica OR), de la siguiente manera:
if (((year & 3) == 0 && (year % 25) != 0) || (year & 15) == 0) { /* LY */ }
Lo anterior es incorrecto. El operador AND lógico tiene una precedencia mayor que el OR lógico y se evaluará primero con o sin los nuevos paréntesis. Los paréntesis alrededor de los argumentos lógicos Y no tienen ningún efecto. Esto podría llevar a eliminar los sub-grupos enteramente:
if ((year & 3) == 0 && (year % 25) != 0 || (year & 15) == 0) { /* LY */ }
Pero, en ambos casos anteriormente, se evalúa la parte derecha de la lógica OR (la prueba año 400a) casi cada vez (es decir, , años no divisibles por 4 y 100). Por lo tanto, una optimización útil ha sido erróneamente eliminada.
Los paréntesis en mi código original implementar la solución más optimizada:
if ((year & 3) == 0 && ((year % 25) != 0 || (year & 15) == 0)) { /* LY */ }
Aquí, el OR lógico sólo se evalúa por año divisible por 4 (debido a la corta-circuito AND). El lado derecho del OR lógico solo se evalúa por años divisibles por 4 y 100 (debido al cortocircuito O).
NOTA PARA C/C++ PROGRAMADORES
C/C++ programadores pueden considerar esta expresión está más optimizado:
if (!(year & 3) && ((year % 25) || !(year & 15))) { /* LY */ }
Esto no es más optimizado! Si bien las pruebas explícitas == 0
y != 0
se eliminan, se vuelven implícitas y aún se realizan. Peor aún, el código ya no es válido en lenguajes fuertemente tipados como C# donde year & 3
se evalúa como int
, pero los operadores lógicos AND (&&
), OR (||
) y NO (!
) requieren bool
argumentos.
¿Funcionó? Además, la legibilidad del código es importante, por lo que 'yearr' es un nombre pobre para una función para encontrar si un año es bisiesto. 'main' devuelve' int' en C, no 'void'. –
Al compilar el código revisado, GCC dice: 'En la función 'yearr': yearr.c: 12: advertencia: el control llega al final de la función no válida'. Si sangra su código correctamente, le resultará más fácil ver por qué es así, basta con decir que si el año es divisible entre 4 pero no divisible entre 100, no le dirá a la persona que llama si eso es o no Un año bisiesto. –
'if (! Yearr (año)) { printf (" Es un año bisiesto "); } else { printf ("No es un año bisiesto"); } ' En lugar de lo anterior uno ' si (yearr (año)) { printf ("No es un año bisiesto."); } else { printf ("Es un año bisiesto"); } ' ¿No crees que el siguiente es fácil de entender? –