¿Cómo puedo verificar si un gráfico dirigido es acíclico? ¿Y cómo se llama el algoritmo? Agradecería una referencia.¿Cómo puedo verificar si un gráfico dirigido es acíclico?
Respuesta
Me gustaría intentar sort the graph topologically, y si no puede, entonces tiene ciclos.
¿Cómo esto no tiene votos? ¡Es lineal en nodos + bordes, muy superior a las soluciones O (n^2)! –
Acabo de responder :) – FryGuy
En muchos casos, un DFS (vea la respuesta de J.Conrod) puede ser más fácil, especialmente si un DFS debe realizarse de todos modos. Pero, por supuesto, esto depende del contexto. – sleske
La solución dada por ShuggyCoUk es incompleta porque podría no verificar todos los nodos.
def isDAG(nodes V):
while there is an unvisited node v in V:
bool cycleFound = dfs(v)
if cyclefound:
return false
return true
Esto tiene timecomplexity O (n + m) u O (n^2)
el mío es de hecho incorrecto - Lo eliminé, así que ahora parece un poco fuera de contexto – ShuggyCoUk
O (n + m) <= O (n + n) = O (2n), O (2n)! = O (n^2) – Artru
@Artru O (n^2) cuando se utiliza la matriz de adyacencia, O (n + m) cuando se utiliza la lista de adyacencia para representar el gráfico. – 0x450
haciendo una simple profundidad de primera búsqueda se no lo suficientemente bueno para encontrar un ciclo. Es posible visitar un nodo varias veces en un DFS sin un ciclo existente. Dependiendo de dónde empiece, es posible que no visite todo el gráfico.
Puede verificar los ciclos en un componente conectado de un gráfico de la siguiente manera. Encuentra un nodo que solo tenga bordes salientes. Si no hay tal nodo, entonces hay un ciclo. Comience un DFS en ese nodo. Al atravesar cada borde, compruebe si el borde apunta a un nodo que ya está en su pila. Esto indica la existencia de un ciclo. Si no encuentra ese borde, no hay ciclos en ese componente conectado.
Como señala Rutger Prins, si su gráfico no está conectado, necesita repetir la búsqueda en cada componente conectado.
Como referencia, Tarjan's strongly connected component algorithm está estrechamente relacionado. También lo ayudará a encontrar los ciclos, no solo a informar si existen.
BTW: un borde que "apunta a un nodo que ya está en su pila" a menudo se denomina "borde posterior" en la literatura, por razones obvias. Y sí, esto puede ser más simple que ordenar el gráfico topológicamente, especialmente si necesita hacer un DFS de todos modos. – sleske
Para que el gráfico sea acíclico, usted dice que cada componente conectado debe contener un nodo con solo bordes salientes. ¿Puede recomendar un algoritmo para encontrar los componentes conectados (a diferencia de los componentes "fuertemente" conectados) de un gráfico dirigido, para que los use su algoritmo principal? – kostmo
@kostmo, si el gráfico tiene más de un componente conectado, entonces no visitará todos los nodos en su primer DFS. Mantenga un registro de los nodos que ha visitado y repita el algoritmo con nodos no visitados hasta que llegue a todos. Esto es básicamente cómo funciona un algoritmo de componentes conectados de todos modos. –
Lemma 22.11 en el libro Introduction to Algorithms
(Segunda Edición) establece que:
un grafo dirigido G es acíclico si y sólo si una búsqueda en profundidad de G no da la espalda bordes
Sé que este es un tema antiguo, pero para los buscadores del futuro aquí hay una implementación de C# que he creado (¡ningún reclamo de que sea más eficiente!). Esto está diseñado para usar un número entero simple para identificar cada nodo. Puedes decorar eso como desees siempre que los hashes de objetos de tu nodo sean iguales.
Para gráficos muy profundos esto puede tener una sobrecarga elevada, ya que crea un hashset en cada nodo en profundidad (se destruyen a lo ancho).
Ingrese el nodo desde el que desea buscar y la ruta de acceso a ese nodo.
- Para un gráfico con un único nodo raíz que envíe ese nodo y un hashset vacío
- Para un gráfico que tiene múltiples nodos raíz que envuelven esta en un foreach sobre esos nodos y aprobar una nueva hashset vacío para cada iteración
Cuando la comprobación de ciclos por debajo de cualquier nodo dado, sólo tiene que pasar a lo largo de ese nodo con un hashset vacío
private bool FindCycle(int node, HashSet<int> path) { if (path.Contains(node)) return true; var extendedPath = new HashSet<int>(path) {node}; foreach (var child in GetChildren(node)) { if (FindCycle(child, extendedPath)) return true; } return false; }
Aquí es mi rubí implemen tación del peel off leaf node algorithm.
def detect_cycles(initial_graph, number_of_iterations=-1)
# If we keep peeling off leaf nodes, one of two things will happen
# A) We will eventually peel off all nodes: The graph is acyclic.
# B) We will get to a point where there is no leaf, yet the graph is not empty: The graph is cyclic.
graph = initial_graph
iteration = 0
loop do
iteration += 1
if number_of_iterations > 0 && iteration > number_of_iterations
raise "prevented infinite loop"
end
if graph.nodes.empty?
#puts "the graph is without cycles"
return false
end
leaf_nodes = graph.nodes.select { |node| node.leaving_edges.empty? }
if leaf_nodes.empty?
#puts "the graph contain cycles"
return true
end
nodes2 = graph.nodes.reject { |node| leaf_nodes.member?(node) }
edges2 = graph.edges.reject { |edge| leaf_nodes.member?(edge.destination) }
graph = Graph.new(nodes2, edges2)
end
raise "should not happen"
end
Solución 1: algoritmo de Kahn para verificar ciclo. Idea principal: mantener una cola donde el nodo con grado cero se agregará a la cola. A continuación, despegue el nodo uno por uno hasta que la cola esté vacía. Verifique si los bordes internos de cualquier nodo existen.
Solución 2: algoritmo Tarjan para verificar componente conectado Strong.
Solución3: DFS. Utilice la matriz de enteros para etiquetar el estado actual del nodo: , es decir, 0 - significa que este nodo no se ha visitado anteriormente. -1 - significa que se ha visitado este nodo y se están visitando sus nodos secundarios. 1 - significa que este nodo ha sido visitado y listo. Entonces, si el estado de un nodo es -1 mientras se realiza el DFS, significa que debe existir un ciclo.
No debe haber ningún borde posterior mientras se hace DFS. Mantenga la pista de los nodos ya visitados mientras hace DFS, si encuentra un borde entre el nodo actual y el nodo existente, entonces el gráfico tiene ciclo.
aquí es un código rápida de encontrar si un gráfico tiene ciclos:
func isCyclic(G : Dictionary<Int,Array<Int>>,root : Int , var visited : Array<Bool>,var breadCrumb : Array<Bool>)-> Bool
{
if(breadCrumb[root] == true)
{
return true;
}
if(visited[root] == true)
{
return false;
}
visited[root] = true;
breadCrumb[root] = true;
if(G[root] != nil)
{
for child : Int in G[root]!
{
if(isCyclic(G,root : child,visited : visited,breadCrumb : breadCrumb))
{
return true;
}
}
}
breadCrumb[root] = false;
return false;
}
let G = [0:[1,2,3],1:[4,5,6],2:[3,7,6],3:[5,7,8],5:[2]];
var visited = [false,false,false,false,false,false,false,false,false];
var breadCrumb = [false,false,false,false,false,false,false,false,false];
var isthereCycles = isCyclic(G,root : 0, visited : visited, breadCrumb : breadCrumb)
La idea es la siguiente: un algoritmo DFS normal con una matriz para realizar un seguimiento de los nodos visitados, y una serie adicional que sirve como un marcador para los nodos que conducen al nodo actual, de modo que cada vez que ejecutemos un dfs para un nodo, establecemos su elemento correspondiente en el conjunto de marcadores como verdadero, de modo que cuando se encuentre un nodo ya visitado, verifiquemos si su correspondiente el elemento en la matriz de marcadores es verdadero, si es verdadero, entonces es uno de los nodos que se permite a sí mismo (por lo tanto, un ciclo), y el truco es cada vez que devuelve un dfs de un nodo, establecemos su marcador correspondiente a falso, por lo que si lo visitamos nuevamente desde otra ruta no nos engañen.
- 1. ¿Cómo transformo un gráfico no dirigido y muy cíclico en un gráfico acíclico dirigido?
- 2. ¿Cómo se almacena un gráfico acíclico dirigido (DAG) como JSON?
- 3. Cómo convertir el gráfico acíclico dirigido (DAG) al árbol
- 4. ¿Cómo detectar si un gráfico dirigido es cíclico?
- 5. ¿Cómo se llama este algoritmo para buscar la ruta máxima en un gráfico acíclico dirigido?
- 6. Formas de mapeo de un gráfico acíclico dirigido a una grilla/matriz
- 7. Escribiendo un programa para verificar si un gráfico es bipartito
- 8. ¿Hay algún tipo de datos de Gráfico Acíclico Dirigido (DAG) en Java, y debería usarlo?
- 9. Ruta acíclica más larga en un gráfico no ponderado dirigido
- 10. Django gráfico no dirigido
- 11. Encontrar un ciclo en un gráfico no dirigido versus encontrar uno en un gráfico dirigido
- 12. Cómo comprobar si un gráfico no dirigido tiene un ciclo de longitud impar
- 13. ¿Cómo puedo verificar si un valor es un número?
- 14. consulta de base de datos eficiente para antepasados en un grafo acíclico dirigido
- 15. El mejor algoritmo para determinar si un gráfico no dirigido es un árbol
- 16. Redis: implementar gráfico dirigido ponderado
- 17. Implementando un gráfico dirigido en python
- 18. ¿Modelar un gráfico no dirigido en Rails?
- 19. ¿Cómo puedo verificar si un entero con signo es positivo?
- 20. ¿Cómo puedo verificar si un identificador de MATLAB es válido?
- 21. Recorrido de gráfico cíclico dirigido
- 22. ¿Cómo puedo verificar si existe un directorio?
- 23. ¿cómo puedo verificar si existe un archivo?
- 24. ¿Cómo implementar un gráfico no dirigido en Ruby on Rails?
- 25. ¿Cómo puedo convertir un dígrafo en un gráfico no dirigido en networkx?
- 26. ¿Cómo verificar si un borde está en algún ciclo?
- 27. Visualizar gráfico no dirigido que es demasiado grande para GraphViz?
- 28. ¿Cuál es la forma más eficiente de determinar si un gráfico dirigido está conectado de forma individual?
- 29. Detectando ciclos de un gráfico (tal vez dirigido o no dirigido) en Haskell
- 30. ¿Por qué no puedo verificar si un 'DateTime' es 'Nothing'?
Otro caso a favor de alguna forma de "arreglar" las respuestas incorrectas en SO. – Sparr
Entonces, umm, estoy más que interesado en el tiempo necesario para encontrarlo. Entonces, solo necesito el algoritmo abstracto. – nes1983
debe atravesar todos los bordes y verificar todos los vértices, por lo que el límite inferior es O (| V | + | E |). DFS y BFS tienen la misma complejidad, pero DFS es más fácil de codificar si tiene recursión, ya que administra la pila para usted ... – ShuggyCoUk