Nombre total de types topologiques

11

Pour un DAG donné (graphe acyclique dirigé), chacune de ses sortes topologiques est une permutation de tous les sommets, où pour chaque arête (u, v) dans le DAG, u apparaît avant v dans la permutation.

Votre tâche consiste à calculer le nombre total de types topologiques d'un DAG donné.

Règles

  • Vous pouvez utiliser n'importe quel format pour représenter le graphique, comme la matrice d'adjacence, la liste d'adjacence ou la liste de bords, tant que vous ne faites pas de calculs utiles dans votre codage. Vous pouvez également avoir des éléments comme le nombre de sommets ou la liste de sommets dans l'entrée, si ceux-ci sont utiles.
  • Vous pouvez supposer que le graphique dans l'entrée est toujours un DAG (n'a pas de cycle).
  • Votre programme devrait fonctionner en théorie pour toute entrée. Mais il peut échouer s'il déborde du type entier de base dans votre langue.
  • Les noms des sommets peuvent être des valeurs consécutives de n'importe quel type. Par exemple: des nombres commençant à 0 ou 1. (Et seulement si vous ne stockez pas de code dans ce numéro, bien sûr.)
  • C'est du code-golf. Le code le plus court gagne.

Exemple

Il s'agit de la même entrée dans différents formats. Votre programme ne doit pas tous les accepter. Les sommets sont toujours des entiers commençant à 0.

Adjacency list:
[ [1 2 3 5] [2 4] [] [2] [] [3] ]
Adjacency matrix:
[ [0 1 1 1 0 1] [0 0 1 0 1 0] [0 0 0 0 0 0] [0 0 1 0 0 0] [0 0 0 0 0 0] [0 0 0 1 0 0] ]
Edge list:
6 [ [0 1] [0 2] [0 3] [0 5] [1 2] [1 4] [3 2] [5 3] ]

C'est le graphique montré dans cette image:

Exemple de graphique

La sortie doit être:

9

Les types topologiques sont:

[0 1 4 5 3 2]
[0 1 5 4 3 2]
[0 1 5 3 4 2]
[0 1 5 3 2 4]
[0 5 1 4 3 2]
[0 5 1 3 4 2]
[0 5 1 3 2 4]
[0 5 3 1 4 2]
[0 5 3 1 2 4]
jimmy23013
la source
Une fonction? Programme entier? Soit?
isaacg
@isaacg Soit.
jimmy23013

Réponses:

4

CJam - 25

q~{_f{1$-_j@j@&!*}_!+:+}j

Avec la grande aide de user23013 :)

Essayez-le en ligne

Explication:

L'algorithme général est le même que dans la solution Python de xnor .
La clé ici est l' jopérateur, qui effectue la récursion mémorisée. Il prend un paramètre, une valeur ou un tableau pour la ou les valeurs initiales (comme dans f (0), f (1), etc.) et un bloc pour définir la récursivité. L' jopérateur est à nouveau utilisé à l'intérieur du bloc pour effectuer des appels récursifs (et mémorisés) vers le même bloc. Il peut également être utilisé avec plusieurs paramètres, mais ce n'est pas le cas ici.
La grande innovation de user23013 est d'utiliser j avec différents types de données, en utilisant la liste d'adjacence comme tableau de valeurs initiales.

q~             read and evaluate the input (vertex list followed by adjacency list)
{…}j           run the block on the vertex list, doing memoized recursion
                and using the adjacency list for initial values
    _          copy the vertex list
    f{…}       for each vertex and the vertex list
        1$-    copy the vertex and remove it from the list
                Python: "V-{v}"
        _j     copy the reduced list and call the j block recursively
                this solves the problem for the reduced vertex list
                Python: "f(G,V-{v})"
        @j     bring the vertex to the top of the stack and call the j block recursively
                in this case, it's called with a vertex rather than a list
                and the memoized value is instantly found in the list of initial values
                effectively, this gets the list of vertices adjacent to the current vertex
                Python: "G[v]"
        @&     bring the reduced list to the top of the stack and intersect
        !*     multiply the number of topological sorts of the reduced vertex list
                with 1 if the intersection was empty and 0 if not
                Python: equivalent to "*(V-G[v]==V)"
               after this loop we get an array of sub-results for the reduced vertex lists
    _!+        add 1 or 0 to the array if the array was empty or not
                because we want to get 1 for the empty array
                Python: equivalent to "V<{0}or"
    :+         add the numbers in the array
                Python: "sum(…)"
