TITRE DESSUS 1re année F. Popineau 204 9. Graph Traversal 9.6.2 Playing With Wheels 2016 – 2017 TITRE DESSOUS PC/UVa IDs: 110902/10067, Popularity: C, Success rate: average Level: 2 Algorithmes IS1210 – PC 3 Consider the following mathematical machine. Digits ranging from 0 to 9 are printed consecutively (clockwise) on the periphery of each wheel. The topmost digits of the wheels form a four-digit integer. For example, in the following figure the wheels form the integer Each wheel has two buttons associated with it. Pressing the button Roues 8,056. codeuses marked Vous with a left arrow rotates the wheel one digit in the clockwise direction and avez la machine représentée ci-dessous. Elle comporte 4 roues et les chiffres de 0 à 9 sont imprimés sur la pressing the deone marked with thesont right arrow rotates by d’afficher one digit indesthe opposite périphérie chacune des roues. Les roues masquées, mais une lucarne it permet un seul chiffres de chaque roue, de sorte que les 4 chiffres des 4 roues forment un nombre. Sur la figure, c’est le nombre 8056 qui direction. est affiché. TITRE DESSUS Chaque roue possède deux boutons associés : un bouton avec une flèche à gauche et un bouton avec une flèche Weà start with bouton an initial wheels, with theLatopmost digitsavecforming droite. Chaque déplaceconfiguration la roue d’un chiffre of versthe la gauche ou vers la droite. machine démarre une configuration . On vous une listeadeset configurations interdites 𝐹configurations 𝐹 𝐹 𝐹 et une configuration the integer S1 S2 S𝑆3 S𝑆 4𝑆. 𝑆You willdonne be given of n forbidden F i 1 Fi 2 Fi 3 Fi 4 finale 𝐹 𝐹 𝐹 𝐹 . On vous demande de déterminer le nombre minimal de boutons à presser pour transformer la (1 ≤ configuration i ≤ n) and a target configuration Tpar T2lesT3configurations T4 . Yourinterdites. job is to write a program initiale en la configuration finale sans passer 1 — Question to calculate the 1minimum number of button presses required to transform the initial Formaliser ce problème comme un problème de graphe. configuration to the target configuration without passing through a forbidden one. — Question 2 TITRE DESSOUS Proposer une solution algorithmique à ce problème. Quelle méthode allez-vous utiliser et pourquoi ? Quelle est la complexité attendue de votre algorithme ? Donner le pseudocode correspondant. — Question 3 Programmer votre méthode. Les entrées seront codées de la façon suivante. La première ligne comprendra un seul entier 𝑁 donnant le nombre de cas à tester. Ensuite on trouvera une ligne vide. Chaque cas de test sera décrit par : — uneof ligne comprenant 4 chiffres dean la configuration de départ, séparés des espacesof test cases. The first line the input lescontains integer N giving theparnumber — une ligne comprenant les 4 chiffres de la configuration finale, séparés par des espaces blank line—then follows. une ligne avec un nombre indiquant les configurations interdites Input A The first line of each test case contains the initial configuration of the wheels, specified by four digits. Two consecutive digits are separated by a space. The next line contains 1 the target configuration. The third line contains an integer n giving the number of forbidden configurations. Each of the following n lines contains a forbidden configuration. There is a blank line between two consecutive input sets. TITRE DESSUS — une suite de lignes avec une configuration interdite par ligne. Le cas de test suivant est séparé par une ligne vide du cas précédent. Voici un fichier de test. Voici également de quoi lire le fichier en question : # Lecture d'une suite de cas à tester. # Aucun contrôle n'est fait sur la validité # syntaxique de ce qui est lu dans le fichier def readProblem(filename): problemList = [] with open(filename) as f: nCases = int(f.readline().split()[0]) f.readline() for i in range(nCases): start = tuple(int(x) for x in f.readline().split()) dest = tuple(int(x) for x in f.readline().split()) forbiddenSets = set() nbForbidden = int(f.readline().split()[0]) for j in range(nbForbidden): forbiddenSets.add(tuple(int(x) for x in f.readline().split())) f.readline() problemList.append((start, dest, forbiddenSets)) return problemList if __name__ == '__main__': problems = readProblem('roues-codeuses-exemple.txt') for start, dest, f in problems: print(BFS(start, dest, f)) La fonction readProblem() retourne une liste de triplets : configuration de départ, configuration d’arrivée, liste de configurations interdites. Chaque configuration est un tuple de 4 chiffres. En sortie, on imprimera le nombre minimum d’appuis sur les touches nécessaire pour passer de chaque configuration de départ à la configuration finale du cas de test, ou bien -1 si c’est impossible. Clustering — Motivation On dispose d’un ensemble d’objets et d’une fonction de distance entre ces objets. Ces objets peuvent être des images, des pages web, des personnes, etc. La distance est définie sur la base des attributs de l’objet. Dans le cadre d’un site marchand, les attributs d’une personne peuvent comprendre l’historique de ses achats. La fonction de distance doit croître lorsque la similarité entre deux objets diminue. Le problème du clustering va consister à regrouper les objets similaires en clusters. On espère bien sûr que les clusters soient éloignés les uns des autres. — Formalisation du problème Soit 𝑈 un ensemble de 𝑛 objets 𝑜 , 𝑜 , … , 𝑜 . La fonction distance est définie sur 𝑈 × 𝑈 et associe 𝑑(𝑜 , 𝑜 ) à toute paire d’objets (𝑜 , 𝑜 ). On demande que $ — 𝑑(𝑜 , 𝑜 ) = 0 — 𝑑(𝑜 , 𝑜 ) > 0 si 𝑖 ≠ 𝑗 — 𝑑(𝑜 , 𝑜 ) = 𝑑(𝑜 , 𝑜 ) Étant donné un entier 𝑘, une 𝑘−partition de 𝑈 (ou 𝑘−clustering) est une partition de 𝑈 en 𝑘 sous-ensembles non-vides 𝐶 , 𝐶 , … , 𝐶 . La propriété de partition implique que 𝐶 = 𝑈 et ∀𝑖, 𝑗 𝑖 ≠ 𝑗 ⇒ 𝐶 ∩ 𝐶 = ∅. Lorsqu’on fait de la classification, on cherche à construire une partition qui optimise un certain critère. Il y a trois familles de critères naturels qui sont la séparation, la dispersion et l’homogénéité. 2 Dans le premier cas, une bonne partition va conduire à maximiser les écarts entre classes. Dans le second cas, on va chercher à minimiser le diamètre des classes pour créer des classes les plus concises possibles. Dans le dernier cas, on va minimiser une fonction d’inertie : la somme des carrés à un centre, qu’il soit réel ou virtuel. Nous allons nous intéresser au premier cas. La séparation d’une 𝑘 −partition est la plus petite distance entre deux éléments appartenant à des clusters différents. separation(𝐶 , … , 𝐶 ) = min 𝑑(𝑝, 𝑞). , ∈ , ∈ Problème : trouver une 𝑘 − partition de séparation maximale parmi toutes les 𝑘 − partitions. — Question 1 Proposer un algorithme glouton pour construire une 𝑘 − partition de séparation maximale. — Question 2 Prouver que la séparation obtenue par votre algorithme est effectivement supérieure à la séparation de toute autre 𝑘 − partition. Quelle est la valeur de cette séparation ? Far-west Vous êtes transportés au Far-West à l’époque où les bandits attaquent les diligences. Vous avez connaissance des lignes empruntées par les diligences qui sont modélisées comme un graphe ou les noeuds sont les villes et les arêtes sont les routes (ou pistes !) entre les villes. Pour chaque route, vous avez une connaissance historique de la probabilité d’une attaque. Entre les villes 𝑣 et 𝑣 , vous savez que la probabilité d’une attaque est de 𝑝 . Les probabilités d’attaques entre différentes routes sont indépendantes. — Question On vous demande d’acheminer une diligence de la ville 𝐴 à la ville 𝐵 en utilisant le réseau de pistes. Donner un algorithme qui détermine le chemin le plus sûr entre 𝐴 et 𝐵. Vous devrez définir la notion de chemin sûr. Planification de chemin — Préliminaires Vous disposez d’un robot pour lequel vous devez calculer une trajectoire entre un point de départ et un point d’arrivée. Vous discrétisez l’espace en coordonnées entières et vous considérez que chaque point de coordonnées (𝑖, 𝑗) possède 8 voisins indiqués sur la figure ci-dessous : 3 Les distances entre les voisins sont calculées par rapport aux centres des carrés : 4 des voisins sont à une distance de 1, et 4 autres sur les diagonales sont à une distance de √2. Le calcul de la trajectoire doit être aussi efficace que possible : vous ne vous intéressez pas seulement à rallier la destination par le plus court chemin, mais également à explorer la plus petite partie possible de l’espace pour obtenir la trajectoire. Pour évaluer la performance de votre algorithme, vous vous intéresserez à compter explicitement les noeuds développés par vos algorithmes : ceux par lesquels vous avez envisagé de passer lors de votre recherche de trajectoire. Vous supposerez votre espace carré de dimension 𝑛 ×𝑛. La grille représentant cette espace vous sera fournie sous forme de tableau à deux dimensions (listes de listes en Python). Chaque case de ce tableau représente un lieu possible de coordonnées (𝑖, 𝑗) pour votre robot. Pour compliquer un peu les choses, certaines coordonnées sont interdites : il existe des obstacles dans votre univers. D’autre part, il y a des murs qui cernent l’espace navigable. Vous recevrez la description de l’espace sous forme d’un tableau à deux dimensions contenant des chiffres dont la signification est la suivante : 0 : case libre 1 : case interdite, obstacle ou mur 2 : case de départ 3 : case d’arrivée 4 : case occupée par le robot 5 : case visitée 6 : case à visiter Vous téléchargerez ce programme auquel vous ajouterez vos propres algorithmes. Vous disposez également de 3 cartes de départ : espace vide, un mur au milieu, labyrinthe. Le programme vous permet de choisir la carte et l’algorithme à appliquer. Vous pouvez dessiner vos propres cartes. Le programme fourni travaille avec une structure de données class PathPlanning dont vous devez connaître certains détails. Vous recevez une instance de cette classe comme paramètre de la fonction de calcul de la trajectoire que vous devez écrire. Cette instance possède plusieurs attributs : 4 — — — — — — — — width largeur de l’espace en nombre de cases height longueur de l’espace en nombre de cases origin position de départ (x, y) dest position d’arrivée (x, y) map tableau à deux dimensions [x][y] des statuts des cases du terrain. Chacune de vos fonctions de calcul de trajectoire devra renvoyer un triplet de valeurs : — un booléen indiquant si la trajectoire a été trouvée — la longueur de la trajectoire trouvée — le nombre de noeuds développés par votre exploration. On vous donne également une fonction Adjacents(u, problem) qui étant donné un problème (instance de class PathPlanning) et une position 𝑢 sur le terrain, vous calcule la liste des posistions adjacentes où vous avez le droit de vous déplacer. Ces cases sont celle de statut différent de 1. Cette fonction renvoie une liste de paires (position, distance). La distance vaut soit 1, soit √2 selon la position. Question : algorithme de Dijkstra La première idée pour calculer un plus court chemin dans un graphe consiste à implémenter l’algorithme de Dijkstra. Nous sommes bien ici dans un graphe, avec des coûts associés aux arêtes qui sont positifs. Expliquez en quoi l’algorithme de Dijkstra dans sa formulation stricte n’est pas adapté à notre problème de navigation. Question : exploration à coût uniforme Si l’algorithme de Dijkstra n’est pas adapté, il faut le modifier pour calculer malgré tout un plus court chemin entre nos deux noeuds d’origine et de destination. Pouvez-vous concevoir un algorithme basé sur un principe similaire à celui de Dijkstra, mais exploitant la notion de frontière. La frontière est cet ensemble de noeuds que vous devez expanser pour progresser dans votre recherche. Au départ, la frontière est réduite au noeud origine. À l’étape suivante, la frontière est constituée des voisins du noeud origine et ainsi de suite. Lorsque le noeud origine sort de la frontière, il devient clos. Les noeuds à l’intérieur de la frontière sont les noeuds clos : leur exploration est terminée et ont connaît la meilleure distance entre chacun de ces noeuds et l’origine. La meilleure distance des noeuds sur la frontière au noeud origine n’est pas encore connue : on en a une estimation qui peut encore être améliorée. Cette méthode d’exploration de l’espace porte le nom d’exploration à coût uniforme ou uniform cost search. Vous exprimerez votre algorithme en pseudo-code. Vous ferez apparaître explicitement les listes closed et frontier. Question : implémentation UCS Programmez votre algorithme en pseudo-code dans le cadre du programme fourni précédemment. Question : information La stratégie à coût uniforme dérivée de l’algorithme de Dijkstra est très prudente : elle s’éloigne très lentement du noeud origine. De quelle information disposez-vous et qui pourrait vous permettre de développer moins de noeuds pour aller plus rapidement vers la destination ? On qualifiera de fonction heuristique une fonction qui estime le coût de la distance entre un noeud et le noeud de destination. Proposez des fonctions heuristiques. Question : exploitation Comment exploiter simplement l’information précédente ? Implémenter un algorithme de type Best-First exploitant uniquement cette information. Visualiser l’espace des noeuds visités. Question : optimisation Comment exploiter au mieux l’information utilisée par l’algorithme de Dijkstra et celle utilisée par l’algorithme Best-first ? Implémenter cet algorithme qu’on appelle algorithme 𝐴⋆ et comparer avec les algorithmes Uniform Cost Search et Best First Search. Question : optimalité On qualifie d’heuristique admissible une fonction heuristique qui ne surestime jamais le coût réel du chemin optimal entre un noeud et le noeud de destination. Montrez que si on utilise une heuristique admissible et que la fonction de coût des noeuds est croissante monotone, l’algorithme 𝐴⋆ est complet et optimal. Question : BFS bi-directionnel Une idée permettant également de diminuer l’espace à explorer dans certains cas consiste à explorer simultanément depuis la source et depuis la destination. Lorsque les deux frontières de noeuds que les explorations développent se rencontrent, on a trouvé un chemin. Implémentez un algorithme d’exploration bi-directionnelle. 5