Advent of code

 21 décembre 2022 

  Monkey Math : Calculer une expression mathématique, puis en résoudre une autre
vbjj: fcml * mwwn
   
mbmw: 2
   
gstw: cldd / rvtn
   
brsc: 5
   
bpjn: 11
   
        
  1. moncode.py
  2. codeReductionEquation.py
  3. calcul.py
from functools import cache
f = open("input.txt", 'r')
lines = [line[:-1] for line in f.readlines()]

SILLY_MONKEY = 'root' 
MONKEY_ME    = 'humn'


monkeys = {}


for line in lines:
    tk = line.split()
    monkeyName = tk[0][:-1]

    # On stocke soit la valeur, soit les trois parties de l'opération. Pas terrible de mélanger les types, mais voilà:
    if len(tk) == 2:
        monkeys[monkeyName] = int(tk[1])
    else:
        monkeys[monkeyName] = tk[1:]




def getResult(monkeyName):
    global monkeys
    calcul = monkeys[monkeyName]
    if type(calcul) == int:
        return calcul
    else:
        mk1, operation, mk2 = calcul
        valMk1 = getResult(mk1)
        valMk2 = getResult(mk2)
        if operation == '+':
            return valMk1 + valMk2
        elif operation == '-':
            return valMk1 - valMk2
        elif operation == '/':
            return valMk1 // valMk2   # changé valMk1 / valMk2 après avoir vérifié à la main que les divisions étaient bien toutes entières
        elif operation == '*':
            return valMk1 * valMk2
        else:
            print("Argh, opération inconnue", calcul)
            exit(1)

print("Réponse partie 1:", getResult('root'))





### PARTIE 2  avec trois façons différentes ! ###

monkeys[SILLY_MONKEY][1] = '-'   # égalité si ça retourne 0 !


# Utilisée par les méthodes 1 et 3
@cache    # on gagne jusqu'à un facteur 10, mais rien de problématique sans
def isNombre(monkeyName):
    t = monkeys[monkeyName]
    if type(t) == list:
        if MONKEY_ME in t:  # utilise ma valeur inconnue
            return False
        return isNombre(t[0]) and isNombre(t[2])
    else:
        return True





### Partie 2, (1) méthode résolution maison; facile car l'inconnue n'apparaît qu'une fois  ###
print("\n====== Partie 2, méthode de résolution maison =======")

def mySolve(monkeyName, valeurVoulue):
    if monkeyName == MONKEY_ME:
        return valeurVoulue
    if isNombre(monkeyName):
        return getResult(monkeyName)

    mk1, operation, mk2 = monkeys[monkeyName]
    isMk1Nombre = isNombre(mk1)

    if operation == '+':
        if isMk1Nombre:
            return mySolve(mk2, valeurVoulue - getResult(mk1))
        else:
            return mySolve(mk1, valeurVoulue - getResult(mk2))
    elif operation == '-':
        if isMk1Nombre:
            return mySolve(mk2, getResult(mk1) - valeurVoulue)
        else:
            return mySolve(mk1, valeurVoulue + getResult(mk2))
    elif operation == '/':
        if isMk1Nombre:
            return mySolve(mk2, getResult(mk1) // valeurVoulue)
        else:
            return mySolve(mk1, valeurVoulue * getResult(mk2))
    elif operation == '*':
        if isMk1Nombre:
            return mySolve(mk2, valeurVoulue // getResult(mk1))
        else:
            return mySolve(mk1, valeurVoulue // getResult(mk2))

print("Réponse partie 2 (méthode de résolution maison):", mySolve(SILLY_MONKEY, 0))






### Partie 2, (2) méthode par dichotomie pour trouver le zéro de getResult('root'). ###
### fonctionne même si l'inconnue apparaît plus d'une fois et même si l'équation est difficile à résoudre. 
### En revanche, il faut être sûr qu'il n'y ait pas des zéros non entiers à rejeter, ni que le zéro soit un max ou un min
print("\n====== Partie 2, méthode de résolution par dichotomie =======")

def sign(x):
    return x > 0 and 1 or x<0 and -1 or 0

def signeDifferenceSelonValeur(maValeur):
    global monkeys
    monkeys[MONKEY_ME] = maValeur
    if 'cache' in str(getResult):   # on change les valeurs, donc il faut vider le cache s'il y en a un
        getResult.cache_clear()
    return sign(getResult(SILLY_MONKEY))

borneInferieure = -10**20
borneSuperieure = 10**20  # Ça devrait être plus grand que la valeur à trouver…

# ajuster les bornes si 10**20 ne suffit pas à obtenir un changement de signe
sgnBorneInf = signeDifferenceSelonValeur(borneInferieure)
sgnBorneSup = signeDifferenceSelonValeur(borneSuperieure)
while sgnBorneInf == sgnBorneSup:
    borneInferieure *= 10
    borneSuperieure *= 10
print("Méthode par dichotomie: trouvé des signes différents ! On peut commencer.")

# démarrer la dichotomie
while borneInferieure < borneSuperieure-1:
    milieuBornes = (borneInferieure+borneSuperieure)//2
    sgnMilieu = signeDifferenceSelonValeur(milieuBornes)
    if (sgnMilieu == sgnBorneInf):
        (borneInferieure,borneSuperieure) = (milieuBornes, borneSuperieure)
    else:
        (borneInferieure,borneSuperieure) = (borneInferieure, milieuBornes)

# afficher le résultat
if signeDifferenceSelonValeur(borneInferieure) == 0:
    print("Réponse partie 2 (méthode par dichotomie):", borneInferieure)
elif signeDifferenceSelonValeur(borneSuperieure) == 0:
    print("Réponse partie 2 (méthode par dichotomie):", borneSuperieure)
else:
    print("Réponse partie 2 (méthode par dichotomie): pas trouvé de valeur entière…")






### Partie 2, (3) méthode de résolution par sympy, ou par un site ou un logiciel  ###
import sympy
print("\n====== Partie 2, méthode de résolution par sympy, ou par un site ou un logiciel =======")

def toMathString(monkeyName):
    global monkeys
    if monkeyName == 'humn':
        return 'x'
    if isNombre(monkeyName):
        return(str(getResult(monkeyName)))
    mk1, operation, mk2 = monkeys[monkeyName]
    return '(' + toMathString(mk1) + operation + toMathString(mk2) + ')'

equation = toMathString(SILLY_MONKEY)
sols=sympy.solve(equation, 'x')
print("Réponse partie 2 (résolution équation par sympy):", ''.join(map(str, sols)))

print("\nSans sympy, copier l'équation dans une appli de maths:\n", equation+'=0')

# Merci quickmath, parce que wolframalpha trouve l'équation trop longue, à moins de passer par la page de résolution d'équation
import urllib.parse
print("ou ouvrir le lien suivant:\n https://quickmath.com/#" + urllib.parse.urlencode({'c':'mySolve','v1':equation+'=0','v3':'x'}))
print("ou ouvrir le lien suivant:\n https://www.wolframalpha.com/input?" + urllib.parse.urlencode({'i2d':'true','i':equation+'=0'}))