Fondamentaux

Coupe minimale

Une coupe minimale est l'ensemble d'arêtes le moins coûteux dont la suppression coupe un graphe en deux. Ce guide distingue les deux problèmes qui portent ce nom, la coupe s-t minimale et la coupe minimale globale, et montre comment calculer chacune : à partir d'un flot maximal et de son graphe résiduel, avec les arbres de Gomory-Hu, avec l'algorithme de Stoer-Wagner et avec la contraction aléatoire de Karger, sur un même réseau d'exemple et avec du code Python testé tout du long.

22 Min de lecture Mis à jour : Octobre 2026 Niveau Débutant
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

1. Ce qu'est une coupe

Une coupe partage les sommets d'un graphe en deux groupes non vides, S et le reste, V \ S. Les arêtes qui ont une extrémité dans chaque groupe traversent la coupe, et les supprimer déconnecte les deux groupes l'un de l'autre. Dans un graphe pondéré, le poids d'une coupe, aussi appelé sa capacité, est le poids total des arêtes qui la traversent ; dans un graphe non pondéré, c'est simplement leur nombre. Une coupe minimale est une coupe de poids minimal : la façon la moins coûteuse de casser le graphe en deux morceaux.

L'exemple suivi est un petit réseau informatique de 8 routeurs reliés par 15 liaisons. Chaque poids est la capacité d'une liaison en gigabits par seconde. Les routeurs A à D se trouvent dans un bâtiment et E à H dans un autre :

bâtiment 1 :  A-B 4   A-C 3   A-D 2   B-C 2   B-D 3   C-D 3
bâtiment 2 :  E-F 3   E-G 2   E-H 3   F-G 4   F-H 2   G-H 3
entre eux :   C-E 2   D-F 1   B-G 1

8 routeurs, 15 liaisons, capacité totale 38
À gauche : le réseau de 8 routeurs, de A à D à gauche et de E à H à droite, avec les capacités des liaisons. Les trois liaisons entre les bâtiments, C-E de capacité 2, D-F de capacité 1 et B-G de capacité 1, sont en pointillés rouges et traversées par une ligne de coupe verticale : la coupe minimale, de poids 4. À droite : les quatre plus légères des 127 coupes possibles. La coupe minimale sépare A, B, C, D de E, F, G, H avec un poids de 4 ; la suivante isole H seul avec un poids de 8, puis viennent A seul et D seul avec un poids de 9.
La coupe minimale du réseau : trois liaisons d'une capacité totale de 4 relient les deux bâtiments. Toute autre coupe coûte au moins 8.

Couper les trois liaisons entre les bâtiments, C-E, D-F et B-G, coûte 2 + 1 + 1 = 4 et coupe le réseau en deux. C'est la coupe minimale, et c'est la seule coupe de poids 4. C'est aussi le point faible du réseau : les deux bâtiments peuvent échanger au plus 4 gigabits par seconde, quelle que soit la configuration du reste du réseau. La coupe suivante la moins coûteuse isole le routeur H, pour un coût de 8, la somme de ses propres liaisons.

2. Deux problèmes sous le même nom

« Coupe minimale » désigne deux problèmes apparentés, et la plupart des confusions sur le sujet viennent de ce qu'on les mélange :

Une borne simple relie les deux idées. Isoler un seul sommet coûte la somme des poids de ses liaisons, son degré pondéré, donc la coupe minimale globale ne peut jamais dépasser le plus petit degré. Hassler Whitney a prouvé la version non pondérée en 1932 : sommet-connexité ≤ arête-connexité ≤ degré minimum. Dans l'exemple, le plus petit degré pondéré est 8, en H, et la coupe minimale de 4 est bien en dessous : le point faible est un faisceau de liaisons entre deux groupes, pas un routeur isolé mal connecté. Sans les poids, la borne est atteinte ici : chaque routeur a au moins 3 liaisons, et 3 liaisons sont le plus petit nombre qui déconnecte le réseau.

3. Pourquoi essayer toutes les coupes ne passe pas à l'échelle

Un graphe à n sommets possède 2n-1 - 1 coupes : chaque sommet autre qu'un premier sommet fixé va d'un côté ou de l'autre, et la répartition qui met tout du même côté n'est pas une coupe. L'exemple en a 27 - 1 = 127, assez peu pour toutes les énumérer.

from itertools import combinations

LINKS = [('A', 'B', 4), ('A', 'C', 3), ('A', 'D', 2), ('B', 'C', 2), ('B', 'D', 3), ('C', 'D', 3),
         ('E', 'F', 3), ('E', 'G', 2), ('E', 'H', 3), ('F', 'G', 4), ('F', 'H', 2), ('G', 'H', 3),
         ('C', 'E', 2), ('D', 'F', 1), ('B', 'G', 1)]

