Le problème d'aujourd'hui prend en entrée une grille composée de chiffres.
L'exemple donné est le suivant:
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533
Le but est d'acheminer de la lave qui démarre à la tuile en haut à gauche à une usine de pièces de machines dont la localisation est la tuile en bas à droite.
Il s'agit donc de trouver un chemin (un creuset) dans la grille. Seulement le chemin a les contraintes suivantes:
- chaque fois qu'il passe par une tuile (hormis la première), la lave perd le chiffre indiqué sur la tuile en chaleur;
- dans la partie 1, il n'est pas possible d'aller plus de 3 fois consécutivement dans la même direction et il n'est pas possible de revenir en arrière.
IL faut donc trouver un chemin qui minimise la perte de chaleur tout en satisfaisant les contraintes données.
Par exemple, pour l'exemple donnée: la solution est la suivante:
2>>34^>>>1323
32v>>>35v5623
32552456v>>54
3446585845v52
4546657867v>6
14385987984v4
44578769877v6
36378779796v>
465496798688v
456467998645v
12246868655<v
25465488877v5
43226746555v>
Dans la partie 2, la dernière contrainte est remplacée par la suivante:
lorsque l'on commence à aller dans une direction, il faut faire au moins 4 pas et au plus 10 pas dans cette direction. Comme dans la partie 1, on ne peut pas revenir en arrière.
# Solution en Haskell
Posté par Guillaume.B . Évalué à 2. Dernière modification le 17 décembre 2023 à 09:19.
Je trouve que ce problème ressemble à celui d'hier.
Il s'agit encore d'un parcours dans un graphe.
Ici, le graphe est pondéré. On va donc utiliser l'algorithme de Dijkstra au lieu d'un simple parcours en profondeur/largeur.
Comme hier, la direction est importante. Les sommets du graphe seront donc les paires (Position, Direction).
Les voisins d'un sommet ne seront pas les positions adjacentes dans la grille mais celle à distance entre 1 et 3 pour la partie 1 et entre 4 et 10 pour la partie 2.
La direction devra également changer à chaque fois.
Le poids des arêtes sera la somme des chiffres indiqués sur chacune des tuiles que l'on a parcouru durant ce déplacement.
Dans mon code, j'utilise la fonction
dijkstra'
que j'ai également écrite pour des problèmes des années précédentes.Voici sa signature
Le type v est celui des sommets et le type w est celui des poids des arêtes.
La fonction prend en entrée
- une fonction de voisinage qui à chaque sommet associe une liste des sommets voisins ainsi que le poids de l'arête entre les deux sommets,
- une fonction de prédicat pour le sommet de fin,
- un ensemble de sommets de départ.
et renvoit la distance entre un des sommets de départs et un des sommets de fin si un chemin existe.
Voici le code (sans les imports)
[^] # Re: Solution en Haskell
Posté par Guillaume.B . Évalué à 1.
J'ai oublié dire:
450ms pour la partie et 2s pour la partie 2.
Pas très satisfait mais je pense que c'est améliorable.
[^] # Re: Solution en Haskell
Posté par Guillaume.B . Évalué à 1.
Après quelques optimisations
200ms pour la partie 1 et 800ms pour la partie 2.
L'idée est qu'au lieu de dire qu'un noeud du graphe est un couple (position, direction) avec 4 directions possibles, je dis qu'un noeud est un couple (position, booléen)
ou le booléen m'indique si je me déplace horizontalement ou verticalement.
Ca fait 2 fois moins de noeuds dans le graphe. Du coup, logiquement, ça divise le temps d'exécution par deux (et même plus avec quelques autres optimisations).
Le code qui change:
[^] # Re: Solution en Haskell
Posté par Yth (Mastodon) . Évalué à 2. Dernière modification le 17 décembre 2023 à 16:31.
J'ai encore codé sur téléphone aujourd'hui, et ça m'a encore pas trop réussi. Le weekend est pas mon ami cette année, et je sais pas comment je vais faire les 23, 24 et 25, mais bon.
J'ai donc tout fait de tête, à inventer des algos avec mes gros doigts sur mon petit écran, sans rien réutiliser de pratique que du python chocolat, ou vanille peut-être…
Et ça a bien bien foiré.
Parce que le coup de la position et du booléen horizontal/vertical je l'ai vu direct, mais pour l'implémentation, j'ai tout raté, et laissé tomber.
J'arrivais pas à modéliser, trop long, trop chiant d'écrire sur le petit écran fêlé.
Et pourtant j'avais presque le résultat de la partie 1 très vite, mais j'avais 104, 107, 111, jamais 102, donc un bug fondamental dans l'algo quelque part.
Et au bout du compte, sur données réelles, j'ai 12s sur l'exo 1 et 5s sur l'exo 2, donc un algo absurde mais qui roule mieux sur le second exercice que sur le premier.
J'ai eu très très peu à adapter pour résoudre l'exo 2, et le rendre efficace pour les deux exercices, ce truc m'a pris 5 minutes et j'ai eu directement la bonne réponse.
Mais à l'évidence, il y a un truc dans mon approche que j'ai raté, parce que ça a beaucoup planté quand même, et je n'ai jamais réussi à raccrocher le fait qu'on s'en moque d'arriver par la droite ou la gauche puisqu'on va à la fois en haut et en bas après dans les deux cas.
Bref, code pourri, beaucoup de temps passé à me casser les orteils, et les dents, sur le périphérique mobile, et une conclusion : c'est vraiment pas un bon outil de programmation.
Bilan j'ai repris mon truc au propre, sur un ordinateur, et j'ai réussi ma modélisation avec uniquement horizontal ou vertical, en refaisant ça calmement, et avec un vrai clavier.
Bah ça fonctionne, et je tombe à 7s et 4s.
On notera que j'ai tellement mal codé mon bidule que PyPy est indispensable , en CPython on monte à 1 minute 42, c'est dire si c'est moche.
Et après, au lieu de stocker mes scores dans un dictionnaire d'états (un
dataclass(int x, int y, bool horizontal)
) j'ai repris un tableau en x*y*horizontal, et l'indexation étant nettement plus efficace comme ça on tombe à 6s au total.Ça reste pourri.
Avec un petit compteur pour comptabiliser les appels à mon itérateur d'Explorateur, j'en ai 1 100 226 sur la partie 1 et 247 885 sur la 2.
Allez, demain, je dois tout faire entre 9h30 et 10h30, sur un PC qui n'est pas à moi :)
[^] # Re: Solution en Haskell
Posté par Guillaume.B . Évalué à 1.
Moi, je suis arrivé à descendre à 300ms mais je pense pas faire beaucoup mieux.
J'ai tenté des heuristiques pour A* mais rien de concluant.
Enfin, j'en avais une bien mais elle prenait trop de temps à être calculer.
# J'étais fatigué, j'ai fait un algo moisie, j'ai du corrigé des bug moisie
Posté par syj . Évalué à 1.
Mauvais cocktail pour coder :)
- S'être couché à 3h
- Être fatigué, limite reste de gueule bois
- Regarder les conneries à la télé en même temps qu'on code.
Conséquences:
- çà donne un code très long à déboguer
- un problème pris de travers.
Mon code est tellement moche que je ne vais pas le partager :)
Honnêtement , je ne voyais pas comment appliquer un algo traditionnel de pathfinding avec les règles de déplacement.
Je suis donc partie sur une fonction récursive dans un premier temps. Mauvaise pioche entre les bogues, je suis arrivé après 3h à me dire que çà ne pouvait pas marcher.
Je change de stratégie. Je pars de l'arrivé et je remonte progressivement en mettant à jour les noeuds adjacents, j'y ai passé encore bien 3h au total sur la journée.
3min pour donner la réponse de la partie 2.
# Trois dimensions
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3. Dernière modification le 18 décembre 2023 à 20:15.
Pour ce problème, j'ai considéré qu'on n'était pas vraiment en deux dimensions, mais plutôt en trois. Parce que l'état d'un creuset, ce n'est pas seulement sa position sur le terrain, mais aussi le nombre de cases qu'il a parcouru dans une direction donnée… ce qui se représente également très bien par un nombre, que je considère comme une troisième coordonnée.
Ça permet de se ramener à un problème de parcours de proche en proche, avec une définition bien particulière des voisins d'un point.
Voici le code :
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.