""" Sección Declarativa Descripción: Calcula la diferencia exacta entre dos fechas restando directamente sus representaciones enteras. Se descompone la segunda fecha solo cuando la resta genera valores complementarios (base 10). Casos de prueba: - Caso 1 (Con complementos): 20240310 - 20230515 = 9795. Requiere calcular mes anterior y año bisiesto para corregir 95 días a 24 días. - Caso 2 (Eficiencia máxima): 20240820 - 20230515 = 10305. Días (5) <= 31. Meses (3) <= 12. Se obvia toda la lógica de calendario y bisiestos. Recursos: - fecha1, fecha2 (int): Fechas ingresadas en formato AAAAMMDD. - diferencia_bruta (int): Resultado de la resta directa. - dif_anios, dif_meses, dif_dias (int): Componentes extraídos. - anio2, mes2, mes_anterior, anio_evaluar, dias_mes_anterior (int): Variables de instanciación "perezosa", solo creadas si dif_dias > 31. - texto_diferencia (str): Cadena final de salida. """ # Sección Algorítmica # 1) Prólogo print("--- Calculadora de Fechas por Análisis de Complementos (Optimizada) ---") print("Ingrese las fechas como un número entero continuo de 8 dígitos (ej. 20250815).") print("La primera fecha debe ser menor a la segunda.\n") fecha1 = int(input("Ingrese la primera fecha (AAAAMMDD): ")) fecha2 = int(input("Ingrese la segunda fecha (AAAAMMDD): ")) # 2) Desarrollo # Resta matemática directa en base 10 diferencia_bruta = fecha2 - fecha1 # Extracción inicial de componentes de la diferencia dif_anios = diferencia_bruta // 10000 dif_meses = (diferencia_bruta // 100) % 100 dif_dias = diferencia_bruta % 100 # Corrección de días: SOLO calculamos el calendario si hubo un préstamo en base 10 if dif_dias > 31: # Extracción diferida: solo ahora necesitamos conocer los datos de la segunda fecha anio2 = fecha2 // 10000 mes2 = (fecha2 // 100) % 100 mes_anterior = mes2 - 1 anio_evaluar = anio2 if mes_anterior == 0: mes_anterior = 12 anio_evaluar = anio2 - 1 # Evaluación de los días del mes anterior if mes_anterior == 1 or mes_anterior == 3 or mes_anterior == 5 or mes_anterior == 7 or mes_anterior == 8 or mes_anterior == 10 or mes_anterior == 12: dias_mes_anterior = 31 elif mes_anterior == 4 or mes_anterior == 6 or mes_anterior == 9 or mes_anterior == 11: dias_mes_anterior = 30 else: # sólo queda como posibilidad, en orden descendente de probabilidad, de que mes_anterior sea 2 # Lógica condicional para año bisiesto if (anio_evaluar % 4 == 0 and anio_evaluar % 100 != 0) or (anio_evaluar % 400 == 0): dias_mes_anterior = 29 else: dias_mes_anterior = 28 # Ajuste del valor complementario dif_dias = dif_dias - 100 + dias_mes_anterior # Corrección de meses: independiente de los días, ajusta solo si superó los 12 meses if dif_meses > 12: dif_meses = dif_meses - 100 + 12 # Nota: la resta en base 10 ya redujo automáticamente un año al hacer este préstamo # Construcción de la cadena de salida omitiendo ceros texto_diferencia = "" if dif_anios > 0: texto_diferencia = texto_diferencia + str(dif_anios) + " años " if dif_meses > 0: texto_diferencia = texto_diferencia + str(dif_meses) + " meses " if dif_dias > 0: texto_diferencia = texto_diferencia + str(dif_dias) + " días" if texto_diferencia == "": texto_diferencia = "0 días" # 3) Epílogo print("\n--- Resultados ---") print(f"Diferencia bruta base 10 : {diferencia_bruta}") print(f"Diferencia real corregida: {texto_diferencia.strip()}") input("Pulse Enter para terminar el programa") ''' Desde un punto de vista estrictamente computacional, esta segunda versión (optimizada mediante restas de enteros y evaluación perezosa) es la mejor. Esta versión es superior a la primera por la reducción en la cantidad absoluta de instrucciones y ciclos de procesador. Motivos computacionales principales: 1. Minimización de operaciones de división (las más costosas) A nivel de hardware (en la Unidad Aritmético Lógica o ALU del procesador), las operaciones de división (//) y módulo (%) "cuestan" significativamente más ciclos de reloj que las sumas y restas. En la versión 1 el programa siempre está obligado a ejecutar 6 operaciones de división/módulo al inicio para descomponer la fecha1 y la fecha2 en sus partes, además de las 3 restas posteriores. En esta versión se realiza una sola resta directa (fecha2 - fecha1) y luego se ejecutan solo 3 operaciones de división/módulo sobre el resultado para extraer las diferencias. Las otras divisiones para obtener el año y mes de la segunda fecha solo ocurren si hay una anomalía (valores complementarios). En la mayoría de los casos, se ahorra casi la mitad del trabajo aritmético pesado. 2. Delegación del acarreo al hardware (ALU) En la versión inicial, si los días restaban a los meses, usábamos lógica condicional por software (if dif_dias < 0:) para pedir prestado un mes, y si los meses restaban a los años, hacíamos otro if en cascada. Al hacer fecha2 - fecha1 como enteros largos, le estamos dejando ese trabajo en cascada a los circuitos sumadores/restadores nativos del procesador, los cuales lo resuelven en una fracción de nanosegundo de manera simultánea. En conclusión, para un usuario normal ejecutando el script una vez, la diferencia de tiempo (microsegundos) es imperceptible en cualquiera de las versiones. Sin embargo, si este algoritmo formara parte del motor de una base de datos que necesita calcular la diferencia de edad en un padrón de 100 millones de personas, la última versión terminaría el trabajo significativamente más rápido y consumiendo menos energía y memoria caché del procesador. Es un excelente ejemplo de cómo el pensamiento matemático lateral (tratar la fecha como un bloque base 10) combinado con estructuras de control eficientes produce código más elegante y veloz.'''