def cut_weight(side, links):
    """Poids total des liaisons qui ont exactement une extrémité dans side."""
    return sum(w for u, v, w in links if (u in side) != (v in side))

def all_cuts(links):
    """Toutes les partitions des sommets en deux côtés non vides, chacune comptée une fois."""
    vertices = sorted({x for u, v, _ in links for x in (u, v)})
    first, rest = vertices[0], vertices[1:]
    for r in range(len(rest)):                 # side contient toujours le premier sommet
        for others in combinations(rest, r):
            side = {first, *others}
            yield cut_weight(side, links), ''.join(sorted(side))

cuts = sorted(all_cuts(LINKS))
print(len(cuts), 'cuts')
# 127 cuts
for weight, side in cuts[:4]:
    print(weight, side)
# 4 ABCD
# 8 ABCDEFG
# 9 A
# 9 ABCEFGH

Le nombre double à chaque sommet : 50 sommets donnent déjà plus de 560 000 milliards de coupes. Le reste de ce guide porte sur des algorithmes qui trouvent le minimum sans examiner la plupart d'entre elles. Le guide sur la complexité des algorithmes de graphes explique pourquoi une croissance exponentielle de ce type exclut la force brute.

4. Coupes s-t minimales par flot maximal

Le théorème flot maximal / coupe minimale, énoncé par Lester Ford et Delbert Fulkerson dans un rapport de la RAND de 1954 et publié en 1956, l'année même où Peter Elias, Amiel Feinstein et Claude Shannon en ont donné une preuve différente, affirme que le plus grand flot que l'on peut envoyer de s à t est égal au poids de la plus petite coupe qui les sépare. Un sens est facile : chaque unité de flot doit traverser chaque coupe s-t, donc aucun flot ne peut dépasser aucune coupe. Tout le contenu du théorème est que les deux se rejoignent toujours. Le guide sur les flots dans les réseaux et le théorème flot maximal / coupe minimale le démontre avec des chemins augmentants.

La question avait une origine militaire. En 1955, T. E. Harris et F. S. Ross, de la RAND Corporation, ont modélisé les chemins de fer de l'ouest de l'Union soviétique et de l'Europe de l'Est sous la forme d'un réseau de 44 sommets et 105 arêtes, pour trouver l'ensemble de lignes le moins coûteux à perturber. Ils s'intéressaient davantage à la coupe qu'au flot : leur rapport désigne une coupe minimale de 163 000 tonnes comme « le goulot d'étranglement ». Il n'a été déclassifié qu'en 1999.

Pour calculer des coupes, l'important est la manière dont la coupe découle d'un flot maximal. Lorsqu'il ne reste plus aucun chemin augmentant de s à t, rassemblez tous les sommets que s peut encore atteindre par les arêtes ayant de la capacité disponible, qui forment le graphe résiduel. Cet ensemble est le côté S d'une coupe minimale, et les arêtes saturées qui en sortent forment la coupe. Le programme ci-dessous exécute l'algorithme d'Edmonds-Karp, qui augmente toujours le long d'un plus court chemin, puis lit la coupe. Chaque liaison non orientée peut porter du flot dans les deux sens.

from collections import deque

LINKS = [('A', 'B', 4), ('A', 'C', 3), ('A', 'D', 2), ('B', 'C', 2), ('B', 'D', 3), ('C', 'D', 3),
         ('E', 'F', 3), ('E', 'G', 2), ('E', 'H', 3), ('F', 'G', 4), ('F', 'H', 2), ('G', 'H', 3),
         ('C', 'E', 2), ('D', 'F', 1), ('B', 'G', 1)]

def min_st_cut(links, s, t):
    """Flot maximal d'Edmonds-Karp, puis la coupe s-t minimale lue sur le graphe résiduel."""
    cap = {}
    for u, v, w in links:                      # une liaison non orientée porte du flot dans les deux sens
        cap.setdefault(u, {})[v] = cap.get(u, {}).get(v, 0) + w
        cap.setdefault(v, {})[u] = cap.get(v, {}).get(u, 0) + w
    flow = 0
    while True:
        parent, queue = {s: None}, deque([s])
        while queue and t not in parent:       # plus court chemin augmentant par BFS
            u = queue.popleft()
            for v, c in cap[u].items():
                if c > 0 and v not in parent:
                    parent[v] = u
                    queue.append(v)
        if t not in parent:
            break
        path, v = [], t
        while parent[v] is not None:
            path.append((parent[v], v))
            v = parent[v]
        push = min(cap[u][v] for u, v in path)
        for u, v in path:                      # consomme la capacité, ouvre le sens inverse
            cap[u][v] -= push
            cap[v][u] += push
        flow += push
    side = set(parent)                         # encore accessible depuis s dans le graphe résiduel
    cut = [(u, v, w) for u, v, w in links if (u in side) != (v in side)]
    return flow, ''.join(sorted(side)), cut

