Nous sommes toujours sur l'ßle du désert. Grùce aux piÚces détachées reçues de l'ßle du métal, triées avec notre aide, les lutins ont pu réparer leurs machines et cherchent maintenant à les démarrer.
PremiĂšre partie
Les machines sont commandées par un systÚme de communication trÚs lutinesque, c'est à dire complexe à souhait : il est constitué de modules reliés les uns aux autres, et qui fonctionnent un peu comme des portes logiques électroniques qui s'envoient des signaux bas ou hauts.
On a une description du systĂšme de communication, par exemple :
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a
Ou encore :
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output
On appuie sur un bouton, qui envoie un signal bas au diffuseur (le broadcaster), qui le transmet à toutes ses sorties. Ensuite, les signaux progressent de module en module avec des rÚgles spécifiques pour chaque type de module (le symbole avant le nom de chaque module indique son type). Les rÚgles sont un peu longues à expliquer ici, cf. la page du problÚme.
On appuie 1000 fois sur le bouton, et on veut savoir combien de signaux bas et combien de signaux hauts ont été transmis. En fait on nous demande le produit de ces deux comptages.
DeuxiĂšme partie
AprÚs cet échauffement, il est temps d'allumer le convoyeur de sable principal. Pour ça, il faut lui transmettre un signal haut. Le problÚme, c'est que quand on appuie une seule fois sur le bouton, il reçoit un signal bas. Quand on appuie une seconde fois, il reçoit un signal bas. Et ainsi de suite, vous pouvez bien appuyer des milliards de fois, il recevra toujours un signal bas.
Il finira bien par recevoir un signal haut, mais ça risque de prendre longtemps. TrĂšs longtemps. Il faut appuyer quelque chose comme plusieurs centaines de milliers de milliards de fois sur ce satanĂ© bouton. Appuyer avec prĂ©caution, parce qu'aprĂšs un tel usage, il risquerait d'ĂȘtre dans un sale Ă©tat. Mais combien de fois exactement, d'ailleurs ? Bonne chance !
# Les données imposent la méthode
PostĂ© par Yth (Mastodon) . ĂvaluĂ© Ă 3.
Ici aussi, l'énoncé ne suffit pas à résoudre le problÚme, les données imposent la méthode.
Il faut constater qu'Ă l'instar de l'exemple 2 avec
output, lerxde sortie n'existe pas dans les données en entrée, et qu'il est uniquement relié à une boßte à conjonction, l'équivalent duinvdu second exemple.rxrecevra un low pulse ssiinvreçoit des high pulses de partout à la fois.Et là on lance des simulations (ou on analyse les données en entrée ?), pour voir quand notre
invreçoit des high pulses, on va constater en environ 13000 itérations qu'on a vu passer 3 high pulses de chacun des 4 sources, et que chacun de ces évÚnement cycle.Chez moi :
Et ça cycle, donc 1Úre entrée en 3797, 7594, 11391, 2Ú entrée en 4051, 8102, 12153, 3Ú entrée en 3847, 7694, 11541, 4Ú entrée en 3877, 7754, 11631.
4 cycles, et qui commencent tous Ă 0 en plus, heureux hasard, n'est-ce pas ?
En analysant les données on doit réaliser qu'on a 4 parcours indépendants, depuis chacune des 4 entrées de notre broadcaster, qui mÚnent chacun à une entrée de notre boßte
inv, donc les cycles sont bien indĂ©pendants.Par contre, deviner qu'ils commencent Ă zĂ©ro ne me semble pas si Ă©vident, mĂȘme si tout les FlipFlop commencent Ă off, et les Conjoncteurs Ă low partout, pourquoi une fois un low reçu Ă l'arrivĂ©e est-ce qu'on retomberait sur l'Ă©tat initial ?
Bon, bah le résultat c'est de multiplier les longueurs de cycles entre eux.
- Parce qu'on a des cycles indépendants.
- Et qu'ils commencent tous à zéro.
Le résultat est dans les 256 billions (proche de 40004 ), donc on lance pas la force brute, quelle que soit la qualité de notre modélisation.
CĂŽtĂ© code j'ai pas lourd Ă montrer, peut-ĂȘtre ma modĂ©lisation pour le 1, mignonne, que j'ai un peu pensĂ©e en amont pour pouvoir dĂ©tecter des cycles, donc avec des frozen dataclass. Ăa tourne pas trop mal avec 1 millions de cycles en 15 secondes et une RAM constante Ă 80Mo (c'est PyPy, ya un overhead de RAM de 70Mo, c'est comme ça).
Le problÚme réel (4051 cycles) prend moins d'une seconde en CPython, avec 10Mo de RAM.
L'état des Conjunctions est un binaire avec les bits à 0 ou 1 selon le dernier pulse reçu de chaque entrée, donc les entrées sont ordonnées. Et bien sûr, partout, low c'est 1 ou True, et high c'est 0 ou False, ça simplifie de le prendre comme ça !
L'état est dans
boxesqui ne contient que des frozen dataclasses, donc en pratique quand une boßte change d'état, elle retourne une nouvelle boßte dans le nouvel état, je ne modifie jamais une boßte, ce n'est pas possible, j'en crée des nouvelles selon la situation.Cette façon de faire permettrait de comparer deux états global du systÚme.
On n'a pas à faire ça, donc probablement qu'il vaudrait mieux coder des boßtes dynamiques à états.
de toute façon le code final est un mélange de trucs fixes et de trucs qui bougent magiquement avec des variables globales, on a vu plus propre (je pense aux statistiques pour le premier problÚme)...
Le coût fixe de PyPy le rend inutile sur le problÚme réel, je monte de 0,55s à 0,65s, par contre si je cycle 1 million de fois, ça passe de 2min10 à 15s.
Ăa doit ĂȘtre pour ça que je n'ai pas trouvĂ© PyPy si pertinent cette annĂ©e : l'an dernier je devais ĂȘtre un gros bourrin, cette annĂ©e je suis tout en finesse (......).
Bon, c'était mignon, mais toujours un poil agaçant quand l'énoncé ne suffit pas à avoir la résolution, et qu'il faut compter sur des particularités des données.
Mais bon, si on nous expliquait dÚs le début qu'il y avait 4 cycles et qu'il fallait les coordonner, l'exercice serait assez trivial...
[^] # Re: Les données imposent la méthode
PostĂ© par Yth (Mastodon) . ĂvaluĂ© Ă 2.
Ah oui, le
deque()permet de faire une FIFO de messages, on les empile Ă droite, on les dĂ©pile Ă gauche, on a fini quand c'est vide, d'oĂč un code d'itĂ©ration assez simple dans la fonctionpush().C'Ă©tait la minute structure de donnĂ©es pas encore vue cette annĂ©e. Le
deque()c'est unelist()optimisée pour faire des ajouts/suppressions aux bouts, donc idéal pour des FIFO, LIFO et trucs du genre.[^] # Re: Les données imposent la méthode
PostĂ© par Guillaume.B . ĂvaluĂ© Ă 1.
En Haskell, on la structure
Seq. C'est des structures immuables permettant d'ajouter ou d'enlever un élément en début ou fin en temps constant. Quand je dis enlever ou ajouter, je veux dire créer une nouvelle séquence en se basant sur l'existante mais sans la modifier.Sous le capot, c'est basé sur les finger trees.
https://en.wikipedia.org/wiki/Finger_tree
# Solution en Haskell
PostĂ© par Guillaume.B . ĂvaluĂ© Ă 2.
Je peux dire que j'ai galéré sur celui là . Tout ça parce que j'ai mal lu l'énoncé.
Je n'avais pas vu qu'il fallait traiter les gestions de pulsations sous forme de file.
Je les traitais sous forme de pile et bizarrement ça m'a quand mĂȘme donnĂ© la bonne rĂ©ponse pour la premiĂšre partie.
Pour la deuxiÚme partie, c'est comme pour le jour 8, c'est trÚs compliqué dans le cas général mais facile parce que l'instance a de bonnes propriétés (je n'aime pas trop ce genre de journée).
Tout d'abord, on remarque que rx a un seul prédécesseur qui est de type Conjonction et que celui a 4 prédécesseurs (que je vais appeler a, b, d) chacun de type Conjonction.
Ensuite, si on enlÚve broadcaster, rx et son prédécesseur, on se retrouve avec 4 composantes connexes et donc les pulsations de chacune vont vivre leur vie indépendamment des autres.
Si, on regarde quand a, b, c ou d envoie une pulsation forte, on remarque que ça forme un cycle sans prépériode et qu'une pulsation forte n'apparait qu'une seule fois durant un cycle.
Il suffit donc de repérer pour a, b, c et d la premiÚre fois qu'il y a une pulsation forte et faire le PPCM entre les différentes valeurs trouvées.
Pour le code en Haskell, j'utilise une monade State et des Lens, ce qui me permet de simplifier l'écriture.
[^] # Re: Solution en Haskell
PostĂ© par Yth (Mastodon) . ĂvaluĂ© Ă 2.
Ah, oui, c'est vrai ça, le rĂ©sultat c'est le PPCM des 4 cycles, sauf qu'en l'occurrence ils sont premiers entre eux (enfin, chez moi en tout cas...), encore une propriĂ©tĂ© cachĂ©e dans les donnĂ©es, d'oĂč la multiplication annoncĂ©e dans mon message !
J'ai testĂ© vite fait la multiplication sur le site avant de lancer le PPCM, et ça a fonctionnĂ©, donc j'ai complĂštement oubliĂ© qu'il fallait faire ça pour ĂȘtre corrects.
# Pas d'exemple
PostĂ© par đČ Tanguy Ortolo (site web personnel) . ĂvaluĂ© Ă 3.
à noter que les exemples fournis en premiÚre partie ne s'appliquaient pas à la deuxiÚme partie, et que cette derniÚre ne fournissait aucun exemple utilisable. J'ai trouvé ça vache.
[^] # Re: Pas d'exemple
PostĂ© par Ysabeau đ§¶ (site web personnel, Mastodon) . ĂvaluĂ© Ă 3.
Y'a moyen de voir ce que fait le code au fait ? Le code ça ne me dit rien.
Je nâai aucun avis sur systemd
[^] # Re: Pas d'exemple
PostĂ© par Yth (Mastodon) . ĂvaluĂ© Ă 3.
C'est pas hyper simple à représenter, mais je vais essayer.
On va dire qu'un « low pulse » est un 1, et un « high pulse » un 0.
C'est plus simple pour comprendre.
Le button envoie un 1 quand on appuie dessus.
Le broadcaster transmet ce qu'il a reçu à toutes ses destinations.
Les noms simples, en deux lettres, sans cadre, sont des alternateurs, s'ils reçoivent un 1, ils envoient une fois un 1, et une fois un 0. Mais s'ils reçoivent 0 ils bougent pas.
à l'état initial ils enverront un 0 au premier 1 reçu.
Donc notre broadcaster étant relié à des alternateurs, au repos il ne se passe rien, mais quand on appuie sur le gros bouton rouge, il active les quatre alternateurs devant lui.
Les noms dans les cadres sont des boßtes de remise à zéro, en gros.
Au début elles sont configurées sur 1 à toutes leurs entrées, mais c'est mis à jour dÚs qu'elles reçoivent un truc.
Si toutes leurs entrĂ©es sont Ă 0, alors elles envoient un 1 Ă toutes leurs sorties ! Sinon, c'est un 0, qui sera ignorĂ© par tous les alternateurs connectĂ©s. D'oĂč le nom de boĂźte de remise Ă zĂ©ro, ça ne va envoyer 1 aux alternateurs que quand ça reçoit du 0 de partout Ă la fois !
Quand ça arrive, les boßtes à droites reçoivent un 1 et donc envoient un 0, sinon elles reçoivent un 0 et envoie un 1.
Avec une seule entrée, ce sont des inverseurs.
Donc quand nos grosses boßtes s'activent et réussissent à envoyer un 1, le signal est inversé en 0 vers la boßte finale.
Si toutes les quatre grosses boĂźtes envoient un 1 en mĂȘme temps, alors la boĂźte finale reçoit quatre 0.
à ce moment là elle va envoyer un 1 vers rx, qui est notre module de sortie et qui n'attends que ça !
Franchement, une fois représenté comme ça, le problÚme il est hyper clair hein ?
Faut trouver le moment oĂč chacune des grosses boĂźte s'active.
Là , on a grosso-modo tous fait une simulation : on part de l'état initial, chaque alternateur va envoyer un 0, et les grosses boßtes sont sur 1 dans toutes leurs entrées, et on appuie sur le gros bouton rouge, jusqu'à ce que les boßtes s'activent.
Là on note quand c'est arrivé.
Typiquement on va continuer pour voir quand ça va se produire à nouveau, pour chaque boßte, et essayer de déduire un schéma.
Ici c'est facile, en fait, dĂšs la premiĂšre fois que ça s'active, on a fait un cycle et c'est retour Ă l'Ă©tat initial, en fait pas tout Ă fait, mais au moins ça boucle sur la mĂȘme durĂ©e.
On a donc 4 cycles, il ne reste plus qu'Ă les coordonner, savoir quand est-ce qu'ils vont s'activer tous ensemble.
Et le plus petit multiple commun aux quatre valeurs mesurées est notre réponse.
Maintenant en poussant l'analyse.
Notre sĂ©rie par exemple de la premiĂšre boĂźte, consiste en 12 alternateurs, tous Ă©teints donc prĂȘts Ă envoyer 0.
On va noter ça
000000000000.La boßte n'enverra absolument rien tant qu'on ne l'aura pas activée (enfin, elle envoie des 0 qui sont ignorés par les alternateurs).
Si on appuie, le premier va s'allumer, et envoyer 0, qui va ĂȘtre ignorĂ©, on est donc dans cette situation :
100000000000.Au second coup, notre premier alternateur va s'éteindre, repasser à 0, mais aura envoyé un 1 à droite, sur le second, qui va s'allumer et envoyer 0 qui sera ignoré.
01000000000Au 3Ăšme coup, on rallume le premier, rien d'autre ne bouge :
110000000000.4 :
00100000005 :
10100000006 :
01100000007 :
1110000000Ok peut continuer la simulation, mais il apparaßt comme évident qu'on est en train de compter. On a exactement la représentation binaire, mais de gauche à droite, des numéros des étapes.
LĂ on va regarder notre boĂźte nl, et constater qu'elle va s'activer dĂšs qu'elle reçoit 0 en mĂȘme temps de la part de hd, rh, tg, qz, rl, pf et pr, ce qui va arriver quand il vont tous s'ĂȘtre allumĂ©s (passer de 0 Ă 1) comme derniĂšre action.
Ăa arrive dĂšs qu'on a 1 sur chacun d'entre eux, et 0 sur les autres, c'est la premiĂšre fois oĂč ça va se produire et c'est cette configuration :
101001001111qu'on retourne en111100100101pour avoir le chiffre binaire 3877, qu'on retrouve bien dans mes résultats plus haut.Pour les autres, on va simplement lire
111000001111soit 3847,110010111111soit 4051, et enfin101010110111soit 3797.Diantre c'est juste.
LĂ nos nombres sont premiers entre eux, donc le PPCM vaut la multiplication des 4 soit... beaucoup.
Mais bref, voilĂ le problĂšme entiĂšrement dĂ©cortiquĂ©, et finalement une lecture directe des chiffre Ă trouver, sans aucun calcul, sans besoin d'ordinateur, sauf pour le PPCM (c'est un peu misĂ©rable Ă la main avec des chiffres aussi gros quand mĂȘme).
Merci de ta question Ysabeau, je n'aurais pas poussé autant sinon :)
Pour continuer un petit peu, sur notre exemple 1.
On active sur
101001001111mais à ce moment là notre boßte ne se contente pas d'envoyer un 1 vers la sortie, elle envoie aussi un 1 vers tous nos 0 !En effet, tous les alternateurs qui n'envoient pas vers la boßte, reçoivent de la boßte.
Ils sont éteints, ils vont donc s'allumer, envoyer des 0 qui seront ignorés et passer tous à 1 :
111111111111= 4095.Sauf que le tout premier reçoit lui aussi un 1, exactement comme si on avait appuyé sur le bouton une fois tout passé à 1 !
C'est
hd, et il est bien en dernier de la liste des alternateurs activĂ©s parnl. On lui envoie un 1 qui va faire s'Ă©teindre tous les alternateurs en chaĂźne, puisqu'ils envoient un 1 en s'Ă©teignant. Ils vont donc tous avoir envoyĂ© un 1 vers la boĂźte (ceux qui sont connectĂ©s) qui verra bien son Ă©tat retourner Ă celui d'origine.D'oĂč la boucle et le cycle qui recommence exactement Ă zĂ©ro, on est effectivement retournĂ© prĂ©cisĂ©ment Ă l'Ă©tat initial.
On a compté de 0 à 3877, et au moment d'arriver à 3877, on a une remise à 0 avec un signal envoyé vers la sortie.
[^] # Re: Pas d'exemple
PostĂ© par Ysabeau đ§¶ (site web personnel, Mastodon) . ĂvaluĂ© Ă 3.
Merci pour les explications (et je comprend qu'il n'y Ă rien Ă voir en fait).
Je nâai aucun avis sur systemd
[^] # Re: Pas d'exemple
PostĂ© par Yth (Mastodon) . ĂvaluĂ© Ă 2.
Ah non, on manipule des chiffres ou des lettres, réunis en ensembles, en listes, en dictionnaires.
Parfois on peut représenter ce qu'il se passe, parfois pas trop.
Et on doit faire gaffe à pas trop demander de calculs à la machine, sinon ça peut prendre des mois.
# Enfin...
PostĂ© par syj . ĂvaluĂ© Ă 1.
J'ai enfin ma solution pour le jour 20.
1) J'ai exploré plusieurs solutions comme laisser mon PC calculé à l'aveugle pendant 1j de travail. Je chauffe en partie à l'électrique. Donc, cela ne me coutait pas de laisser mon PC cramer des Watt :).
2) Jâai tentĂ© de simplifier les cycles des flip-flop sans grand succĂšs. Car je pensais que les conjuctions serait beaucoup plus complexe avec leurs multiples false, true quâelles crachent
3) Via rĂ©cursivitĂ© de trouver une mĂ©thode pour factoriser les circuits. Je nâai pas vu la solution suivante toute suite, car ma fonction recursive allĂ© jusquâau flip / flop. Mais au final,câĂ©tait trop complexe pour trouver une simplification Ă©vidente.
4) Finalement, jâai tentĂ© juste de regarder les cycles des trues sur les 4 conjonctions qui sont avant RX { "hz", "pv", "qh", "xm" }
Et lĂ , câĂ©tait Ă©vident. Jâavais des cycles rĂ©guliers pour ces 4 conjonctions. Il ne me reste plus quâĂ trouver quand elles sont Ă true toutes les 4 en mĂȘme temps. Ce qui revient Ă trouver le PPCM des 4 cycles.
Jâai demandĂ© Ă chatgpt de me fournir le calcul dâun PPCM (çà se reconnait au style).
En prenant, le problĂšme dans le bon sens, jâaurai pu le rĂ©soudre en 1h partie 1 et partie 2.... Jâai mis encore bien 5h cumulĂ©.
Suivre le flux des commentaires
Note : les commentaires appartiennent Ă celles et ceux qui les ont postĂ©s. Nous nâen sommes pas responsables.