web-dev-qa-db-fra.com

Comment trouver un triangle dans un graphique?

Voici un exercice dans Manuel de conception d’algorithmes .

Considérons le problème de déterminer si un graphe non dirigé donné G = (V, E) contient un triangle ou un cycle de longueur 3.

(a) Donnez un O (| V | ^ 3) pour trouver un triangle s’il en existe un.

(b) Améliorez votre algorithme pour qu'il soit exécuté dans le temps O (| V | · | E |). Vous pouvez assumer | V | ≤ | E |.

Observez que ces limites vous donnent le temps de convertir entre les représentations de la matrice d'adjacence et de la liste d'adjacence de G.

Voici mes pensées:

(a) Si le graphe est donné comme une liste d'adjacence, je peux convertir la liste en matrice par O (| V | ^ 2). alors je fais:

for (int i = 0;i < n;i++) 
   for (int j = i+1;j < n;j++) 
   if (matrix[i][j] == 1) 
      for (int k = j+1;k < n;k++) 
         if (matrix[i][k] == 1 && matrix[j][k] == 1)
             return true;

Cela devrait donner un O (| V | ^ 3) pour tester le triangle.

(b) Ma première hypothèse est que si le graphe est donné sous forme de liste de contiguïté, je ferai un bfs. Chaque fois qu'un bord croisé est trouvé, par exemple, if y-x is a cross Edge, alors je vais check whether parent[y] == parent[x], if true, then a triangle is found.

Quelqu'un pourrait-il me dire si ma pensée est correcte ou non?

Aussi pour cela (b), je ne suis pas sûr de sa complexité. Devrait-il être O (| V | + | E |), non?

Comment puis-je le faire dans O (| V | * | E |)?

22
Jackson Tale

Une solution possible O(|V||E|), est la même idée de la force brute dans (a):

for each Edge (u, v):
  for each vertex w:
     if (v, w) is an Edge and (w, u) is an Edge:
          return true
return false

Cochez toutes les arêtes , et pas toutes les paires de sommets - avec un autre sommet formant un triangle - ces informations sont suffisantes pour déterminer si l'arête et le sommet constituent une solution réalisable.

Contre-exemple à la solution BFS:

       A
     / | \
    /  |  \
   B   C   D
   |   |   |
   |   |   |
   F---G---H
   |       |
   ---------
    (F, H) is also an Edge

Notez que father[F] != father[G] != father[H], ainsi l’algorithme renverra false - mais néanmoins, (F, G, H) est une solution réalisable!

28
amit

Si vous avez une matrice d'adjacence, vous pouvez trouver des triangles en quadrillant la matrice et en vérifiant si la matrice d'origine et la matrice carrée ont une entrée non nulle au même endroit.

Une multiplication de matrice naïve prend du temps O(n^3), mais il existe des algorithmes de multiplication de matrice rapides qui donnent de meilleurs résultats. L’un des plus connus est l’algorithme Coppersmith-Winograd , qui s’exécute dans le temps O(n^2.4). Cela signifie que l'algorithme va quelque chose comme:

  • Utilisez O(V^2) time pour convertir en une matrice d'adjacence.
  • Utilisez O(V^2.4) time pour calculer le carré de la matrice d'adjacence.
  • Utilisez O(V^2) time pour vérifier sur les matrices les entrées coïncidentes non nulles.
  • L'index de la ligne et de la colonne où vous trouvez des entrées non nulles qui coïncident dans (le cas échéant) vous indique deux des nœuds impliqués.
  • Utilisez O(V) time pour limiter le troisième nœud commun aux deux nœuds connus.

Donc dans l’ensemble, cela prend O(V^2.4) time; plus précisément, cela prend aussi longtemps que la multiplication de matrice prend. Vous pouvez basculer de manière dynamique entre cet algorithme et l'algorithme if-any-Edge-end-points-have-a-commun-voisin qu'amit explique dans leur réponse pour améliorer cela en O(V min(V^1.4, E)).

Voici un papier qui va plus en profondeur dans le problème .

C’est assez bien de voir à quel point ce problème est dépendant de découvertes théoriques. Si les conjectures sur la multiplication matricielle en réalité se révélaient vraies, alors vous auriez une limite temporelle vraiment agréable de O(V^2) ou O(V^2 log(V)) ou quelque chose comme ça comme ça. Mais si les ordinateurs quantiques fonctionnent, nous pourrons faire même mieux que cela (quelque chose comme O(V^1.3))!

7
Craig Gidney

Voici ce que je pense:

La solution BFS d'origine est incorrecte, comme indiqué ci-dessus. Mais on peut modifier le DFS. Attribuez des numéros aux nœuds visités lors de la visite de chaque sommet du DFS. Maintenant, si nous atteignons un sommet (dans la question que j'ai vue croiser des arêtes, il n'y en a pas dans un graphe non dirigé), nous vérifions sa liste de contiguïté et supposons qu'un sommet est découvert (mais non traité, cela ne peut pas se produire), puis nous vérifions son nombre . Si la différence est 2, il existe un cycle de longueur 3.

1
Prashant

J'aime beaucoup la solution de multiplication de matrice discutée dans cet article de blog .

Soit a = la matrice d'adjacence

  • Les adjacences dans une matrice * a (a2) multipliées sont les nombres de chemins de 2 longueurs
  • Les adjacences dans a2 * une matrice multipliée sont les nombres de chemins de 3 longueurs

Le problème est que la multiplication de matrice est lente ... Cependant, vous pouvez utiliser GPGPU pour effectuer la multiplication de matrice et disposer d'une solution performante sur les architectures modernes comprenant un GPU. 

0
class