print(min_st_cut(LINKS, 'A', 'H'))
# (4, 'ABCD', [('C', 'E', 2), ('D', 'F', 1), ('B', 'G', 1)])
print(min_st_cut(LINKS, 'A', 'B'))
# (9, 'A', [('A', 'B', 4), ('A', 'C', 3), ('A', 'D', 2)])
À gauche : un flot maximal de 4 de A à H, composé de trois chemins : A, C, E, H qui porte 2, A, D, F, H qui porte 1 et A, B, G, H qui porte 1. À droite : après le flot, les routeurs encore accessibles depuis A par des liaisons ayant de la capacité disponible sont A, B, C et D, ombrés. Les trois liaisons saturées qui sortent de cet ensemble, C-E, D-F et B-G, forment la coupe minimale de poids 4.
Un flot maximal de 4 de A à H remplit les trois liaisons entre les bâtiments. Les routeurs que A peut encore atteindre forment un côté de la coupe minimale.

De A à H, le flot maximal vaut 4, et il peut se décomposer en trois chemins portant 2, 1 et 1 unités. Ensuite, les trois liaisons entre les bâtiments sont saturées, A peut encore atteindre B, C et D mais rien au-delà, et la coupe est exactement la coupe des bâtiments. De A à B, le flot vaut 9 et la coupe isole A. Edmonds-Karp s'exécute en temps O(V E2) ; il existe des algorithmes de flot plus rapides, mais tout algorithme correct donne de cette façon une coupe s-t minimale.

Pour les graphes non pondérés, le théorème a une forme combinatoire célèbre, qui porte généralement le nom de Karl Menger, dont le théorème de 1927 est la version pour les sommets : le plus grand nombre de chemins de u à v qui ne partagent aucune arête est égal au plus petit nombre d'arêtes dont la suppression sépare u de v. Cette forme pour les arêtes du théorème de Menger découle du théorème flot maximal / coupe minimale. Dans l'exemple, sans tenir compte des poids, il y a trois chemins arêtes-disjoints de A à H, et trois liaisons entre les bâtiments.

5. Coupes minimales globales par flots maximaux

Une coupe minimale globale sépare une certaine paire de sommets ; c'est donc la plus petite coupe s-t sur l'ensemble des paires. Moins de paires suffisent. Fixez un sommet quelconque s : quelle que soit la coupe minimale, un sommet t se trouve de l'autre côté, donc le minimum sur les n - 1 choix de t la trouve. Dans l'exemple, en fixant s = A, les quatre exécutions avec t dans l'autre bâtiment renvoient toutes 4.

Ralph Gomory et T. C. Hu ont montré en 1961 que n - 1 flots maximaux révèlent bien davantage : ils construisent un arbre sur les mêmes sommets, appelé aujourd'hui arbre de Gomory-Hu, dans lequel la coupe minimale entre deux sommets quelconques est égale au plus petit poids d'arête sur le chemin de l'arbre qui les relie. Pour l'exemple, les sept arêtes de l'arbre portent les valeurs 4, 8, 9, 9, 10, 10 et 10, si bien que la valeur de chacune des 28 coupes minimales entre paires peut s'y lire, et la plus petite arête, 4, est la coupe minimale globale.

Les méthodes fondées sur les flots fonctionnent aussi pour les graphes orientés, et ce sont les bons outils lorsque la coupe minimale entre des sommets précis importe. Pour la coupe minimale globale d'un graphe non orienté, les deux algorithmes suivants sont plus simples et plus rapides.

6. L'algorithme de Stoer-Wagner

En 1997, Mechthild Stoer et Frank Wagner ont publié un algorithme qui trouve la coupe minimale globale d'un graphe non orienté à poids positifs ou nuls sans utiliser le moindre flot, en s'appuyant sur des travaux antérieurs de Hiroshi Nagamochi et Toshihide Ibaraki. Il se déroule en n - 1 phases. Chaque phase fait trois choses :

  1. En partant d'un sommet quelconque, ajoutez les sommets un par un, en choisissant toujours celui qui est le plus fortement relié à ceux déjà ajoutés : le plus grand poids total de liaisons vers l'ensemble en croissance. Cet ordre est appelé recherche par adjacence maximale.
  2. Lorsque le dernier sommet t est ajouté, le poids de ses liaisons vers tout le reste est la coupe de la phase. Un court argument montre que c'est une coupe minimale qui sépare t de s, le sommet ajouté juste avant lui.
  3. Fusionnez s et t en un seul sommet, en additionnant les poids des liaisons parallèles, et commencez la phase suivante sur le graphe réduit.

