Sujet 3 - Algorithmes sur les graphes

publicité
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
Téléchargement