aditsu quitte parce que SE est MAL
la source
1
Modifié pour autoriser explicitement la liste des sommets dans l'entrée. Maintenant 25 octets .
jimmy23013
@ user23013 De quel genre de sorcellerie s'agit-il? : o
aditsu quitte car SE est EVIL le
7

Python, 58

f=lambda G,V:V<{0}or sum(f(G,V-{v})*(V-G[v]==V)for v in V)

L'entrée se compose d'un dictionnaire d'adjacence Get d'un ensemble de sommets V.

G = {0:{1,2,3,5}, 1:{2,4}, 2:set(), 3:{2}, 4:set(), 5:{3}, 6:set()}
V = {0,1,2,3,4,5}

Le code est récursif. L'ensemble Vstocke tous les nœuds qui doivent encore être visités. Pour chaque prochain nœud potentiel, nous vérifions sa pertinence en voyant si aucun sommet ne pointe vers lui, en V-G[v]==Vvérifiant cela Vet s'ils G[v]sont disjoints. Pour tous ces sommets appropriés, nous ajoutons le nombre de tris topologiques avec celui-ci supprimé. Comme cas de base, l'ensemble vide donne 1.

xnor
la source
+1 pour ne pas utiliser la liste des bords.
jimmy23013
5

Mathematica, 80 57 51 octets

Count[Permutations@#,l_/;l~Subsets~{2}~SubsetQ~#2]&

Mise en œuvre très simple de la définition. Je ne fais que générer toutes les permutations et je compte combien d'entre elles sont valides. Pour vérifier si une permutation est valide, j'obtiens toutes les paires de sommets dans la permutation. Commodément, Subsets[l,{2}]non seulement me donne toutes les paires, mais il maintient également l'ordre dans llequel elles se trouvent - exactement ce dont j'ai besoin.

Ce qui précède est une fonction qui attend la liste des sommets et la liste des bords, comme

f[{1, 2, 3, 4, 5, 6}, {{1, 2}, {1, 3}, {1, 4}, {1, 6}, {2, 3}, {2, 5}, {4, 3}, {6, 4}}]

si vous appelez la fonction f.

J'essaierai de jouer au golf, ou j'utiliserai peut-être une autre approche plus tard.

Martin Ender
la source
2

Pyth, 27 octets

Mlf!sm}_dHfq2lYyTfqSZUZ^UGG

Définit une fonction d'entrée 2, g. La première entrée est le nombre de sommets, la seconde est la liste des arêtes dirigées.

Tester:

Code:
Mlf!sm}_dHfq2lYyTfqSZUZ^UGGghQeQ

Input:
6, [ [0, 1], [0, 2], [0, 3], [0, 5], [1, 2], [1, 4], [3, 2], [5, 3] ]

Essayez-le ici.

isaacg
la source
@ user23013 Le nombre et la liste Boht sont utilisés, dans l'expression ^UGG, qui génère toutes Gles listes d'entrées de range(len(G)).
isaacg
Je veux dire, sera-t-il plus court si vous l'utilisez [0, 1, ...]directement dans l'entrée?
jimmy23013
@ user23013 Non, ce serait la même longueur: par ^GlGrapport ^UGG.
isaacg
2

Haskell, 102 107 100 89 85 octets

import Data.List
(%)=elemIndex
n#l=sum[1|p<-permutations[0..n],and[u%p<v%p|[u,v]<-l]]

L'entrée est le nombre de sommets le plus élevé (commençant par 0) et une liste d'arêtes, où une arête est une liste à deux éléments. Exemple d'utilisation:5 # [[0,1], [0,2], [0,3], [0,5], [1,2], [1,4], [3,2], [5,3]]

Comment ça marche: compter toutes les permutations pdes sommets pour lesquelles toutes les arêtes [u,v]satisfont: la position de uin pest inférieure à la position de vin p. Il s'agit d'une mise en œuvre directe de la définition.

Edit: ma première version a renvoyé les types topologiques eux-mêmes et non le nombre. A corrigé.

Edit II: ne fonctionnait pas pour les graphiques avec des sommets non connectés. A corrigé.

nimi
la source
Je pense à ajouter un cas de test avec seulement des sommets mais pas des bords ...
jimmy23013
@ user23013: fonctionne maintenant pour les graphiques avec des sommets non connectés. Il est même devenu plus court.
nimi