Soit la coupe minimale globale sépare s de t, auquel cas cette phase l'a trouvée, soit elle les garde du même côté, auquel cas les fusionner ne fait rien perdre. Après n - 1 phases, la plus petite des coupes de la phase est la réponse.

LINKS = [('A', 'B', 4), ('A', 'C', 3), ('A', 'D', 2), ('B', 'C', 2), ('B', 'D', 3), ('C', 'D', 3),
         ('E', 'F', 3), ('E', 'G', 2), ('E', 'H', 3), ('F', 'G', 4), ('F', 'H', 2), ('G', 'H', 3),
         ('C', 'E', 2), ('D', 'F', 1), ('B', 'G', 1)]

def stoer_wagner(links):
    """Coupe minimale globale : n - 1 phases, chacune se terminant par une coupe de la phase et une fusion."""
    w = {}
    for u, v, c in links:
        w.setdefault(u, {})[v] = w.get(u, {}).get(v, 0) + c
        w.setdefault(v, {})[u] = w.get(v, {}).get(u, 0) + c
    groups = {v: v for v in w}                 # les sommets fusionnés retiennent ce qu'ils contiennent
    best, phase = (float('inf'), ''), 0
    while len(w) > 1:
        phase += 1
        start = min(w)
        order, attached = [start], {v: w[start].get(v, 0) for v in w if v != start}
        while attached:                        # ajoute ensuite le sommet le plus fortement relié
            v = max(sorted(attached), key=attached.get)
            phase_cut = attached.pop(v)
            order.append(v)
            for x, c in w[v].items():
                if x in attached:
                    attached[x] += c
        s, t = order[-2], order[-1]
        side = ''.join(sorted(groups[t]))
        print(f'phase {phase}: cut of the phase {phase_cut:2}, side {side}')
        best = min(best, (phase_cut, side))
        for x, c in w.pop(t).items():          # fusionne t dans s
            if x != s:
                w[s][x] = w[s].get(x, 0) + c
                w[x][s] = w[x].get(s, 0) + c
            del w[x][t]
        groups[s] += groups.pop(t)
    return best

print(stoer_wagner(LINKS))
# phase 1: cut of the phase  8, side H
# phase 2: cut of the phase 10, side F
# phase 3: cut of the phase 10, side FGH
# phase 4: cut of the phase  4, side EFGH
# phase 5: cut of the phase 10, side C
# phase 6: cut of the phase 10, side B
# phase 7: cut of the phase  9, side BCDEFGH
# (4, 'EFGH')
Un tableau des sept phases de l'algorithme de Stoer-Wagner sur l'exemple, avec une barre pour chaque coupe de la phase. La phase 1 isole H avec un poids de 8, la phase 2 F avec 10, la phase 3 le groupe fusionné F, G, H avec 10, la phase 4 le groupe E, F, G, H avec 4, mis en évidence comme minimum, la phase 5 C avec 10, la phase 6 B avec 10, et la phase 7 tout sauf A avec 9.
Chaque phase se termine par une coupe candidate et une fusion. La quatrième phase trouve la coupe des bâtiments, de poids 4, et aucune phase ultérieure ne fait mieux.

Sur l'exemple, la première phase se termine en H avec une coupe de 8, le coût de son isolement. À mesure que les phases fusionnent des sommets, les groupes grossissent : à la phase 4, le dernier sommet ajouté est le groupe fusionné E, F, G, H, et sa coupe de la phase est la coupe des bâtiments, 4. Les phases restantes ne trouvent que des coupes plus grandes. Avec un tas de Fibonacci pour le choix « le plus fortement relié », chaque phase prend O(E + V log V) et l'algorithme complet O(V E + V2 log V); la version simple ci-dessus, qui cherche le maximum par un balayage, prend O(V3).

7. La contraction aléatoire de Karger

L'algorithme de David Karger, de 1993, est l'un des algorithmes randomisés les plus célèbres. Il est presque trop simple pour fonctionner :

  1. Choisissez une liaison au hasard, avec une probabilité proportionnelle à son poids, et contractez-la : fusionnez ses deux extrémités en un seul sommet, en supprimant les liaisons qui les relient.
  2. Répétez jusqu'à ce qu'il ne reste que deux sommets fusionnés.
  3. Les liaisons entre ces deux sommets forment une coupe. Renvoyez-la.

Une exécution peut facilement échouer : si elle contracte à un moment une liaison de la coupe minimale, cette coupe est perdue. La surprise, c'est qu'elle échoue assez rarement. Une coupe minimale est légère, donc les choix aléatoires tendent à éviter ses liaisons, et Karger a prouvé qu'une seule exécution renvoie une coupe minimale donnée avec une probabilité d'au moins 2 / (n (n - 1)). Répéter l'exécution et garder la coupe la plus légère fait baisser la probabilité d'échec aussi vite que vous le souhaitez.

