C'est du moins comme ça que j'ai l'intention de gagner une étoile d'or aujourd'hui :
La difficulté sera dans les détails.
Faisons ça !
Tout d'abord, je dois analyser chaque ligne en une liste de nombres :
let eqs = input.split('\n').map(line => { return [...line.matchAll(/\d+/g)].map(el => +el[0]) })
Le premier élément est le total souhaité.
Le reste sont les opérandes ordonnés de l'équation.
Je devrai en tenir compte dans ma fonction récursive.
Voici ma fonction récursive :
function eqChecker(operands, amount, test) { if (amount > test) { return false } else if (amount == test && operands.length == 0) { return true } else if (operands.length) { let copy = operands.slice() let first = copy.shift() return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test) } }
Et voici la réduction qui l'utilise :
let part1 = eqs.reduce((count, eq) => { if (eqChecker(eq.slice(2), eq[1], eq[0])) { count += eq[0] } return count }, 0)
Comme je l'espérais mais ne m'y attendais jamais, cela génère la bonne réponse pour l'exemple d'entrée !
Est-ce que le traitement de ma saisie de puzzle sera terminé ?
Et si oui, cela générera-t-il la bonne réponse ?
Honnêtement, je ne suis pas sûr...
C'EST FAIT !!!
Waouh !!!
Aussi excité que je sois, je crains que la prochaine partie ajoute plus d'opérateurs ou nécessite du CS avancé pour que la récursion ne soit plus une solution viable.
Comment vais-je faire ça ?
...
Quelques jours plus tard...
Un récapitulatif de mon processus de réflexion :
Pour cette équation :
292: 11 6 16 20
Ce sont toutes les équations possibles étant donné les trois opérateurs :
11 11+6 11+6+16 11+6+16+20 11+6+16*20 11+6+1620 11+6*16 11+6*16+20 11+6*16*20 11+6*1620 11+616 11*6 11*6+16 11*6+16+20 11*6+16*20 11*6+1620 11*6*16 11*616 116 116+16 116+16+20 116+16*20 116+1620 116*16 11616
Peut-être que je peux créer une chaîne de chaque équation et l'évaluer manuellement dans ma fonction récursive.
Par exemple :
Je commence par une chaîne vide dans l'appel de fonction le plus externe :
""
À partir de là, je crée trois variantes en utilisant le numéro suivant :
"" + "+N" "" + "*N" "" + "N"
Hmm, mais cela ne fonctionnera pas pour le premier numéro.
Je dois démarrer mon premier appel de fonction avec le premier numéro, pas une chaîne vide :
"N"
Même chose à partir de là :
"N" + "+N" "N" + "*N" "N" + "N"
Ouais, ça devrait marcher.
À la fin, j'aurai ces exemples de variations, tous évaluables :
let eqs = input.split('\n').map(line => { return [...line.matchAll(/\d+/g)].map(el => +el[0]) })
J'ai écrit du code qui génère avec succès toutes les variantes de l'équation.
function eqChecker(operands, amount, test) { if (amount > test) { return false } else if (amount == test && operands.length == 0) { return true } else if (operands.length) { let copy = operands.slice() let first = copy.shift() return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test) } }
La fonction obtient quatre valeurs :
J'appelle la fonction en utilisant presque la même signature que dans la partie 1 :
let part1 = eqs.reduce((count, eq) => { if (eqChecker(eq.slice(2), eq[1], eq[0])) { count += eq[0] } return count }, 0)
La différence réside dans ce que je passe comme arguments :
Excellente nouvelle :
Mauvaise nouvelle :
J'aurais dû savoir mieux... que l'évaluateur JavaScript intégré utiliserait par défaut le bon ordre des opérations, et non de gauche à droite.
Cela met vraiment une clé encore plus grande dans mon algorithme :
Uggghhh.
Heureusement, je pense que je sais exactement comment faire ça.
J'ai besoin d'obtenir du JavaScript pour évaluer une équation comme celle-ci :
292: 11 6 16 20
Dans cet ordre :
11 11+6 11+6+16 11+6+16+20 11+6+16*20 11+6+1620 11+6*16 11+6*16+20 11+6*16*20 11+6*1620 11+616 11*6 11*6+16 11*6+16+20 11*6+16*20 11*6+1620 11*6*16 11*616 116 116+16 116+16+20 116+16*20 116+1620 116*16 11616
J'aimerais diviser cette équation en plusieurs parties :
""
La seule façon pour moi de voir comment, c'est avec cette expression à triple chaîne :
"" + "+N" "" + "*N" "" + "N"
Je remplis chaque opérateur avec un espace blanc uniquement pour l'utiliser comme séparateur.
Un fait sur cette liste de parties d'équation :
Comment puis-je exploiter ce fait dans une boucle qui parcourt chaque paire opérande-opérateur-opérande ?
Voici mon idée :
J'espère que ça marchera !
Mon simulateur de mathématiques de travail en JavaScript :
"N"
Excellente nouvelle :
Mauvaise nouvelle :
La réponse que je continue de générer est environ 7k inférieure à la réponse attendue.
Cela me fait penser que mon algorithme n'identifie pas cette équation comme correcte :
let eqs = input.split('\n').map(line => { return [...line.matchAll(/\d+/g)].map(el => +el[0]) })
Dans l'explication de l'exemple de saisie, voici l'équation gagnante :
function eqChecker(operands, amount, test) { if (amount > test) { return false } else if (amount == test && operands.length == 0) { return true } else if (operands.length) { let copy = operands.slice() let first = copy.shift() return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test) } }
Mon algorithme évalue cette équation et génère ce résultat :
let part1 = eqs.reduce((count, eq) => { if (eqChecker(eq.slice(2), eq[1], eq[0])) { count += eq[0] } return count }, 0)
C'est parce que mon algorithme fonctionne comme ceci :
292: 11 6 16 20
Je ne vois pas comment cela pourrait être un autre numéro.
Alors... j'ai cherché sur Google.
Et j'ai trouvé ma réponse, qui se cachait en clair dans l'explication, comme toujours :
Tous les opérateurs sont toujours évalués de gauche à droite.
Je pré-concaténais les valeurs à chaque appel de fonction récursive.
Au lieu de cela, mon algorithme devrait faire ceci :
11 11+6 11+6+16 11+6+16+20 11+6+16*20 11+6+1620 11+6*16 11+6*16+20 11+6*16*20 11+6*1620 11+616 11*6 11*6+16 11*6+16+20 11*6+16*20 11*6+1620 11*6*16 11*616 116 116+16 116+16+20 116+16*20 116+1620 116*16 11616
Maintenant que je comprends ce qui est censé se produire, puis-je ajuster mon algorithme pour qu'il corresponde à ce comportement de traitement ?
Heureusement, ajuster mon algorithme a été relativement simple.
J'ai ajouté une clause replaceAll() pour tenir compte de ||.
La nouvelle boucle while dans laquelle je traite tous les trois éléments ressemble à ceci :
""
Et j'ai ajusté le || de ma déclaration de retour. clause pour inclure ces caractères, au lieu de concaténer instantanément les deux nombres.
J'ai exécuté l'algorithme sur l'exemple d'entrée.
Il enfin a généré la bonne réponse !!
Quel soulagement !!
Je me demande s'il va finir de fonctionner et générer la bonne réponse sur ma saisie de puzzle.
Appuyer sur courir...
...
...
J'ai une réponse !
C'est énorme, donc c'est probablement bon signe.
Est-ce la bonne réponse ?
...
Non. Trop haut.
Déception.
Ma condition pour une équation gagnante est simplement que le résultat mathématique traité soit égal au montant du test.
Mais que se passe-t-il si l'une des variantes d'équations permet à un sous-ensemble de nombres de générer une réponse correcte ?
Pour détecter et exclure ce scénario, j'ai mis à jour ma condition if pour inclure une clause supplémentaire :
"" + "+N" "" + "*N" "" + "N"
De cette façon, ce n'est que si tous les nombres sont traités et que le montant résultant est égal au numéro du test que l'équation sera comptée.
La grande question :
Appuyer à nouveau sur Exécuter...
...
Hmm, cela ressemble toujours à la même réponse.
Oh, attendez, il y a deux chiffres vers la fin qui sont différents !
Ma nouvelle réponse est exactement 80 de moins qu'avant.
Existe-t-il une équation avec 80 comme montant attendu ?
Oui !
"N"
Existe-t-il un moyen d'obtenir 80 sans utiliser tous les nombres ?
Oui !
"N" + "+N" "N" + "*N" "N" + "N"
Était-ce le seul cas limite que je devais exclure ?
Envoi de ma nouvelle réponse...
C'EST CORRECT !!!
Woohoo!!!
Je l'ai fait !!!
Ça. Était. Épuisant. Et exaltant. Et vraiment courir. Et un défi.
Et toutes les raisons pour lesquelles j'aime faire ces puzzles.
En avant le suivant !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!