Algorithmique et Programmation Projet : permutations de matrices

Transcription

Algorithmique et Programmation Projet : permutations de matrices
Algorithmique et Programmation
Projet : permutations de matrices creuses et décompositions de graphes ∗
Ecole normale supérieure
Département d’informatique
[email protected]
2013-2014
Le pivot de Gauss est une méthode classique efficaces
pour résoudre des systèmes linéaires A · x = y.
La complexité du processus est de l’ordre de O n3 opérations. Quand A est creuse, c’est-à-dire que
la plupart de ses coefficients sont égaux à zéro, il est possible d’utiliser des algorithmes plus efficaces.
On souhaite programmer un algorithme qui prend en entrée une matrice creuse, et qui produit une
permutation des lignes et une permutation des colonnes qui mettent la matrice sous forme triangulaire
par blocs. L’intérêt de la procédure est double : cette procédure n’augmente pas le nombre d’entrées
non-nulles dans la matrice ; une fois la matrice en forme triangulaire, il sera suffisant d’appliquer une
résolution linéaire (creuse) aux blocs diagonaux pour résoudre le système linéaire correspondant.
Une matrice rectangulaire décrit naturellement un graphe biparti non-orienté : il y a deux types de
noeuds, les noeuds “lignes” et les noeuds “colonnes”. Chaque entrée non-nulle de la matrice correspond
à une arête du graphe. Le problème original sur les matrices creuses se réduit donc à un problème de
décomposition de graphes : la mise sous forme triangulaire par bloc de la matrice d’adjacence d’un graphe
biparti.
1
Algorithme de décomposition
Dans un graphe non-orienté biparti, un couplage maximum est un ensemble d’arêtes, de cardinal
maximum, et tel qu’elles n’aient aucun sommet en commun. Un sommet est libre s’il n’appartient à
aucune arête du couplage. Un chemin alternant est un chemin dans le graphe qui ne repasse pas deux fois
par le même sommet, et dont une arête sur deux appartient au couplage.
L’algorithme complet a la structure suivante :
1. Construire le graphe biparti G = (L, C, E) associé à la matrice. L est l’ensemble des sommets-lignes
et C l’ensemble des sommets-colonnes.
2. Construire un couplage maximum de G.
3. Faire une partition 1 de L en au plus trois parties, définies comme suit :
Lv = {sommets de L accessibles par un chemin alternant depuis un sommet libre de L}
Lh = {sommets de L accessibles par un chemin alternant depuis un sommet libre de C}
Ls = L − (Lv ∪ Lh )
De même on fait une partition de C en au plus trois parties :
Cv = {sommets colonnes accessibles par un chemin alternant depuis un sommet ligne non-apparié}
Ch = {sommets colonnes accessibles par un chemin alternant depuis un sommet colonne non-apparié}
Cs = C − (Cv ∪ Ch )
4. On en déduit une permutation des lignes et des colonnes qui transforme la matrice d’adjacence en
la figure suivante. On peut démontrer (ce n’est pas demandé) qu’effectivement les trois blocs du
coin inférieur gauche sont nuls, que Ms est une matrice carrée, Mh plus large que haute, et Mv plus
haute que large. L’existence du couplage garantit d’autre part Ms a une diagonale entièrement nonnulle, et que la diagonale qui part dans le coin inférieur droit (resp. supérieur gauche) de Mh (resp.
Mv ) est non-nulle. C’est bien illustré dans [?]. Tout ceci nous donne une première décomposition,
celle de Dulmage-Mendelsohn.
∗ Conception : Charles Bouillaguet. Révisé par Claire Mathieu 9/2013.
1. On admettra que ceci définit bien une partition.
1
Lh
Ch
Mh
Cs
A
Cv
B
Ls
0
Ms
C
Lv
0
0
Mv
5. Décomposer Mh , Ms et Mv en blocs en utilisant les composantes connexes ou fortement connexes.
2
Couplage maximum dans un graphe biparti
Il faut dans un premier temps calculer un couplage maximum. Un chemin augmentant est un chemin
alternant dont les deux extrémités sont des sommets libres. L’idée commune de l’algorithme de FordFulkerson (ou d’Hopkroft-Karp, l’algorithme Hongrois, de flot maximal, ou autre) est de chercher un
couplage qui n’a pas de chemin augmentant. En effet, étant donné un couplage, et un chemin augmentant,
alors on peut construire un nouveau couplage ayant une arête de plus. Il suffit de “flipper” les arêtes du
chemin augmentant (si elles étaient dans le couplage, on les en retire ; si elles n’y étaient pas, on les y
ajoute).
Algorithme de Ford-Fulkerson. Pour trouver un chemin augmentant, on part d’un sommet libre
et on fait un parcours en profondeur, en marquant “vu” tous les sommets visités. Si aucun chemin n’est
trouvé, on part d’un autre sommet libre, mais on ne revisite pas les sommets “vus”.
“Cheap matching”. Avant de se lancer dans la recherche de chemins augmentants, il est bénéfique
de construire un couplage le plus grand possible de manière gloutonne.
3
Composantes (fortement) connexes
Parties rectangulaires. Il se peut que le graphe biparti décrit par Mh (resp. Mv ) ne soit pas connexe.
Dans ce cas-là, il est possible de l’écrire comme l’union disjointe d’une famille de sous-graphes connexes
G01 , . . . , G0k . Si Mi0 est la matrice d’adjacence de G0i , alors la matrice diagonale par bloc :

 0