import math, random

LINKS = [('A', 'B', 4), ('A', 'C', 3), ('A', 'D', 2), ('B', 'C', 2), ('B', 'D', 3), ('C', 'D', 3),
         ('E', 'F', 3), ('E', 'G', 2), ('E', 'H', 3), ('F', 'G', 4), ('F', 'H', 2), ('G', 'H', 3),
         ('C', 'E', 2), ('D', 'F', 1), ('B', 'G', 1)]

def contract(links, rnd):
    """Une exécution de l'algorithme de Karger : fusionne les extrémités de liaisons aléatoires jusqu'à ce qu'il reste deux groupes."""
    group = {x: x for u, v, _ in links for x in (u, v)}
    def find(x):
        while group[x] != x:
            x = group[x]
        return x
    groups = len(group)
    while groups > 2:
        alive = [(u, v, w) for u, v, w in links if find(u) != find(v)]
        u, v, _ = rnd.choices(alive, weights=[w for *_, w in alive])[0]   # les liaisons plus lourdes fusionnent plus souvent
        group[find(u)] = find(v)
        groups -= 1
    side = ''.join(sorted(x for x in group if find(x) == find('A')))
    return sum(w for u, v, w in links if find(u) != find(v)), side

rnd = random.Random(2026)
runs = [contract(LINKS, rnd) for _ in range(10_000)]
hits = sum(weight == 4 for weight, _ in runs)
print(f'minimum cut found in {hits} of 10,000 runs')
print(f'guaranteed at least 2 / (n (n - 1)) = {2 / (8 * 7):.4f} per run')
best_of_10 = [min(runs[i:i + 10]) for i in range(0, 10_000, 10)]
print(f'best of 10 runs is the minimum cut {sum(c[0] == 4 for c in best_of_10)} times out of 1,000')
print(f'runs needed for 99% with only the guarantee: {math.ceil(math.log(100) / -math.log(1 - 2 / 56))}')
# minimum cut found in 3228 of 10,000 runs
# guaranteed at least 2 / (n (n - 1)) = 0.0357 per run
# best of 10 runs is the minimum cut 978 times out of 1,000
# runs needed for 99% with only the guarantee: 127
À gauche : la fin d'une exécution réussie de la contraction aléatoire. Les routeurs ont été fusionnés en deux groupes, A, B, C, D et E, F, G, H, reliés par les trois liaisons restantes, de poids 2, 1 et 1. À droite : un diagramme en barres des taux de réussite sur l'exemple. Une exécution trouve la coupe minimale dans 32,3 % des cas, contre un minimum garanti de 3,6 % ; la meilleure de 10 exécutions la trouve dans 97,8 % des cas.
Une exécution réussit lorsqu'elle ne contracte jamais l'une des trois liaisons légères. Sur ce graphe, cela arrive dans environ un tiers des exécutions, bien au-dessus de la garantie de 1 sur 28.

Sur 10 000 exécutions sur l'exemple, la coupe minimale est sortie 3 228 fois, soit environ 32 %, neuf fois les 3,6 % garantis. Garder la meilleure de 10 exécutions l'a trouvée dans 978 essais sur 1 000. La garantie est un pire cas : en ne comptant que sur elle, il faudrait 127 exécutions pour une confiance de 99 % sur 8 sommets. La borne de Karger a aussi une conséquence frappante. Chaque coupe minimale est trouvée avec une probabilité d'au moins 2 / (n (n - 1)), et ces événements ne peuvent pas se produire en même temps, donc un graphe peut avoir au plus n (n - 1) / 2 coupes minimales. Un cycle à n sommets en a exactement autant : supprimer deux quelconques de ses arêtes donne une coupe minimale.

David Karger et Clifford Stein ont amélioré la méthode en 1996 en ne contractant qu'une partie du chemin, puis en poursuivant récursivement sur deux copies, ce qui donne un algorithme en O(n2 log3 n) qui trouve une coupe minimale avec forte probabilité. Karger a ensuite donné un algorithme qui s'exécute en temps quasi linéaire en le nombre d'arêtes.

8. Quel algorithme utiliser

MéthodeTrouveTempsRemarques
Essayer toutes les coupesGlobale ou s-tO(2n E)Seulement pour de tout petits graphes ou pour vérifier un autre code
Un seul flot maximalCoupe s-t minimaleCelui de l'algorithme de flot, O(V E2) pour Edmonds-KarpFonctionne pour les graphes orientés ; la coupe se lit sur le graphe résiduel
n - 1 flots maximauxGlobale, et toutes les paires avec Gomory-Hun - 1 fois un flotL'arbre de Gomory-Hu répond d'un coup à la question pour chaque paire
Stoer-WagnerGlobaleO(V E + V2 log V)Déterministe, sans flots ; graphes non orientés, poids positifs ou nuls
Karger, répétéGlobale, avec forte probabilitéO(n2) par exécution avec une implémentation simpleFacile à coder et à paralléliser ; peut aussi énumérer toutes les coupes minimales
Karger-SteinGlobale, avec forte probabilitéO(n2 log3 n)Contraction récursive

