0
votes

Détecter le cycle en graphique non dirigé dans OCAML

Quelqu'un a-t-il une idée de la façon de détecter s'il y a un cycle dans un graphe non orienté dans OCaml?

Voici le type que j'utilise pour le graphe:

let graph = { nodes = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'j';]; 
              edges = [('c', 'j', 9); ('d', 'e', 8); ('a', 'b', 8); ('b', 'c', 7); ('f', 'g', 6); ('b', 'h', 4); ('a', 'd', 4); ('g', 'h', 2); ('b', 'f', 2); ('e', 'g', 1)]}

Et par exemple, je voudrais vérifier si ce graphique contient des cycles:

type 'a graph = { nodes : 'a list; edges : ('a * 'a * int) list }


5 commentaires

Que se passe-t-il lorsque vous utilisez un algorithme de parcours de graphe sur un graphe avec cycle? Quelle expérience avez-vous essayée? Où êtes-vous resté coincé? Avez-vous un code de démarrage qui ne fonctionne pas? Il serait beaucoup plus utile que vous passiez un peu plus de temps à rédiger votre question.


J'implémente l'algorithme de Kruskal pour les arbres couvrant minimum et je suis obligé de détecter s'il y a un cycle dans les arêtes que j'ai déjà extrait. Donc, j'ai un graphique comme j'ai écrit dans l'exemple et je l'itère en prenant à chaque fois l'arête avec un poids minimal, mais je ne peux pas prendre une arête qui rend le cycle. Donc, maintenant je suis coincé avec ça parce que je ne sais pas comment détecter s'il y a un cycle.


Si vous gardez une trace de quels nœuds que vous avez vus jusqu'à présent, vous pouvez détecter que vous êtes dans un cycle juste en vérifiant si le nœud actuel est dans l'ensemble des nœuds à vue.


Je ne peux pas les suivre, ou du moins je ne sais pas comment, car je ne prends en compte que les arêtes. Donc, à mon avis, je devrais à chaque fois faire un algorithme (DST ou similaire) pour vérifier s'il existe un chemin qui mène au nœud initial, mais je ne sais pas comment je le ferais non plus. Je suis nouveau dans OCaml. Je lisais aussi que l'algorithme de Kruskal devrait contenir l'algorithme de recherche d'union, mais je ne sais pas comment l'implémenter. stackoverflow.com/questions/4290163/...


C'est une question déjà meilleure, vous devriez éditer votre question initiale pour ajouter ce genre d'informations minimales. Et oui, l'aide à l'union peut être utilisée pour détecter si deux sommets appartiennent aux mêmes composants connectés du graphe.


3 Réponses :



0
votes

Il est également possible d'éviter de visiter deux fois la même arête en la supprimant de la liste des arêtes disponibles; en supposant que l'ordre n'a pas d'importance entre les arêtes, vous pouvez supprimer une arête comme suit:

let visit2 graph =
  let g = ref graph in
  let rec v () = match (choose_edge !g) with
  | None -> ()
  | Some e -> begin g := graph_remove_edge !g e; v () end
  in v(); !g

La suppression d'une arête d'un graphe se fait ensuite en construisant un nouveau graphe qui partage les données avec le graphe précédent, mais avec une liste d'arêtes modifiée:

let choose_edge graph = match graph.edges with
  | [] -> None
  | e::_ -> Some e;;

let rec visit graph = match (choose_edge graph) with
  | None -> graph
  | Some e -> visit (graph_remove_edge graph e);;

# visit graph;;
- : char graph =
{nodes = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'j']; edges = []}

Vous pouvez ensuite transformer le graphe le long des appels récursifs de votre parcours de graphe; l'exemple ne fait rien d'intéressant ici, c'est juste pour montrer la structure:

let graph_remove_edge graph edge =
  { nodes = graph.nodes;
    edges = edges_remove_edge graph.edges edge }

Ou, vous gardez une trace du graphe courant avec une ref:

let edges_remove_edge edges edge =
  let (src, dst, _) = edge in
  let rec iter edges res = match edges with
    | [] -> res
    | ((s, d, _) as e)::edges ->
       if (s = src && d = dst) then
         res @ edges
       else
         iter edges (e::res)
  in iter edges []


0 commentaires

0
votes

J'ai réussi à détecter le cycle en utilisant la structure de données union-find.

Une structure pour représenter un sous-ensemble pour union-find:

let thereIsCycle c1 c2 g subset =
  let isCycle = try Some (union subset (findIndex c1 g.nodes) (findIndex c2 g.nodes)) with _ -> None in
      match isCycle with
     | Some isCycle -> false
     | None -> true

let rec findIndex x lst =
    match lst with
    | [] -> raise (Failure "Not Found")
    | h :: t -> if x = h then 0 else 1 + findIndex x t

Une fonction utilitaire à trouver ensemble d'un élément. Il utilise la technique de compression de chemin:

let union ({ parent = p; rank = r } as uf) x y =
    let cx = find uf x in
    let cy = find uf y in
    if cx == cy then raise (Failure "Cycle detected") else  begin
       if r.(cx) > r.(cy) then
          p.(cy) <- cx
       else if r.(cx) < r.(cy) then
          p.(cx) <- cy
       else begin
          r.(cx) <- r.(cx) + 1;
          p.(cy) <- cx
       end
    end

Une fonction qui fait l'union de deux ensembles de x et y. Il utilise l'union par rang:

let rec find uf i =
  let pi = uf.parent.(i) in
  if pi == i then
     i
  else begin
     let ci = find uf pi in
     uf.parent.(i) <- ci;
     ci
  end

J'ai créé une fonction pour vérifier s'il y a un cycle.

let create n =
    {parent = Array.init n (fun i -> i);
     rank = Array.init n (fun i -> 0)} 


0 commentaires