M1
0 ···
0


 0 M20 . . .
0 
0


M = .
.. 
..
..
 ..
.
.
. 
0
···
0 Mk0
peut être obtenue en permutant les lignes et les colonnes de Mh .
Partie carrée. On peut ensuite mettre Ms sous forme triangulaire par blocs. Pour, on observe que
Ms étant une matrice carrée, elle décrit un graphe orienté G dont le nombre de sommets est le nombre
de lignes de M : si Mij 6= 0 alors on met un arc du sommet i vers le sommet j. On détermine les
composantes fortement connexes C1 , . . . , C` de ce G (avec l’algorithme classique décrit dans [?] et dû
à Tarjan). On observe que le graphe des composantes connexes est orienté et acyclique, donc on peut
(grâce à un tri topologique) renuméroter les composantes connexes de G de telle sorte que s’il y a dans
G une arête de Ci à Cj , alors i < j. Enfin on met les noeuds du graphe dans l’ordre C1 , . . . , C` , ce qui
met alors automatiquement la matrice en forme triangulaire par blocs (et les blocs diagonaux décrivent
les composantes fortement connexes).
4
Travail demandé
Programmez les deux phases de la décomposition en C. Votre programme devra lire les matrices dans
des fichiers au format suivant : la première ligne du fichier contient, séparé par des espaces, les dimensions
de la matrice puis le nombre de coefficients non-nuls. Toutes les lignes suivantes contiennent, séparés par
2
des espaces, deux entiers et un flottant. Chaque ligne décrit un coefficient de la matrice. Par exemple, la
matrice suivante :


1
0
0
6
0
0 10.5
0
0
0 


0
0
.015
0
0 


0 250.5
0
−280 33.32
0
0
0
0
12
est décrite par le fichier :
5
1
2
3
1
4
4
4
5
5
1
2
3
4
2
4
5
5
8
1.000
1.050
1.500e-02
6
2.505e+02
-280
33.32
12
Votre programme devra afficher le nombre de blocs trouvés dans chacune des phases de la décomposition et afficher leurs dimensions. Il devra écrire trois fichiers : un contenant la permutations des lignes
(resp. des colonnes), et un contenant la matrice permutée. Vous pouvez/devez tester votre programme
sur les matrices du MatrixMarket :
http://math.nist.gov/MatrixMarket
3