En pratique : pour la coupe entre deux sommets donnés, exécutez un flot maximal. Pour la coupe minimale globale d'un graphe non orienté, Stoer-Wagner est le choix déterministe standard, et c'est ce qu'implémentent des bibliothèques comme NetworkX. L'algorithme de Karger est celui vers lequel se tourner lorsque le graphe est grand, lorsque le calcul peut être réparti sur de nombreuses machines, ou lorsqu'il vous faut toutes les coupes minimales plutôt qu'une seule.

9. Graphes orientés et coupes de sommets

Dans un graphe orienté, une coupe (S, V \ S) ne compte que les arcs qui vont de S vers l'autre côté ; les arcs qui reviennent n'aident en rien à la déconnexion. Stoer-Wagner et l'algorithme de Karger reposent sur la symétrie et ne s'appliquent pas. La coupe minimale globale peut tout de même être trouvée avec des flots maximaux, d'un sommet fixé vers chacun des autres et en retour, et Jianxiu Hao et James Orlin ont montré en 1994 comment le faire en à peu près le temps d'un seul calcul de flot.

Parfois, la question est de savoir quels sommets supprimer plutôt que quelles arêtes : les routeurs dont la panne couperait le réseau en deux. Le plus petit ensemble de ce type donne la sommet-connexité, et une astuce classique la ramène aux coupes d'arêtes, en dédoublant chaque sommet en une copie « entrée » et une copie « sortie » reliées par une arête de capacité 1. Un sommet seul dont la suppression déconnecte le graphe est un point d'articulation, et une arête seule qui le fait est un pont. Les deux peuvent être trouvés en temps linéaire sans flots, comme le guide sur les composantes connexes, ponts et points d'articulation l'explique.

10. Où servent les coupes minimales

Le problème opposé, la coupe maximale, qui demande la partition avec le plus possible d'arêtes traversantes, semble similaire mais est NP-difficile. Minimiser une coupe est facile parce que c'est le dual d'un flot ; la maximiser n'offre aucune structure de ce genre.

11. Progrès récents

Les coupes minimales ont connu certaines des avancées récentes les plus frappantes de l'algorithmique. En 2015, Ken-ichi Kawarabayashi et Mikkel Thorup ont trouvé un algorithme déterministe pour la coupe minimale globale d'un graphe simple non pondéré qui s'exécute en temps quasi linéaire. En 2021, Jason Li a étendu le temps presque linéaire déterministe aux graphes pondérés. Et en 2022, Li Chen, Rasmus Kyng, Yang P. Liu, Richard Peng, Maximilian Probst Gutenberg et Sushant Sachdeva ont montré que le flot maximal avec des capacités entières, et avec lui la coupe s-t minimale, peut être calculé en temps presque linéaire m1 + o(1), un résultat qui a reçu le prix du meilleur article à FOCS 2022. Ces algorithmes ne sont pas encore ceux qu'utilisent les logiciels pratiques, mais ils établissent à quelle vitesse ces problèmes peuvent être résolus en principe.

12. Erreurs fréquentes

ErreurPourquoi c'est fauxCorrection
Confondre coupe s-t minimale et coupe minimale globaleDe A à B la coupe minimale vaut 9, mais la globale vaut 4Demandez-vous si deux sommets doivent être séparés
Prendre le sommet le plus léger comme réponseLe degré minimum n'est qu'une borne supérieure : 8 en H contre un vrai minimum de 4Utilisez Stoer-Wagner ou Karger
Lire la coupe uniquement sur les arêtes saturéesDes arêtes saturées peuvent se trouver à l'intérieur d'un même côté ; toute arête saturée n'est pas dans la coupePrenez les sommets accessibles depuis s dans le graphe résiduel, puis les arêtes qui en sortent
Exécuter Stoer-Wagner sur un graphe orientéL'algorithme suppose que chaque liaison compte de la même façon dans les deux sensUtilisez des flots maximaux, ou Hao-Orlin
Se fier à une seule exécution de KargerUne exécution peut ne réussir qu'avec une probabilité aussi faible que 2 / (n (n - 1))Répétez-la et gardez la coupe la plus légère
Choisir les liaisons uniformément dans un graphe pondéréLa garantie de Karger exige une probabilité proportionnelle au poidsTirez selon le poids, ou traitez les poids comme des liaisons parallèles
Utiliser de simples coupes minimales pour le clusteringElles tendent à détacher quelques sommets faiblement rattachésUtilisez un critère équilibré comme la coupe normalisée
S'attendre à ce que la coupe maximale soit tout aussi facileLa coupe maximale est NP-difficileUtilisez une approximation ou des heuristiques pour la coupe maximale

