Wordle est mort. Racheté par le capitalisme et blindé de tackers et de pubs.
Changeons de jeu : trouver 24.
Le but est de combiner les 4 nombres proposés en 3 opérations pour atteindre le nombre 24.
Un petit exemple : 8 5 7 1
sortent, on peut faire 8-5=3, 7+1=8, 3*8=24
.
Il est obligatoire d'utiliser tous les nombres. Par exemple : 1 2 3 8
sortent, on ne peut pas faire 8 * 3 = 24
, car 1
et 2
n'ont pas été utilisés.
J'ai joué un peu puis me suis dis "c'est un boulot pour une machine".
Le jeu resemble un peu au jeu le compte est bon, avec 4 nombres au lieu de 6 et trouver 24 au lieu d'un nombre entre 101 et 999 et le fait qu'il faut utiliser tous les nombres.
J'ai donc ressorti un code que j'ai écrit en 1999 (oui monsieur!), en C et qui résolvait "le compte est bon" en une fraction de seconde sur un processeur PPC G3 de 233MHz.
Le programme est brute force. Il tente toutes les combinaisons possibles (733'188 si mon dénombrement est le bon) avec un parcours récursif systématique. Il ne tente pas de réutiliser les résultat précédents. Par exemple quand on calcule les combinaisons pour (a1,a2,a3,a4,a6), une grosse partie - (a1,a2a3,a4) - a déjà été exploré quand on a fait (a1,a2,a3,a4,a5). Mais mettre en place de la mémorisation m'a paru bien plus compliqué que le brute force.
Le code est probablement très laid pour un pratiquant du C. Ce n'est pas mon cas. Et deux décennies après, j'ai pu m'y replonger et l'adapter sans problème. Je n'ai pas eu trop honte de mon ancien moi à la relecture :)
$ ./trouve_24 1 2 3 8
1 + 2 = 3 ; 3 * 8 = 24 ;
1 + 2 = 3 ; 8 * 3 = 24 ;
1 * 2 = 2 ; 3 * 8 = 24 ;
1 - 2 = 1 ; 3 * 8 = 24 ;
1 - 2 = 1 ; 8 * 1 = 8 ; 3 * 8 = 24 ;
1 + 3 = 4 ; 2 - 8 = 6 ; 4 * 6 = 24 ;
1 + 3 = 4 ; 8 + 4 = 12 ; 2 * 12 = 24 ;
1 * 3 = 3 ; 8 * 3 = 24 ;
1 + 8 = 9 ; 3 + 9 = 12 ; 2 * 12 = 24 ;
1 * 8 = 8 ; 3 * 8 = 24 ;
2 - 8 = 6 ; 1 + 3 = 4 ; 6 * 4 = 24 ;
3 + 8 = 11 ; 1 + 11 = 12 ; 2 * 12 = 24 ;
3 * 8 = 24 ;
420 numbers generated
13 solutions
À noter que le programme donne des solutions invalides qui n'utilisent pas tous les nombres, il faut filtrer. Je n'ai pas non plus implémenté la division qui me paraissait plus compliquée qu'utile à l'époque pour le compte est bon. Pour trouve 24, il peut être nécessaire de l'utiliser pour consommer des nombres. Dans ce cas, le programme ne donne pas de solution et il faut donc se dire qu'une division est nécessaire. Cela réduit alors le problème qui devient facilement résoluble de tête.
Est-ce que vous avez déjà tenté de résoudre ce type de puzzle ? Si oui quelle a été votre approche ?
Ah et si les chiffres c'est pas votre truc, mais que vous aimez la géographie, découvrez worldle, avec ou sans openstreetmap sous les yeux, c'est pas le même délire.
# Quelques pensées
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 5.
Si je ne m'abuse, les résultats intermédiaires se ramènent à une utilisation de parenthèses, et en somme, une solution possible a forcément la forme d'un arbre binaire, les opérations utilisées ayant à chaque fois deux termes. Même si plusieurs arbres peuvent être équivalent à cause de la commutativité de l'addition et de la multiplication.
Bref, des arbres binaires avec quatre feuilles, il n'y en a pas trente-six, en fait il n'y en a que deux si je ne m'abuse, les autres formes possibles étant équivalentes à celles-ci en échangeant des nombres :
Donc, deux formes d'arbres, 4! répartitions des quatre nombres, et, les arbres ayant 3 nœuds sur lesquels il faut choisir à chaque fois une opération parmi les quatre opérations de base, 4³ choix d'opérations. Soit un total de 3072 possibilités. Je me trompe ?
On pourrait envisager d'optimiser le calcul en réutilisant des résultats de sous-arbres. Cela implique d'utiliser une forme de cache, et je ne suis en fait pas certain que ce soit vraiment plus efficace que de calculer sans cache. Un avis ?
[^] # Re: Quelques pensées
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 4.
Quoi que, connaissant d'avance la forme des arbres, on peut procéder à l'inverse, du bas vers le haut, et calculer toutes les possibilités de couples, puis les assembler.
[^] # Re: Quelques pensées
Posté par steph1978 . Évalué à 2.
Quand on fait "échouer" le programme - par exemple
./trouve_24 1 1 1 1
ne parviendra pas à trouver de solution - le programme annonce avoir tester 543 combinaisons. Il a parcourus tout l'espace de recherche, pas de branches coupées (lesbreak
dans le code).Si je modifie pour avoir 4 opérations (probablement ton calcul), il compte 939 combinaisons.
J'avoue que je n'ai jamais vraiment trouvé le formule qui permet de calculer le nombre de combinaisons, encore une fois j'ai fait la brute en les comptant une par une :)
J'étais arrivé à ça mais je ne sais plus comment et je ne retombe pas sur les chiffres donnés par le programme.
Après, ça reste une jolie formule :D
On peut mesurer que le programme utilise vraiment peu de mémoire (on pourrait faire un calcul théorique).
Pour un espace de recherche aussi petit, je ne pense pas qu'une stratégie de cache soit efficace.
Cependant, j'y vois un intérêt théorique :) : J'aimerai voir un programme qui minimise le nombre de calcul en réutilisant les sous-résultats. J'avais essayé pour 'le compte est bon' car l'espace de recherche est plus grand (733'188 si mon dénombrement est le bon) et que mon CPU était beaucoup plus faible. Mais j'ai laissé tombé devant la complexité et le fait que la méthode brute force donnait un résultat très rapidement.
[^] # Re: Quelques pensées
Posté par steph1978 . Évalué à 2.
Alors, si mes calculs sont bon, j'ai une erreur dans ma formule et un gros bug dans le comptage dans mon programme.
La formule serait :
Remarquez la division par deux qui a disparue. Elle était la pour la commutativité. Mais en fait c'est déjà pris en compte par l'opération "combinaison" C(n-j,2) qui n'a pas de notion d'ordre - (a,b) est (b,a) sont comptés pour 1.
J'ai un alignement des planètes : pour "trouve 24" - 4 nombres et 4 opérateurs - on aurait 1464 combinaisons (en comptant aussi celle de 1 ou 2 opérations qui ne sont pas valides). Pour "le compte est bon" - 6 nombres et 3 opérateurs - on aurait 900'495 combinaisons.
Si qqun savait confirmer les chiffres, ça serait top.
# En Prolog
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 6.
En voyant le type de problème et vu ma tendance à aimer les vieux langages (est-il besoin de le préciser), j'ai tout de suite pensé à Prolog.
Forcément, il y a déjà quelqu'un qui a codé une solution(Tout en bas de cette page) :)
Bon, ce n'est pas forcément très simple à comprendre quand on ne connait pas le langage et même quand on connait, il faut se creuser pour tout assimiler mais c'est sympa pour s'ouvrir un peu les chakras.
[^] # Re: En Prolog
Posté par zurvan . Évalué à 3.
faut que je me remette à Prolog ! J'en ai fait à la fac (de lettres), c'était pour programmer des pluriels et autres déclinaisons dans différentes langues, j'avais trouvé ça passionnant…
ce programme est bien fichu, par contre il ne va pas forcément utiliser tous les nombres proposés, ce qui est demandé par le jeu 24. Je serai incapable de modifier le programme prolog pour prendre ça en compte, mais ça doit être trivial pour qui connait ce langage :)
« Le pouvoir des Tripodes dépendait de la résignation des hommes à l'esclavage. » -- John Christopher
[^] # Re: En Prolog
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 3.
Voilà un défi intéressant pour s'y remettre :)
Je vais essayer de plancher dessus, c'est inutile donc indispensable.
[^] # Re: En Prolog
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 5.
Bon, j'ai eu un peu de temps pour adapter le code trouvé sur l'autre jour et voici la solution en Prolog:
Le second paramètre de bonCompte étant une liste contenait en tête le résultat, il suffisait de vérifier que cette liste ne contenait que le résultat. C'est ce que dit la première règle.
J'ai autorisé le fait que le résultat de la soustraction puisse fournir la même chose que sa opérande (le cas 2-1=1) et autorisé qu'une division et une multiplication par 1 soit faisable.
Ca s'appelle simplement via bonCompte(24, [7,8,5,1]). par exemple
[^] # Re: En Prolog
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3. Dernière modification le 01 mars 2022 à 01:52.
Juste excellent ! Je n'ai pas eu le temps dimanche (je comptais jeter un œil pour dérouiller mon Prolog —découverte d'adolescence que j'avais beaucoup aimé)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: En Prolog
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 01 mars 2022 à 10:51.
Par contre, je viens de voir, d'après les commentaires plus bas, que cela ne gère pas les fractions.
Je pense qu'aux chiffres et aux lettres, on travaille toujours sur des entiers.
Le problème est que je n'ai pas le niveau en Prolog pour corriger cela rapidement.
Peut-être faut-il regarder du côté de rational/1
[^] # Re: En Prolog
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
En fait il faudra essayer sur l'exemple problématique :
bonCompte(24, [1,4,5,6])
La solution étant
6/((5/4)-1)
; il ne faudrait pas que s'arrêter à5/4
qui n'est pas entier, ni même à(5/4)-1 = 1/4
qui n'est pas un entier, donc probablement un truc dans le genreÇa, ça marcherait aussi pour des cas difficiles du compte est bon. Pour le cas de jeu du Hollandais Volant, d'après le source JS on est plutôt dans du number/1 mais faudrait savoir si on a la même précision de part et d'autre (sachant que les fraction vont couvrir plus de 99% des cas on peut ne pas aller jusqu'à ce niveau de détail.)
Il y a aussi des subtilités avec les soustractions : notre résultat sera forcément positif, mais pas forcément certaines opérations intermédiaires… Par exemple :
1-5=-4; 7-4=3; 3*8=24;
avecbonCompte(24, [1,5,3,8])
S'il le trouve pas, faudra probablement modifierDe même, par anticipation, je me demande si les fractions doivent être forcément positives ? (je pense qu'il faudrait utiliser
dif
au lieu de\=
ou\==
)Les tests sur le site web de trouve 24 amènent à changer un peu les bornes qu'on se fixait pour le compte est bon…
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: En Prolog
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 3.
Alors, il faut aussi modifier choisir2elements qui ordonne les deux opérandes sinon on ne passe pas par les négatifs mais dans ce cas, on se récupère tous les doublons liés à la commutativité des opérateurs +, - et *.
Et en plus, pour une raison que j'ignore pour l'instant, cela ne ressort pas ta solution…
On va finir par y arriver à avoir la fédération trouve24 à la TapTempo Federation :D
[^] # Re: En Prolog
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Les doublons : c'est le premier truc qu'on constate :D En général ça se comprend/pardonne sauf quand on a le même chiffre en plusieurs exemplaires en entrée :-/
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: En Prolog
Posté par Anthony Jaguenaud . Évalué à 3.
(6×4)÷(5-1) et il n’y a que des divisions entières.
[^] # Re: En Prolog
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
Il faut que t'ailles sur le site essayer le programme ;) Le code de la page utilise bien des décimaux (ou plus exactement des flottants JS)
C'est pour éviter les malentendus et voir large qu'on a fini par parler de fractions… ()
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
# Mauvais exemple
Posté par groumly . Évalué à 10.
heu, ben si: 8*3*(2-1) :)
Ok, je chipote.
Linuxfr, le portail francais du logiciel libre et du neo nazisme.
[^] # Re: Mauvais exemple
Posté par steph1978 . Évalué à 3.
Je n'ai pas dit qu'il n'était pas possible de les utiliser - d'ailleurs le site dit "100 % des parties ont une ou plusieurs solutions" - j'ai juste dit qu'il fallait tous les utiliser pour avoir une solution valide.
# un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 4. Dernière modification le 24 février 2022 à 03:44.
C'est ballot …parce que, justement dans un lien posté récemment, que l'absence de pisteurs est un des ingrédients du succès.
https://linuxfr.org/users/gilcot/liens/wordle-is-pretty-damn-smart-in-many-subtle-ways
Ou pas… Ce genre de jeux (le compte est bon) est une façon d'évaluer l'aptitude de calculs mentaux et l'intuition (plutôt la capacité d'estimation) face aux combinaisons de nombres. Évaluer une machine, ou tricher, a peu d'intérêt ici.
Ceci dit, l'informatique y est utile pour évaluer des conjectures mathématiques ou explorer certains aspects algorithmiquement (notamment tout ce qui touche aux arbres de décision, dont il n'est pas question ici puisqu'on emploie la force brute.) Il me semble d'ailleurs avoir vu passer deux articles sur le sujet, dont un dans Tangente si j'ai bonne mémoire.
Du coup, je vais relever le défi, histoire de me changer les idées après une journée harassante de travail. Et pour épicer un peu la chose, l'implémentation sera en shell POSIX…
Bien, je passe sur l'étape de la saisie des nombres et du contrôle des entrées (toujours.)
Je ne vais pas non plus stocker les solutions, mais plutôt les sortir au fil de l'eau.
Comme on n'a pas de structure de tableau (en KSh, BASh et probablement ZSh, oui) je vais utiliser une variable par nombre (mais il n'y en a heureusement que quatre) !
Mon idée est qu'on va faire une boucle sur la liste de nombres.
Dans un langage comme RPL, on va juste faire quatre tours en décalant…
Pour la plupart des autres langages, non Forth-like on va utilier un tableau et décaler le curseur dans la boucle (tout en prenant tous les éléments, c'est juste pour changer l'ordre à chaque itération.)
Bon, on ne gère pas de tableau et heureusement il n'y a que quatre nombres.
Ces quatre lignes sont trompeuses (ça m'a servi à valider le fonctionnement, mais) il y a en fait vingt-quatre permutations (prises en compte dans la version finale.)
Le processus va consister à appliquer les quatre opérateurs… de sorte à couvrir tous les cas…
On peut les énumérer, mais par peur d'oublier, je boucle pareillement pour les quatre opérations… (l'optimisation serait de déboucler …parce que sinon on aura des doublons du fait la commutativité, mais cet exercice se fera plus tard.)
Bon, c'est pas tout ça. Dans ce truc brutalement automatique, il ne faut pas oublier de gérer la division par zéro…
En plus du second processus, on va appeler un troisième dans la même boucle (c'est du « quick and dirty » et il doit être possible de fusionner les deux…)
Enfin, on pense à ajouter les compteurs.
Comme c'est bête/brute et méchant/truand, il m'annonce vingt réponses qui pourraient se ramener à 10 du fait des symétries et on a exploré deux mille huit cents combinaisons dont on aurait pu se passer du quart je pense.
J'ai exploré ici les formes (c'est
(8-5)*(7+1)
ou(1+3)*(2-8)
par exemple) et (c'est3*(2-1)*8
par exemple) ; mais pas et ! Il s'agit d'une part d'un jet rapide, et d'autre part ce ne serait pas pertinent en shell car je soupçonne qu'il y aura des doublons et/ou même des triplons. Cependant, avec ces ajouts, on devrait arriver aux trois mille septante douze…Petit bémol cependant : les calcul ici sont fait sur des entiers et c'est problématique pour les divisions car le hollandais volant l'entend flottant… (si j'en crois la fonction
randomizeNumbers()
qui pourtant fait unMath.floor()
, à moins que ce ne soit à cause duevalMath(cell1, operator, cell2)
dans la fonctionevalResultat()
?) Je pense pouvoir corriger cela facilement, ce qui réduira le nombre de solutions proposées.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par le_poney (Mastodon) . Évalué à 2.
Il ne manque pas également →
Par exemple avec 7 7 1 2 je m'attends au résultat ((7*7)-1)/2 alors qu'avec ce script je vais avoir 7*7=49; 1*2=2; 49/2=24 ce qui est un peu simplifié ?
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 24 février 2022 à 14:08.
Avec les commutativités (additions et multiplications) et les priorités (multiplications et divisions) on devrait retomber sur l'un des 4 cas précédemments énumérés…
Mais je dis ça au doigt mouillé ; faudrait que je prenne le temps faire la preuve mathématique.
Justement avec les priorités,
((7*7)-1)/2
est(7*7-1)/2
; ce qui me fait dire qu'il manque éventuellement les cas (solutions5) et (solutions6) ainsi que (solutions0) tout simplement ?On peut facilement les rajouter… Le plus dur étant en fait, de détecter pour chaque type de solutions les divisions par zéro…
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 25 février 2022 à 01:42.
Les quatre manquants sont rajoutés, ce qui porte le nombre de formes explorées à 6.
Je croise les doigts ; on ne devrait pas avoir d'erreur de division.
Consultation/Téléchargement → https://framagit.org/-/snippets/6519
Par contre, comme il n'y a pas de table de sauvegarde intermédiaire, je ne sais trop comment élimer les doublons pour l'instant. Faudra filtrer soi-même les résultats :
Je m'attaque à l'autre grand problème (ne retenir que les divisions entières) demain.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par steph1978 . Évalué à 2. Dernière modification le 24 février 2022 à 10:53.
cool une version bash
J'ai essayé de recoller tous les bouts. J'ai encore des /0 et
process2
n'est jamais utilisé ; et je ne comprends pas le "$7" danssolution2
.Mais le plus gênant est quand même l'arrondi des divisions, cela ne respecte pas les règles du jeux donc ce n'est pas très utilisable. C'est pour ces raisons que je ne l'ai pas implémenté, trop de cas tordus.
[^] # Re: un petit peu plus (de divisions)
Posté par steph1978 . Évalué à 2.
(ok, je viens de comprendre les $6, $7, my bad)
J'ai fait une version "épurée" car j'aime bien l'approche mix de parenthèses et permutations des nombres.
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Désolé, je n'ai pas encore eu le temps de mettre en ligne, j'ai priorisé le commentaire expliquant l'approche à mon code jetable (il faudrait nettoyer un peu plus pour que ce soit présentable… bon, j'ai rajouté des commentaires à l'instant …parce-que des fonctions dont on n'a pas la signature ça va être dur à maintenir)
C'est maintenant en ligne https://framagit.org/-/snippets/6519
Ouf, mes explications ici ont été assez claires pour permettre de reconstituer l'ensemble …et l'adapter/épurer (ne pas s'enquiquiner avec les divisions)
Les règles n'étaient pas très claires pour moi. :-S Pour une fois que je n'ai pas voulu chipoter (et ai supposé qu'on faisait tous les calculs dans …à tort) Il a fallu être devant le cas (en testant en ligne) pour se rendre compte que la page donnait des résultats en flottants. Du coup je suis allé jeter un œil au source de la page (heureusement tout est en JS et pas caché, mais mon ECMAScript est un peu rouillé et je n'ai pas su voir/dire si c'est intentionnel ou si c'est une erreur de codage : seul-e l'auteur-e peut le dire)
Je vais corriger cela dans une v2 dans l'une des manières suivantes (à voir ce qui est plus simple en shell)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
Bon, comme je disais plus tôt, j'ai rajouté les cas manquants en faisant attention aux divisions par zéro :-D Le nombre de résultats, par rapport à la vingtaine d'hier est multiplié par
3.8
;-P1 2 3 8
7 7 1 2
AxByCzD
Ax(ByC)zD
(AxB)y(CzD)
AxBy(CzD)
Ax(ByCzD)
(AxB)yCzD
(AxByC)zD
Ensuite, comme promis hier, j'ai viré les solutions qui ne sont pas valides suite au constat. J'ai opté pour le faire au niveau de l'affichage des résultats car ça passe par l'appel de deux processus externes (jusque là, mis à part
test
qui peut être interne ou pas, tout était fait directement par le shell) qui sontdc
(que je préfère à sa surcouchebc
) etgrep
. Comme on pouvait s'y attendre, il y a moins de monde à l'arrivée… :-D1 2 3 8
7 7 1 2
AxByCzD
Ax(ByC)zD
(AxB)y(CzD)
AxBy(CzD)
Ax(ByCzD)
(AxB)yCzD
(AxByC)zD
Comme tu verras plus loin, avec l'implémentation en Python, il y a le cas intéressant de
1 4 5 6
pour lequel le script shell ne trouve pas de solution non plus (en fait on fait maintenant l'impasse sur le genre de solution invalide qui était proposé avant —testé et ça le fait.) Du coup, le défi suivant est de lui faire trouver l'élégante solution qui montre qu'on ne peut pas faire l'impasse sur la division…)À suivre.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
On remet l'œuvre sur l'établi, et on fait la vérification en amont de la procédure d'affichage. Normalement le calcul doit se dérouler sans accroc parce que les cas tordus (pour l'instant les zéros et je n'en vois pas d'autres) ont été éliminés juste avant. On va faire appel à un programme externe pour quasiment tous les calculs, histoire qu'ils se fassent dans et non plus .
Comme je le craignais, lancer des processus a un certain coût qui est quand même important sur la machine où j'ai travaillé. Ainsi, par exemple pour
8 3 2 1
avec justel1
, je note quereal/user/sys
passe0m0.109s/0m0.073s/0m0.040s
(ancienne version)0m4.503s/0m2.242s/0m3.984s
(nouvelle version)41/30/99
fois plus de temps…Ceci dit, ça reste intéressant car il faut moins d'une minute pour lister toutes les solutions possibles sur les pistes explorées. Le fait de changer de domaine de calcul ramène plus de résultats… (cela me fait penser à la programmation linéaire, sauf que c'est dans l'autre sens ; c'est peut-être une forme d'optimisation de production qui sait ? Mais revenons à nos moutons…)
1 2 3 8
1 2 7 7
1 4 5 6
Ax(ByC)zD
(AxB)y(CzD)
AxByCzD
(AxB)yCzD
(AxByC)zD
AxBy(CzD)
Ax(ByCzD)
Plus de résultats mais pas mal de duplications… Arriver à les anticiper et donc ne pas calculer inutilement (ce qui aurait quand même été le cas si on stockait les résultats pour au final n'en afficher que les occurrences uniques…) C'est une piste d'amélioration qui pourrait impacter positivement le temps d'exécution.
Si on prend le cas soumis par le_poney, il n'y a qu'une seule solution qui est trouvée dans deux branches branches mais en deux ou quatre fois pour chacune…
La solution de le_poney est la seule possible… C'est une
r2
qui se présente comme suit chez moi :Mission accomplie ; la solution en shell semble l'emporter sur les autres ;-) Plus qu'à l'optimiser.
(p.s. Je mettrai à jour le snippet plus tard)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
[3615 mavie]
J'ai pu optimiser un peu en essayant de réduire les calculs qui étaient refaits plusieurs fois : maintenant on stocke ces valeurs (échange d'un peu de temps CPU par un chouia de RAM ahha) et ça porte son fruit.
1 2 3 8
old1 1 3 8
new1 2 7 7
old1 2 7 7
new1 5 7 8
old1 5 7 8
new1 4 5 6
old1 4 5 6
newÀ noter que je n'ai pas fait plusieurs exécutions et tiré les moyennes ; je ne suis pas dans un contexte de benchmark mais un contexte d'optimisation de code. Justement, une étape suivante serait de faire du profilage (un vaste débat pour du shell…)
Bien. On note un effondrement des performances… /o\ Pourtant, j'ai réduit les appels à
dc
:Ax(ByC)zD
(AxB)y(CzD)
AxByCzD
(AxB)yCzD
(AxByC)zD
AxBy(CzD)
Ax(ByCzD)
Certes pas de beaucoup… Surtout qu'en contrepartie j'ai augmenté les appels à
tr
(via une sous-évaluation du shell) :Ax(ByC)zD
(AxB)y(CzD)
AxByCzD
(AxB)yCzD
(AxByC)zD
AxBy(CzD)
Ax(ByCzD)
Le mieux est vraiment l'ennemi du bien… /o\ Et comme on dit, faut pas chercher à optimiser trop tôt, et le script évolue encore.
Par ailleurs, j'ai revu les tests de garde-fous et ai viré ce qui n'avait de sens que lorsqu'on travaillait en . Donc on calcule plus de cas qu'avant… (un élément en plus de l'augmentation de consommation de ressource, et je n'ai refait les mesures qu'après avoir fait toutes ces modifications.)
1 2 3 8
1 2 7 7
1 4 5 6
1 5 7 8
Ax(ByC)zD
(AxB)y(CzD)
AxByCzD
(AxB)yCzD
(AxByC)zD
AxBy(CzD)
Ax(ByCzD)
Et là on a une petite surprise…
Eh non, il y a une seconde solution…
console
$ trouve24.sh 7 7 1 2
2/7=.28; 7/.28=25.00; 25.00-1=24; B1
7*7=49; 49-1=48; 48/2=24; B3
7*7=49; 49-1=48; 48/2=24; L1
7*7=49; 49-1=48; 48/2=24; L2
2/7=.28; 7/.28=25.00; 25.00-1=24; B1
7*7=49; 49-1=48; 48/2=24; B3
7*7=49; 49-1=48; 48/2=24; L1
7*7=49; 49-1=48; 48/2=24; L2
Found 8 solutions for 10648 computations.
…qui, un peu similaire à un autre cas, se traduit par
7/(2/7)-1
(forme B1) ou… $$Bon, faudra peut-être augmenter la précision de calcul ?
En fait c'est
et on peut sentir que c'est faux même si ça tombe juste malgré les erreurs qui s'annulent (est-ce lié à l'usage de la virgule fixe dans
dc
?) En effet,On ne peut rien contre ce genre de truc à partir du moment où le site lui-même travaille en flottants et non en entiers. C'est marrant/intrigant…
Je l'ai faite hier pour la monture dont je parle présentement (y a eu depuis d'autres améliorations dont je parlerai une autre fois).
Je ferai un autre topo sur ce point plus tard.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
[…]
suite
Il faut en fait distinguer deux cas de figure…
Le premier aspect, c'est que pour une même branche/forme, on a par exemple
…qui s'explique par les permutations des nombres ! Dans l'exemple, on a puis :-D On le voit mieux sur cet autre exemple ( puis ) :
Comme on ne fait pas le stockage des résultats, il n'y a pas vraiment de correction possible …à moins de trouver une façon de s'assurer qu'on n'a pas de doublon dans les permutations. Sachant que les permutations sont maintenant générées et non plus manuellement listées, faut donc rajouter des tests bien sentis aux bons endroits.
Le second aspect est celui des diverses formes…
J'étais parti dans l'idée d'explorer les différents arbres et comme annoncé, « plusieurs arbres peuvent être équivalent à cause de la commutativité de l'addition et de la multiplication. » C'est le cas ici :
Avec la priorité de la multiplication et de la division sur l'addition et la soustraction, mettre la première partie entre parenthèses (L1) revient au même que de ne pas mettre de parenthèses (B3) et faire séquentiellement les autres opérations… Si on met la parenthèse autour des deux premières opérations (L2) on aboutit aussi au même résultat… Ouch !
On ne peut malheureusement pas anticiper les formes égales dans notre approche exploratrice (puisqu'on était parti sur du brute force je le rappelle). Il aurait fallu lister (et catégoriser) toutes les combinaisons possibles (approche dite par tables) : c'est plus fastidieux mais plus efficace et rapide à terme. Je me note d'implémenter cela quand j'aurai un peu de temps (et si le fun y est toujours.) En l'état (approche exploratrice) on n'y peut rien :(
Ayant dit cela, le parcours par formes n'est pas compatible avec l'approche brutale qui aurait juste parcouru les opérations sans se préoccuper des formes : c'est ce que font les autres propositions (en Python et en Prolog pour l'instant), et cela correspond en fait à la forme B3 ! C'est donc la seule que devrait implémenter la variante épurée (code ci-après) et c'est la seule explorée par défaut par la nouvelle monture du script (le reste est laissé, accessible par des options, pour me permettra de satisfaire ma curiosité mathématique …et pour vérifier les cas moins triviaux où on n'a pas de solution B3.)
Le script devenant important d'une part (même si à fonctionnement équivalent j'ai réduit le nombre de lignes) et complexe (avec pilotage maintenant par des options) d'autre part, je l'ai déplacé dans un dépôt dédié. https://framagit.org/gilcot/trouve24 Cela me permet également de garder trace des différents essais (et donc de savoir ce qui a déjà été tenté lors des prochaines améliorations.)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
POSIX shell, j'insiste :-D Ça devrait fonctionner aussi
dash
/ash
avec lequel je teste. C'est juste qu'on évite des trucs propres àbash
/ksh??
/pdksh
/zsh
/etc ; mais ce faisant on s'évite certaines facilités (comme les tableaux et certaines formes de tests)J'ai oublié de corriger mon post (rédigé certes offline pour avoir le temps de tester un peu et donc présenter un POC au lieu d'une théorie, mais j'ai du zapper dans les aller-retours rédaction-débogage)
Dans la boucle
for
du second type type, j'avais mis deux tests (juste avant le calcul) au lieu du seul mentionné :J'ai ensuite remplacé les deux tests par un seul qui fusionne les deux…
Le
$7
est$o3
; mais on peut réarranger l'ordre si ça simplifie la lecture et la compréhension.Pas possible ! Je les ai testé séparément sur ton exemple et ça m'a bien renvoyé les quatre résultats (pour 1344 calculs) suivants :
Oui et c'était le premier pan de mon défi :-D
Je viens d'en trouver un avec l'exemple de "le_poney" :-D (dans la sortie suivante, je n'ai activé que solutions2)
C'est le cas
1 / (7 - 7) + 2
qui devrait pouvoir se corriger sans trop de difficulté. Je fais ça tout à l'heure.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: un petit peu plus (de divisions)
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Corrigé …par l'ajout d'un test supplémentaire que j'ai probablement zappé à cause de la fatigue.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
# Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 4.
En Python 3 :
Le site du jeu m'a proposé les nombres suivants : 1 4 5 6, ce qui m'amène à douter un peu de la garantie que « 100 % des parties ont une ou plusieurs solutions ».
[^] # Re: Python 3
Posté par le_poney (Mastodon) . Évalué à 2.
Par rapport à la solution de 1 4 5 6 -> 6/((5/4)-1)
Donc j'imagine qu'il y a des modifications à faire pour ton résolveur ;)
[^] # Re: Python 3
Posté par Cangamra . Évalué à 0.
il y a aussi la solution :
(6x4)+(1/5)
car en division entière 1/5 ça fait zéro
[^] # Re: Python 3
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 3.
Attention, dans mon implémentation en shell, j'ai naïvement cru que la division entière le ferait. Mais comme je le mentionne dans un commentaire, le site ne l'entend pas de cette oreille.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Python 3
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Euh… je traduis par :
Ce qui donne bien :
(contrairement à mon intuition en commençant ce message)
C'est bluffant… mais logique :
(diviser par le quart revient à multiplier par quatre)
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Python 3
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 25 février 2022 à 19:59.
Je crois que je viens de comprendre…
Nous avons tous compris qu'on fait des divisions entières… Du coup, ici, il ne poursuit pas avec ! Et moi, dans l'implémentation Shell, j'ai fait directement les calculs entiers (i.e. ) et donc sauté cette combinaison
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 4.
Bien vu. Il me faut utiliser des rationnels. Je vais voir ce que je peux faire pour ça.
[^] # Re: Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 5.
Correction, en utilisant les rationnels du module standard
fractions
:[^] # Re: Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3.
Et pour les nombres cités :
[^] # Re: Python 3
Posté par steph1978 . Évalué à 2.
C'est pour moi la meilleure solution à date.
L'élégance du Python sûrement :)
Permet de résoudre trouve 24 et le compte est bon.
[^] # Re: Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 4.
Ça pourrait être sympa en (O)Caml, aussi.
[^] # Re: Python 3
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 01 mars 2022 à 01:50.
En Haskell et en Lisp aussi (: Mais j'ai d'abord pensé à OCaml hier en lisant ton code.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Python 3
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3.
C'est le besoin de type union qui m'a rappelé Caml, en rédigeant ça en Python, en fait.
[^] # Haskell
Posté par Anthony Jaguenaud . Évalué à 3. Dernière modification le 04 mars 2022 à 19:37.
J’y ai pensé aussi, je l’ai fait. Je ne suis pas un pro du haskell, soyez bienveillant avec vos remarques ;-)
Et les résultats :
Évidemment, on prend toute les permutations d’une même solution.
[^] # Re: Haskel
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
PS Un admin pour changer le titre de « Re: Python 3 » en « Haskel » ? (bien que ce soit toujours le même fil, c'est plus sympa pour voir les déviations et parenthèses)
Merci beaucoup pour la contribution à la Fédération Trouve Vingt-Quatre :D
Oui, les doublons du fait de toutes les permutations sont un problème connu et normal/prévisible…
Sinon, as-tu testé le fameux cas
get24 [ 1,4,5,6]
? ;-)“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Haskell
Posté par Benoît Sibaud (site web personnel) . Évalué à 4.
Fait.
[^] # Re: Haskel
Posté par Anthony Jaguenaud . Évalué à 2. Dernière modification le 04 mars 2022 à 20:27.
Non, je n'avait pas testé…
Il retourne une liste vide, donc sans solution du point de vue de mon programme… c'est le résultat attendu ?
[^] # Re: Haskel
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2. Dernière modification le 04 mars 2022 à 20:48.
Héhé c'est pour ça que c'est un fameux cas… La solution est
https://linuxfr.org/users/steph1978/journaux/resoudre-trouve-24#comment-1884537
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Haskel
Posté par Anthony Jaguenaud . Évalué à 2. Dernière modification le 04 mars 2022 à 23:38.
Oui, mais j'ai volontairement omis les nombres à virgules…
I’ll be back with Rational soon ;-)
[^] # Re: Haskel
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Oui, remplacer
div
par%
et adapter les tests dansdoOp Div (a:b:xs)
:-)Mais fais gaffe aussi à ne pas jeter trop vite les négatifs dans
doOp Min (a:b:xs)
…parce-que le produit ou la divisions de deux négatifs donnent un positif d'une part, et que l'ajout d'un négatif à un truc plus grand que 24 t'en rapproche. Je vois si je te retrouve un des cas sur lesquels j'étais tombé comme ça.“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Haskel
Posté par Anthony Jaguenaud . Évalué à 2.
Non, ça ne marchera pas.
La division entière à besoin d’un type
integral
ce que n'est pas Ratio. On ne peut pas mélanger les types dans les divisions.[^] # Re: Haskel
Posté par Anthony Jaguenaud . Évalué à 4.
Voilà la solution, j’ai changé les
Int
enRatio Int
, enlevé les vérifications de la division et de la soustraction (mais là, je n’en vois pas l’intérêt ;-) )À chaque modification, j’ai laissé l’ancienne ligne en commentaire pour voir les différences.
Et le résultat :
[^] # Re: Haskel
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Top ! C'est plus complet et le code a passé tous les tests :-D
Bien vu pour la seconde solution :
puis
C'est formellement correct mais ne serait pas passé sur le site, vu que le JS travaille en flottants (i.e. notation scientifique) et que → (ptdr)
Ceci ne remet aucunement en cause ton programme qui trouve justement des solutions exactes qui passent sous le radar ailleurs (je me demande si on doit ouvrir un issue au hollandais volant ;D j'ai le même souci avec mon implémentation shell, qui elle travaille en décimale fixe…)
Si, si. Il faut que tu testes avec et sans le check sur les soustraction pour les valeurs 7,6,5,1
Normalement, si j'ai bien compris, la contrainte imposée sur les soustractions rejetait qui pourtant permet d'arriver au résultat avec au final.
Autre exemple avec un effet similaire :
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
[^] # Re: Haskel
Posté par Anthony Jaguenaud . Évalué à 2.
En fait ta première ligne :((1-5) + 7 ) × 8 <=> ((7 - 5) + 1)×8. Solution que mon programme trouve. Il trouve également : (7-(5-1))×8.
[^] # Re: Haskel
Posté par Gil Cot ✔ (site web personnel, Mastodon) . Évalué à 2.
Oui, je n'en disconvient pas et c'était pour donner des exemples où on a une différence négative (i.e. soustraction qui donne un résultat non positif). Et bien sûr il y a des solutions qui sont équivalentes puisqu'on ne fait que permuter les termes et que pour certains opérateurs ça ne change pas le résultat. Dans mon cas, voici ce qui est calculé (forme dite L2) :
(1-5+7)×8
avec calcul de1-5
en premier(7-5+1)×8
avec calcul de7-5
en premierJe n'ai pas présenté tous les résultats possibles sinon on a aussi (seulement qui font intervenir des différences négatives) :
(1-5+7)×8
avec calcul de1-5
en premier8×(7+1-5)
avec calcul de7+1
en premier8×(1-5+7)
avec calcul de1-5
en premier, ce qui est exactement pareil que le précédent sauf que là pas de différence négative(1-(5-7))×8
(7+(1-5))×8
qui revient exactement au même que le premier (L1)(7+(1-5))×8
qui est la même que la précédente (B1, L1)L'autre résultat que tu mentionnes est donnée chez moi par le parcours B1 (c'est la quatrième ligne) et n'a pas de différence négative.
“It is seldom that liberty of any kind is lost all at once.” ― David Hume
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.