Calculer la largeur d'arbre

14

La largeur d' arbre d'un graphe non orienté est un concept très important en théorie des graphes. Des tonnes d'algorithmes de graphe ont été inventés qui fonctionnent rapidement si vous avez une décomposition du graphe avec une petite largeur d'arbre.

La largeur d'arbre est souvent définie en termes de décompositions d'arbre. Voici un graphique et une décomposition arborescente de ce graphique, gracieuseté de Wikipedia:

entrez la description de l'image ici

Une décomposition d'arbre est un arbre où chaque sommet est associé à un sous-ensemble des sommets du graphe d'origine, avec les propriétés suivantes:

  • Chaque sommet du graphique d'origine se trouve dans au moins l'un des sous-ensembles.
  • Chaque arête du graphique d'origine possède ses deux sommets dans au moins l'un des sous-ensembles.
  • Tous les sommets de la décomposition dont les sous-ensembles contiennent un sommet d'origine donné sont connectés.

Vous pouvez vérifier que la décomposition ci-dessus respecte ces règles. La largeur d'une décomposition d'arbre est la taille de son plus grand sous-ensemble, moins un. Par conséquent, c'est deux pour la décomposition ci-dessus. La largeur d'arbre d'un graphique est la plus petite largeur de toute décomposition d'arbre de ce graphique.


Dans ce défi, vous recevrez un graphique connecté et non orienté, et vous devez trouver sa largeur d'arbre.

Bien qu'il soit difficile de trouver des décompositions d'arbre, il existe d'autres façons de calculer la largeur d'arbre. La page Wikipedia contient plus d'informations, mais une méthode de calcul de la largeur d'arbre qui n'y est pas mentionnée et qui est souvent utilisée dans les algorithmes pour calculer la largeur d'arbre est la largeur de classement d'élimination minimale. Voir ici pour un article utilisant ce fait.

Dans un ordre d'élimination, on élimine tous les sommets d'un graphe un par un. Lorsque chaque sommet est éliminé, des arêtes sont ajoutées reliant tous les voisins de ce sommet les uns aux autres. Ceci est répété jusqu'à ce que tous les sommets aient disparu. La largeur d'ordre d'élimination est le plus grand nombre de voisins que tout sommet qui est éliminé a au cours de ce processus. La largeur d'arbre est égale au minimum sur tous les ordres de la largeur d'ordre d'élimination. Voici un exemple de programme utilisant ce fait pour calculer la largeur d'arbre:

import itertools
def elimination_width(graph):
    max_neighbors = 0
    for i in sorted(set(itertools.chain.from_iterable(graph))):
        neighbors = set([a for (a, b) in graph if b == i] + [b for (a, b) in graph if a == i])
        max_neighbors = max(len(neighbors), max_neighbors)
        graph = [edge for edge in graph if i not in edge] + [(a, b) for a in neighbors for b in neighbors if a < b]
    return max_neighbors

def treewidth(graph):
    vertices = list(set(itertools.chain.from_iterable(graph)))
    min_width = len(vertices)
    for permutation in itertools.permutations(vertices):
        new_graph = [(permutation[vertices.index(a)], permutation[vertices.index(b)]) for (a, b) in graph]
        min_width = min(elimination_width(new_graph), min_width)
    return min_width

if __name__ == '__main__':
    graph = [('a', 'b'), ('a', 'c'), ('b', 'c'), ('b', 'e'), ('b', 'f'), ('b', 'g'),
            ('c', 'd'), ('c', 'e'), ('d', 'e'), ('e', 'g'), ('e', 'h'), ('f', 'g'), ('g', 'h')]
    print(treewidth(graph))

Exemples:

[(0, 1), (0, 2), (0, 3), (2, 4), (3, 5)]
1

[(0, 1), (0, 2), (1, 2), (1, 4), (1, 5), (1, 6), (2, 3), (2, 4), (3, 4), (4, 6), (4, 7), (5, 6), (6, 7)]
2

[(0, 1), (0, 3), (1, 2), (1, 4), (2, 5), (3, 4), (3, 6), (4, 5), (4, 7), (5, 8), (6, 7), (7, 8)]
3