13. Glossaire

TermeSignification
CoupeUne partition des sommets en deux groupes non vides, ou l'ensemble des arêtes qui la traversent
Poids de la coupe (capacité)Le poids total des arêtes qui traversent une coupe
Coupe s-t minimaleLa coupe la plus légère avec s d'un côté et t de l'autre
Coupe minimale globaleLa plus légère de toutes les coupes, sans sommets imposés
Arête-connexitéLe plus petit nombre d'arêtes dont la suppression déconnecte un graphe non pondéré
Sommet-connexitéLe plus petit nombre de sommets dont la suppression déconnecte le graphe
Graphe résiduelLes liaisons qui ont encore de la capacité disponible après l'envoi d'un flot
Théorème flot maximal / coupe minimaleLe plus grand flot s-t est égal au poids de la plus petite coupe s-t
Arbre de Gomory-HuUn arbre qui enregistre la coupe minimale entre chaque paire de sommets
Coupe de la phaseDans Stoer-Wagner, la coupe qui isole le dernier sommet ajouté au cours d'une phase
ContractionLa fusion des deux extrémités d'une arête en un seul sommet
Coupe normaliséeUn poids de coupe divisé par les tailles des deux côtés, utilisé pour le clustering

14. Questions fréquentes

Qu'est-ce qu'une coupe minimale dans un graphe ?

Une coupe partage les sommets d'un graphe en deux groupes non vides, et son poids est le poids total des arêtes qui passent de l'un à l'autre. Une coupe minimale est une coupe de poids minimal : l'ensemble d'arêtes le moins coûteux dont la suppression déconnecte le graphe en deux morceaux. Dans un graphe non pondéré, sa taille est l'arête-connexité du graphe.

Quelle est la différence entre une coupe s-t minimale et une coupe minimale globale ?

Une coupe s-t minimale doit séparer deux sommets donnés, une source s et un puits t, et se calcule avec un flot maximal. Une coupe minimale globale peut séparer deux groupes de sommets quelconques et est la plus petite de toutes les coupes s-t minimales. Les deux peuvent beaucoup différer : deux sommets d'un même groupe bien connecté peuvent exiger une coupe bien plus lourde que le point le plus faible du graphe.

Comment trouver la coupe minimale à partir d'un flot maximal ?

Calculez un flot maximal de s à t. Trouvez ensuite tous les sommets que s peut encore atteindre par des arêtes ayant de la capacité disponible dans le graphe résiduel. Ces sommets forment un côté d'une coupe s-t minimale, les arêtes saturées qui en sortent forment la coupe, et d'après le théorème flot maximal / coupe minimale, son poids est égal à la valeur du flot.

Comment fonctionne l'algorithme de Stoer-Wagner ?

Stoer-Wagner trouve la coupe minimale globale d'un graphe non orienté à poids positifs ou nuls en n - 1 phases. Chaque phase ajoute les sommets un par un, en prenant toujours celui qui est le plus fortement relié à ceux déjà ajoutés ; le poids des liaisons du dernier sommet est une coupe candidate, puis les deux derniers sommets sont fusionnés. La plus petite candidate est la coupe minimale. Il s'exécute en temps O(VE + V au carré log V) avec un tas de Fibonacci.

Comment fonctionne l'algorithme de Karger ?

L'algorithme de Karger choisit à plusieurs reprises une arête au hasard, avec une probabilité proportionnelle à son poids, et fusionne ses deux extrémités, jusqu'à ce qu'il ne reste que deux sommets fusionnés ; les arêtes qui les relient forment une coupe. Une exécution trouve une coupe minimale donnée avec une probabilité d'au moins 2 / (n (n - 1)), donc l'algorithme est répété de nombreuses fois et la coupe la plus légère est conservée. Il en découle qu'un graphe a au plus n (n - 1) / 2 coupes minimales.

Le problème de la coupe minimale est-il NP-difficile ?

Non. La coupe s-t minimale comme la coupe minimale globale peuvent être trouvées en temps polynomial, avec des flots maximaux, l'algorithme de Stoer-Wagner ou l'algorithme randomisé de Karger, et des algorithmes récents s'exécutent en temps presque linéaire. Le problème apparenté de la coupe maximale, qui demande la partition avec le plus d'arêtes traversantes, est NP-difficile.

15. Références

