introduction
Le défi est une variante très intéressante du circuit de jeu et de ces deux défis:
La source de ce défi est ici (en allemand): c't-Racetrack
Ce défi est particulièrement intéressant (et différent des deux défis susmentionnés) car il combine un immense espace de recherche avec certaines conditions exactes qui doivent être remplies. En raison de l'immense espace de recherche, les techniques de recherche exhaustives sont difficiles à utiliser, en raison des conditions exactes, les méthodes approximatives ne sont pas non plus facilement utilisables. En raison de cette combinaison unique (plus l'intuition sous-jacente de la physique), le problème est fascinant (et tout ce qui concerne les voitures de course est fascinant de toute façon ;-)
Défi
Jetez un oeil à l'hippodrome suivant ( source ):
Vous devez commencer (120,180)
et terminer exactement à (320,220)
("Ziel" en allemand) sans toucher l'un des murs.
La voiture est contrôlée par des vecteurs d'accélération de la forme (a_x,a_y)
- à titre d'exemple:
(8,-6)
(10,0)
(1,9)
Le premier nombre est l'accélération pour le vecteur x, le second pour le vecteur y. Ils doivent être des entiers car vous n'êtes autorisé à utiliser que les points entiers sur la grille. De plus, la condition suivante doit être remplie:
a_x^2 + a_y^2 <= 100,
ce qui signifie que l'accélération dans n'importe quelle direction doit être inférieure ou égale à 10
.
Pour voir comment cela fonctionne, regardez l'image suivante ( source ):
Par exemple: à partir de (120,180)
vous, accélérez 8
dans la direction x et -6
dans la direction y. Pour l'étape suivante, c'est votre vitesse à laquelle vous ajoutez votre accélération (10,0)
pour obtenir (physiquement correct) votre prochain mouvement résultant (à pointer (146,168)
. Le mouvement résultant est ce qui compte quand il s'agit d'examiner si vous avez touché l'un des murs. À l'étape suivante vous ajoutez à nouveau votre prochain vecteur d'accélération à votre vitesse actuelle pour obtenir le prochain mouvement et ainsi de suite. Ainsi, à chaque étape, votre voiture a une position et une vitesse. (Dans l'illustration ci-dessus, les flèches bleues sont pour la vitesse, les flèches orange pour l'accélération et les flèches rouges foncées pour le mouvement résultant.)
Comme condition supplémentaire, vous devez avoir (0,0)
une vitesse terminale lorsque vous êtes sur le point d'arrivée (320,220)
.
La sortie doit être une liste de vecteurs d'accélération sous la forme susmentionnée.
Le gagnant est celui qui fournit un programme qui trouve une solution avec le moins de vecteurs d'accélération.
Tiebreaker
De plus, ce serait bien si vous pouvez montrer qu'il s'agit d'une solution optimale et s'il s'agit de la seule solution optimale ou s'il existe plusieurs solutions optimales (et quelles sont-elles).
Il serait également bon que vous puissiez donner un aperçu général du fonctionnement de votre algorithme et commenter le code afin que nous puissions le comprendre.
J'ai un programme qui vérifie si une solution donnée est valide et je donnerai des commentaires.
Addendum
Vous pouvez utiliser n'importe quel langage de programmation mais je serais particulièrement ravi que quelqu'un utilise R parce que je l'utilise beaucoup dans mon travail de jour et que je m'y suis habitué :-)
Addendum II
Pour la première fois, j'ai commencé une prime - j'espère que cela fait rouler la balle (ou mieux: que la voiture roule :-)
print "(10,42)\n(62,64)..."
?Réponses:
Python, 24 étapes (travaux en cours)
L'idée était de résoudre le problème continu en premier, en réduisant considérablement l'espace de recherche, puis en quantifiant le résultat sur la grille (en arrondissant simplement au point de grille le plus proche et en recherchant les 8 carrés environnants)
Je paramétre le chemin comme une somme de fonctions trigonométriques (contrairement aux polynômes, ils ne divergent pas et sont plus faciles à contrôler). Je contrôle également la vitesse directement au lieu de l'accélération, car il est plus facile d'appliquer la condition aux limites en multipliant simplement une fonction de pondération qui tend vers 0 à la fin.
Ma fonction objective consiste en
un score exponentiel pour l'accélération> 10
un score polynomial pour la distance euclidienne entre le dernier point et le
score cible constant élevé pour chaque intersection avec un mur, diminuant vers les bords du mur
Pour minimiser le score, je lance tout cela dans l' optimisation Nelder-Mead et j'attends quelques secondes. L'algorithme réussit toujours à arriver au bout, à s'arrêter là et à ne pas dépasser l'accélération maximale, mais il a des problèmes avec les murs. Le chemin se téléporte dans les coins et s'y bloque, ou s'arrête à côté d'un mur avec le but juste en face (image de gauche)
Pendant les tests, j'ai eu de la chance et j'ai trouvé un chemin qui a été gondolé de manière prometteuse (image de droite) et après avoir peaufiné les paramètres, je pourrais l'utiliser comme hypothèse de départ pour une optimisation réussie.
Quantification
Après avoir trouvé un chemin paramétrique, il était temps de supprimer les points décimaux. Regarder le voisinage 3x3 réduit l'espace de recherche d'environ 300 ^ N à 9 ^ N, mais c'est toujours trop grand et ennuyeux à mettre en œuvre. Avant d'emprunter cette voie, j'ai essayé d'ajouter un terme "Snap to Grid" à la fonction objectif (les parties commentées). Une centaine d'étapes d'optimisation supplémentaires avec l'objectif actualisé et un simple arrondi ont suffi pour obtenir la solution.
Le nombre d'étapes était fixe et ne faisait pas partie de l'optimisation, mais comme nous avons une description analytique du chemin, (et puisque l'accélération maximale est bien inférieure à 10), nous pouvons la réutiliser comme point de départ pour une optimisation plus poussée avec un nombre plus petit de pas de temps
À faire: GUI qui vous permet de dessiner un chemin initial pour obtenir un sens approximatif de la direction. Tout est meilleur que l'échantillonnage aléatoire à partir d'un espace à 14 dimensions
la source