[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
4

Vous recevrez le graphique en entrée et vous devrez renvoyer la largeur d'arbre en sortie. Le format d'entrée est flexible. Vous pouvez prendre une liste d'arêtes, une carte d'adjacence ou une matrice d'adjacence en entrée. Si vous souhaitez utiliser un autre format d'entrée, demandez dans les commentaires. Vous pouvez supposer que l'entrée est connectée, et vous pouvez construire cette hypothèse dans votre format d'entrée, par exemple en utilisant une liste de bords.

EDIT: les opérations intégrées qui calculent la largeur d'arbre ne sont pas autorisées. Je m'excuse de ne pas l'avoir précisé à l'avance.

Le code le plus court gagne.

isaacg
la source
Puisqu'un graphique est formellement un tuple, est (V,E)-ce que ce serait une entrée valide?
ბიმო
@Bruce_Forte Absolument.
isaacg

Réponses:

7

Octave, 195 octets

function x=F(a)r=rows(a);P=perms(s=1:r);x=r;for m=s;b=a;n=0;for z=P(m,:);(T=sum(b)(z))&&{b|=(k=accumarray(nchoosek(find(b(z,:)),2),1,[r r]))|k';n=max(T,n);b(z,:)=0;b(:,z)=0}{4};end;x=min(x,n);end

Une fonction qui prend en entrée une matrice d'adjacence. Il consomme beaucoup de mémoire, il est donc inutile si le nombre de sommets est supérieur à 10-12.

  • il n'est pas nécessaire, endfunctionmais il doit être ajouté à tio.

Essayez-le en ligne!

Non golfé:

function min_width = treewidth(graph_adj)
    Nvertices = rows(graph_adj)
    Permutations = perms(1:Nvertices);                                                            % do not try it for large number of vertices
    min_width = Nvertices;
    for v = 1:Nvertices;
        new_graph=graph_adj;
        max_neighbors=0;
        for p = Permutations(v,:)
            Nneighbors=sum(new_graph)(p);
            if(Nneighbors)>0
                connection=accumarray(nchoosek(find(new_graph(p,:)),2),1,[Nvertices Nvertices]);  % connect all neighbors
                new_graph|=connection|connection';                                                % make the adjacency matrix symmetric
                new_graph(p,:)=0;new_graph(:,p)=0;                                                % eliminate the vertex
                max_neighbors=max(Nneighbors,max_neighbors);
            end
        end
        min_width=min(min_width,max_neighbors);
    end
end
rahnema1
la source
5

SageMath, 29 octets non compétitifs *

lambda L:Graph(L).treewidth()

* Cette réponse a été publiée avant le changement par OP de la question "Les buildins sont interdits", donc je l'ai rendue non compétitive.

Démo en ligne!

rahnema1
la source
1
Petit. C'est sans intérêt. Malheureusement, je vais devoir interdire les buildins, désolé.
isaacg
@isaacg Aucun problème. J'ai une autre chose dans la main :)
rahnema1
@isaacg cette réponse ne viole-t-elle pas une faille standard?
PyRulez
rahnema1, voir ma modification. Les buildins sont interdits, cette réponse n'est donc pas autorisée. Veuillez le supprimer ou le marquer comme non compétitif
isaacg
@isaacg Merci, je l'ai marqué comme non compétitif.
rahnema1
5

Haskell (Lambdabot), 329 321 245 octets

Voici ma solution, grâce à la flexibilité de l'entrée, elle fonctionne sur les graphiques avec des graphiques contenant n'importe quel type qui est une instance de Eq.

(&)=elem
l=length
t n g s=last$minimum[max(t n g b)$t(n++b)g$s\\b|b<-filterM(\_->[0>1,1>0])s,l b==div(l s)2]:[l[d|d<-fst g,not$d&n,d/=s!!0,(d&)$foldr(\x y->last$y:[x++y|any(&y)x])[s!!0]$join(>>)[e|e<-snd g,all(&(s!!0:d:n))e]]|1==l s]
w=t[]<*>fst

Essayez-le en ligne!

Version non golfée:

type Vertex a = a
type Edge a   = [Vertex a]
type Graph a  = ([Vertex a],[Edge a])

vertices = fst
edges = snd

-- This corresponds to the function w
treeWidth :: (Eq a) => Graph a -> Int
treeWidth g = recTreeWidth g [] (vertices g)

-- This is the base case (length s == 1) of t
recTreeWidth graph left [v] =
    length [ w | w <- vertices graph
               , w `notElem` left
               , w /= v
               , w `elem` reachable (subGraph w)
           ]

  where subGraph w = [ e | e <- edges graph, all (`elem` v:w:left) e ]

        reachable g = foldr accumulateReachable [v] (g>>g)
        accumulateReachable x y = if any (`elem` y) x
                                  then x++y
                                  else y

-- This is the other case of t
recTreeWidth graph left sub =
  minimum [ comp sub' | sub' <- filterM (const [False,True]) sub
                      , length sub' == div (length sub) 2
          ]

  where comp b = max (recTreeWidth graph left b)
                     (recTreeWidth graph (left++b) (sub\\b))
ბიმო
la source