Les théorèmes, algorithmes et attributions ci-dessus proviennent des sources suivantes, classées par ordre chronologique.

  1. Menger, K. (1927). "Zur allgemeinen Kurventheorie." Fundamenta Mathematicae 10, 96 à 115.
  2. Whitney, H. (1932). "Congruent Graphs and the Connectivity of Graphs." American Journal of Mathematics 54(1), 150 à 168.
  3. Harris, T. E. et Ross, F. S. (1955). Fundamentals of a Method for Evaluating Rail Net Capacities. Research Memorandum RM-1573. Santa Monica : RAND Corporation.
  4. Elias, P., Feinstein, A. et Shannon, C. E. (1956). "A Note on the Maximum Flow Through a Network." IRE Transactions on Information Theory 2(4), 117 à 119.
  5. Ford, L. R. et Fulkerson, D. R. (1956). "Maximal Flow Through a Network." Canadian Journal of Mathematics 8, 399 à 404.
  6. Gomory, R. E. et Hu, T. C. (1961). "Multi-Terminal Network Flows." Journal of the Society for Industrial and Applied Mathematics 9(4), 551 à 570.
  7. Schwartz, B. L. (1966). "Possible Winners in Partially Completed Tournaments." SIAM Review 8(3), 302 à 308.
  8. Edmonds, J. et Karp, R. M. (1972). "Theoretical Improvements in Algorithmic Efficiency for Network Flow Problems." Journal of the ACM 19(2), 248 à 264.
  9. Greig, D. M., Porteous, B. T. et Seheult, A. H. (1989). "Exact Maximum A Posteriori Estimation for Binary Images." Journal of the Royal Statistical Society, Series B 51(2), 271 à 279.
  10. Nagamochi, H. et Ibaraki, T. (1992). "Computing Edge-Connectivity in Multigraphs and Capacitated Graphs." SIAM Journal on Discrete Mathematics 5(1), 54 à 66.
  11. Karger, D. R. (1993). "Global Min-cuts in RNC, and Other Ramifications of a Simple Min-cut Algorithm." Dans Proceedings of the 4th ACM-SIAM Symposium on Discrete Algorithms, 21 à 30.
  12. Wu, Z. et Leahy, R. (1993). "An Optimal Graph Theoretic Approach to Data Clustering: Theory and Its Application to Image Segmentation." IEEE Transactions on Pattern Analysis and Machine Intelligence 15(11), 1101 à 1113.
  13. Hao, J. et Orlin, J. B. (1994). "A Faster Algorithm for Finding the Minimum Cut in a Directed Graph." Journal of Algorithms 17(3), 424 à 446.
  14. Karger, D. R. et Stein, C. (1996). "A New Approach to the Minimum Cut Problem." Journal of the ACM 43(4), 601 à 640.
  15. Stoer, M. et Wagner, F. (1997). "A Simple Min-Cut Algorithm." Journal of the ACM 44(4), 585 à 591.
  16. Karger, D. R. (2000). "Minimum Cuts in Near-Linear Time." Journal of the ACM 47(1), 46 à 76.
  17. Shi, J. et Malik, J. (2000). "Normalized Cuts and Image Segmentation." IEEE Transactions on Pattern Analysis and Machine Intelligence 22(8), 888 à 905.
  18. Boykov, Y. et Jolly, M.-P. (2001). "Interactive Graph Cuts for Optimal Boundary and Region Segmentation of Objects in N-D Images." Dans Proceedings of the 8th IEEE International Conference on Computer Vision, 105 à 112.
  19. Schrijver, A. (2002). "On the History of the Transportation and Maximum Flow Problems." Mathematical Programming 91(3), 437 à 445.
  20. Kawarabayashi, K. et Thorup, M. (2015). "Deterministic Global Minimum Cut of a Simple Graph in Near-Linear Time." Dans Proceedings of the 47th ACM Symposium on Theory of Computing, 665 à 674.
  21. Li, J. (2021). "Deterministic Mincut in Almost-Linear Time." Dans Proceedings of the 53rd ACM Symposium on Theory of Computing, 384 à 395.
  22. Chen, L., Kyng, R., Liu, Y. P., Peng, R., Probst Gutenberg, M. et Sachdeva, S. (2022). "Maximum Flow and Minimum-Cost Flow in Almost-Linear Time." Dans Proceedings of the 63rd IEEE Symposium on Foundations of Computer Science, 612 à 623.

Trouvez la coupe vous-même

Construisez le réseau à deux bâtiments dans le calculateur de coupe minimale, choisissez A comme source et H comme puits, et regardez le flot remplir les trois liaisons entre les bâtiments jusqu'à ce que la coupe apparaisse.

Ouvrir le calculateur

Trouvez le goulot d'étranglement

Envoyez du flot à travers votre propre réseau, pas à pas, et regardez les chemins augmentants remplir les liaisons jusqu'à ce que le goulot d'étranglement, la coupe minimale, apparaisse.

Lancer le visualiseur de flot maximal