introduction
Vous êtes un biologiste qui étudie les schémas de mouvement des bactéries. Votre équipe de recherche en a un tas dans une boîte de Pétri, et vous enregistrez leur activité. Malheureusement, vous êtes sérieusement sous-financé et vous ne pouvez pas vous permettre une caméra vidéo, vous prenez donc simplement une photo du plat à intervalles réguliers. Votre tâche consiste à créer un programme qui retrace les mouvements des germes à partir de ces images.
Contribution
Vos entrées sont deux tableaux 2D de caractères dans n'importe quel format raisonnable, représentant des images consécutives de la boîte de Pétri. Dans les deux tableaux, le personnage .
représente un espace vide et O
représente un germe (vous pouvez choisir deux caractères distincts si vous le souhaitez). En outre, le tableau "après" est obtenu à partir du tableau "avant" en déplaçant certains germes d'un pas dans l'une des quatre directions cardinales; en particulier, les tableaux ont la même forme. Les germes se déplacent simultanément, de sorte que l'un d'eux peut se déplacer vers un espace qui contenait déjà un autre germe, s'il s'éloigne. Il est garanti que les bordures du tableau "avant" ne contiennent que des espaces vides et qu'il y a au moins un germe. Ainsi, ce qui suit est une paire d'entrées valide:
Before After
...... ......
.O..O. ....O.
.OO.O. .OO.O.
...... ..O...
Production
Votre sortie est un seul tableau 2D de caractères dans le même format que les entrées. Il est obtenu à partir du tableau «avant» en remplaçant les germes qui se sont déplacés par l'un des >^<v
, selon la direction du mouvement (vous pouvez également utiliser 4 caractères distincts ici). Il peut y avoir plusieurs sorties possibles, mais vous ne devez en donner qu'une seule. Dans l'exemple ci-dessus, une sortie correcte possible est
......
.v..O.
.>v.O.
......
Les mouvements inutiles sont autorisés dans la sortie et les germes peuvent changer de place, ce qui suit est également valide:
......
.v..v.
.>v.^.
......
Règles et notation
Vous pouvez écrire un programme complet ou une fonction. Le nombre d'octets le plus bas l'emporte et les failles standard sont interdites.
Je suis intéressé par des algorithmes relativement efficaces, mais je ne veux pas interdire complètement le forçage brut. Pour cette raison, il y a un bonus de -75% pour résoudre le dernier cas de test dans les 10 minutes sur un processeur moderne (je ne peux pas tester la plupart des solutions, donc je vais juste vous faire confiance ici). Avertissement: je sais qu'il existe un algorithme rapide (recherche de "problème de chemins disjoints"), mais je ne l'ai pas implémenté moi-même.
Cas de test supplémentaires
Before
......
.O..O.
..OO..
......
After
......
..O...
...OO.
..O...
Possible output
......
.>..v.
..vO..
......
Before
.......
.OOOOO.
.O..OO.
.OO..O.
.OOOOO.
.......
After
.......
..OOOOO
.O...O.
.O...O.
.OOOOOO
....O..
Possible output
.......
.>>>>>.
.O..>v.
.Ov..v.
.O>>v>.
.......
Before
..........
.OOO..OOO.
.OOOOOOOO.
.OOO..OOO.
..........
After
..O.......
.OOO..O.O.
..OOOOOOOO
.O.O..OOO.
.......O..
Possible output
..........
.>^O..O>v.
.^O>>>vO>.
.O>^..>vO.
..........
Before
............
.OO..OOOOOO.
.OO......OO.
...OOOOOO...
.O.OOOOOO.O.
...OOOOOO...
.OOOOOOOOOO.
............
After
..........O.
.OO..OOOOO..
.O...O...O..
.O.OOOOOOO..
.O.OOOOOO..O
...OO..OO...
....OOOOOOOO
.OOO........
Possible output
............
.OO..v<<<<^.
.v<......^<.
...OOO>>>...
.O.OOO^OO.>.
...OOv^OO...
.vvvO>>>>>>.
............
Before
................
.OOOOOO.OOOOOOO.
..OO..OOOOOOOOO.
.OOO..OOOO..OOO.
..OOOOOOOO..OOO.
.OOOOOOOOOOOOOO.
................
After
................
..OOOOO.OOOOOOOO
..OO..OOOOOOOOO.
..OO..OOOO..OOOO
..OOOOOOOO..OOO.
..OOOOOOOOOOOOOO
................
Possible output
................
.>>>>>v.>>>>>>>.
..OO..>>^>>>>>v.
.>>v..OOO^..OO>.
..O>>>>>>^..OOO.
.>>>>>>>>>>>>>>.
................
Before
..............................
.OOO.O.O.....O.....O.O.O..O...
..OOO.O...O..OO..O..O.O.......
.....O......O..O.....O....O...
.O.OOOOO......O...O..O....O...
.OO..O..OO.O..OO..O..O....O...
..O.O.O......OO.OO..O..OO.....
..O....O..O.OO...OOO.OOO...O..
.....O..OO......O..O...OO.OO..
........O..O........OO.O.O....
..O.....OO.....OO.OO.......O..
.O.....O.O..OO.OO....O......O.
..O..OOOO..O....OO..........O.
.O..O...O.O....O..O....O...OO.
....O...OO..O.......O.O..OO...
........O.O....O.O....O.......
.OO.......O.OO..O.......O..O..
....O....O.O.O...OOO..O.O.OO..
.OO..OO...O.O.O.O.O...OO...O..
..............................
After
..............................
.OOOOO.......OO.....O..O......
...OO..O...O...O....OO....O...
....O.O......O..OO...OO...O...
.OO.OOOO......OO..O..O........
O.O.OO..O..O..O..OO...O...OO..
.OO.....O....OO.O..O.OO.O.....
......O.....O.....OOO.OO...O..
....O..OOOO..O..O..O.O.O.OO...
..O......O.O........O...O.O...
.O.....OOO.....OO.OO...O...O..
.......OOO..O.O.O...........O.
.O...O.....O...OOOO..O.O....O.
.O..O.O..O.....O......O....OO.
....O..O..O.O......O.....O....
........OOO....O......O..O....
.OO......O..OO..OOO.....O..O..
..O.O....OO..O...OO...O...OO..
.O..OO....O..O...O.O.O.OO.....
..............O............O..
Possible output
..............................
.OOO.O.v.....>.....>.v.O..v...
..>>^.v...>..^>..v..O.v.......
.....<......>..>.....O....O...
.O.<O><O......O...O..O....v...
.<O..O..v<.O..O^..O..>....>...
..<.^.v......OO.O^..>..<O.....
..^....v..v.Ov...>>^.<OO...O..
.....<..OO......O..O...Ov.v<..
........>..O........O^.v.^....
..^.....Ov.....OO.OO.......O..
.^.....^.^..O>.vO....v......O.
..<..Ov^^..O....><..........O.
.O..O...>.v....O..^....^...OO.
....O...<v..O.......<.^..v<...
........O.O....O.v....O.......
.OO.......<.Ov..O.......O..O..
....O....O.<.^...O^v..O.v.OO..
.O^..<<...O.>.v.>.^...<O...v..
..............................
la source
>^<v
correspond à un mouvement d'exactement un pas dans la direction respective.Réponses:
Octave,
494496 octets - bonus de 372 octets = 124 octetsIl y a encore beaucoup de golf à faire sur cette réponse, mais je voulais obtenir l'explication non golfée.
J'ai vu cela comme un problème de satisfaction de contrainte, alors j'ai choisi mon heuristique de recherche locale préférée, Min-conflits . L'idée est, étant donné un placement de départ avec chaque germe dans une destination accessible, de sélectionner un germe aléatoire qui occupe la même cellule de destination qu'un ou plusieurs autres germes et de le déplacer vers une cellule valide qui contient déjà un minimum d'autres germes. Répétez autant de fois que nécessaire jusqu'à ce que le placement corresponde à l'objectif.
Fait intéressant, cet algorithme n'est pas garanti de se terminer (si l'objectif est inaccessible, il continuera indéfiniment, par exemple), mais s'il se termine, il est garanti de produire une solution valide.
Voici le code:
Sortie pour le dernier cas de test:
Le temps moyen écoulé était inférieur à
9 secondes1 seconde * sur un Core i5 de 5 ans, éligible au bonus.J'essaie de faire fonctionner cela sur ideone, mais j'ai ce que je crois être des problèmes de portée avec la façon dont il gère les fonctions imbriquées. (Voici le lien ideone qui ne fonctionne pas pour référence: http://ideone.com/mQSwgZ )Le code sur ideone fonctionne maintenant. J'ai dû forcer toutes les variables à global, ce qui n'était pas nécessaire de l'exécuter localement.
* J'avais une note dans ma version non golfée qu'une des étapes était inefficace, donc je l'ai essayée pour voir si je pouvais accélérer l'exécution et pour 2 octets supplémentaires le temps d'exécution est maintenant réduit à moins d'une seconde. Le code et l'exemple de sortie ont été mis à jour et l'entrée sur ideone a été changée pour le dernier cas de test.
la source
Python, 1171 octets - 878,25 octets bonus = 292,75 octets
Lien Ideone: http://ideone.com/0Ylmwq
Cela prend de 1 à 8 secondes en moyenne sur le dernier test élémentaire, ce qui donne droit au bonus.
Ceci est ma première soumission de code-golf, donc ce n'est probablement pas le programme le mieux joué. Néanmoins, ce fut un défi intéressant et je l'ai beaucoup apprécié. @Beaker mérite une mention pour me rappeler que les recherches basées sur l'heuristique sont une chose. Avant de poster sa solution et de m'inspirer pour refaire la mienne, ma recherche par force brute était trop longue pour être éligible au bonus sur le dernier cas de test (c'était de l'ordre de 69! Itérations, qui est un nombre à 99 chiffres .. .).
Je ne voulais pas copier directement la solution de Beaker, j'ai donc décidé d'utiliser le recuit simulé pour mon heuristique de recherche. Il semble plus lent que min-conflict pour ce problème (probablement parce que c'est un algorithme d'optimisation plutôt que de satisfaction de contraintes), mais il est toujours bien dans la limite des 10 minutes. Il avait également l'avantage d'être assez petit, au niveau du code. J'ai dépensé beaucoup plus d'octets pour transformer le problème que pour trouver une solution.
Explication
Ma solution est probablement assez inefficace au niveau des octets, mais j'ai eu du mal à conceptualiser comment résoudre le problème tel quel et j'ai donc dû le transformer en un problème différent qui était plus facile à comprendre pour moi. J'ai réalisé qu'il y a quatre possibilités pour chaque cellule sur la grille:
Après avoir décomposé les données dans ces classes, j'ai pu transformer davantage le problème. Il était immédiatement évident pour moi que je devais trouver un moyen de fournir un germe de l'ensemble «avant mais pas après» à une cellule de l'ensemble «après mais pas avant». De plus, les germes ne peuvent se déplacer que d'un espace, donc la seule façon pour eux d'affecter des cellules plus éloignées est de "pousser" un chemin ininterrompu de germes dans cette cellule. Cela signifiait que le problème devenait de trouver X chemins disjoints au sommet sur un graphique, où chaque cellule avec un germe était un sommet dans ledit graphique, et les bords représentaient des cellules adjacentes.
J'ai résolu ce problème en construisant d'abord le graphique expliqué ci-dessus. J'ai ensuite énuméré chaque chemin possible depuis chaque cellule dans Avant et chaque cellule dans Après, puis assigné au hasard chaque cellule dans Avant l'un de ses chemins possibles. Enfin, j'ai utilisé un recuit simulé pour muter semi-aléatoirement la solution potentielle jusqu'à ce que je trouve finalement une solution qui n'a aucun conflit pour aucun des chemins.
Version annotée
la source