¿Cómo puedo encontrar (iterar) TODOS los ciclos en un gráfico dirigido desde / hacia un nodo dado?
Por ejemplo, quiero algo como esto:
A->B->A
A->B->C->A
pero no: B-> C-> B
algorithm
graph-theory
graph-algorithm
usuario7305
fuente
fuente
Respuestas:
Encontré esta página en mi búsqueda y dado que los ciclos no son los mismos que los componentes fuertemente conectados, seguí buscando y finalmente, encontré un algoritmo eficiente que enumera todos los ciclos (elementales) de un gráfico dirigido. Es de Donald B. Johnson y el documento se puede encontrar en el siguiente enlace:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Una implementación de Java se puede encontrar en:
http://normalisiert.de/code/java/elementaryCycles.zip
Una demostración de Mathematica del algoritmo de Johnson se puede encontrar aquí , la implementación se puede descargar desde la derecha ( "Descargar código de autor" ).
Nota: En realidad, hay muchos algoritmos para este problema. Algunos de ellos se enumeran en este artículo:
http://dx.doi.org/10.1137/0205007
Según el artículo, el algoritmo de Johnson es el más rápido.
fuente
A->B->C->A
elemental también?simple_cycle
en networkx.La primera búsqueda en profundidad con retroceso debería funcionar aquí. Mantenga una matriz de valores booleanos para realizar un seguimiento de si visitó un nodo anteriormente. Si te quedas sin nuevos nodos para ir (sin golpear un nodo que ya has estado), entonces retrocede e intenta con una rama diferente.
El DFS es fácil de implementar si tiene una lista de adyacencia para representar el gráfico. Por ejemplo, adj [A] = {B, C} indica que B y C son hijos de A.
Por ejemplo, pseudocódigo a continuación. "inicio" es el nodo desde el que comienza.
Llame a la función anterior con el nodo de inicio:
fuente
if (node == start):
- lo que estánode and start
en la primera llamadastart
). Comienza en ese vértice y realiza un DFS hasta que vuelve a ese vértice nuevamente, luego sabe que encontró un ciclo. Pero en realidad no genera los ciclos, solo un recuento de ellos (pero modificarlo para hacerlo no debería ser demasiado difícil).start
. Realmente no necesita borrar las banderas visitadas ya que cada bandera visitada se borrará debido avisited[node]=NO;
. Pero tenga en cuenta que si tiene un cicloA->B->C->A
, lo detectará 3 veces, comostart
puede ser cualquiera de esos 3. Una idea para evitar esto es tener otra matriz visitada dondestart
se establezca cada nodo que ha sido el nodo en algún momento, y luego no volver a visitarlos.En primer lugar, realmente no desea intentar encontrar literalmente todos los ciclos porque si hay 1, entonces hay un número infinito de esos. Por ejemplo, ABA, ABABA, etc. O puede ser posible unir 2 ciclos en un ciclo similar a 8, etc., etc. El enfoque significativo es buscar todos los llamados ciclos simples, aquellos que no se cruzan excepto en el punto inicial / final. Entonces, si lo desea, puede generar combinaciones de ciclos simples.
Uno de los algoritmos de línea de base para encontrar todos los ciclos simples en un gráfico dirigido es este: Realice un recorrido en profundidad de todos los caminos simples (aquellos que no se cruzan) en el gráfico. Cada vez que el nodo actual tiene un sucesor en la pila, se descubre un ciclo simple. Consiste en los elementos en la pila que comienzan con el sucesor identificado y terminan con la parte superior de la pila. El primer recorrido en profundidad de todas las rutas simples es similar a la primera búsqueda en profundidad, pero no marca / registra nodos visitados distintos de los que están actualmente en la pila como puntos de parada.
El algoritmo de fuerza bruta anterior es terriblemente ineficiente y, además, genera múltiples copias de los ciclos. Sin embargo, es el punto de partida de múltiples algoritmos prácticos que aplican varias mejoras para mejorar el rendimiento y evitar la duplicación de ciclos. Me sorprendió descubrir hace algún tiempo que estos algoritmos no están disponibles en los libros de texto y en la web. Así que investigué un poco e implementé 4 algoritmos y 1 algoritmo para ciclos en gráficos no dirigidos en una biblioteca Java de código abierto aquí: http://code.google.com/p/niographs/ .
Por cierto, ya que mencioné gráficos no dirigidos: el algoritmo para ellos es diferente. Construya un árbol de expansión y luego cada borde que no sea parte del árbol forma un ciclo simple junto con algunos bordes del árbol. Los ciclos encontrados de esta manera forman una llamada base de ciclo. Todos los ciclos simples se pueden encontrar combinando 2 o más ciclos básicos distintos. Para más detalles ver, por ejemplo, esto: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf .
fuente
jgrapht
que se usahttp://code.google.com/p/niographs/
, puede tomar un ejemplo de github.com/jgrapht/jgrapht/wiki/DirectedGraphDemoLa opción más simple que encontré para resolver este problema fue usar la lib llamada python
networkx
.Implementa el algoritmo de Johnson mencionado en la mejor respuesta de esta pregunta, pero es bastante simple de ejecutar.
En resumen, necesitas lo siguiente:
Respuesta: [['' a ',' b ',' d ',' e '], [' a ',' b ',' c ']]
fuente
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
Para aclarar:
Strongly Connected Components encontrará todos los subgrafos que tienen al menos un ciclo en ellos, no todos los ciclos posibles en el gráfico. por ejemplo, si toma todos los componentes fuertemente conectados y contrae / agrupa / fusiona cada uno de ellos en un nodo (es decir, un nodo por componente), obtendrá un árbol sin ciclos (un DAG en realidad). Cada componente (que es básicamente un subgrafo con al menos un ciclo) puede contener muchos más ciclos posibles internamente, por lo que SCC NO encontrará todos los ciclos posibles, encontrará todos los grupos posibles que tengan al menos un ciclo, y si agrupa ellos, entonces el gráfico no tendrá ciclos.
Para encontrar todos los ciclos simples en un gráfico, como otros mencionaron, el algoritmo de Johnson es un candidato.
fuente
Una vez me dieron esto como una pregunta de entrevista, sospecho que esto te ha sucedido y que vienes aquí por ayuda. Divide el problema en tres preguntas y se vuelve más fácil.
Problema 1) Use el patrón iterador para proporcionar una forma de iterar los resultados de la ruta. Un buen lugar para poner la lógica para obtener la siguiente ruta es probablemente el "moveNext" de su iterador. Para encontrar una ruta válida, depende de su estructura de datos. Para mí, era una tabla sql llena de posibilidades de ruta válidas, así que tuve que construir una consulta para obtener los destinos válidos dada una fuente.
Problema 2) Empuje cada nodo a medida que los encuentre en una colección a medida que los obtiene, esto significa que puede ver si está "duplicando" un punto muy fácilmente al interrogar la colección que está construyendo sobre la marcha.
Problema 3) Si en algún momento ves que te estás duplicando, puedes sacar cosas de la colección y "hacer una copia de seguridad". Luego, a partir de ese momento, intente "avanzar" nuevamente.
Hack: si está usando Sql Server 2008, hay algunas cosas nuevas de "jerarquía" que puede usar para resolver esto rápidamente si estructura sus datos en un árbol.
fuente
Las variantes basadas en DFS con bordes traseros encontrarán ciclos de hecho, pero en muchos casos NO serán ciclos mínimos . En general, DFS le da la señal de que hay un ciclo, pero no es lo suficientemente bueno como para encontrar ciclos. Por ejemplo, imagine 5 ciclos diferentes que comparten dos aristas. No hay una manera simple de identificar ciclos usando solo DFS (incluidas las variantes de retroceso).
De hecho, el algoritmo de Johnson ofrece todos los ciclos simples únicos y tiene una buena complejidad de tiempo y espacio.
Pero si solo desea encontrar ciclos MÍNIMOS (lo que significa que puede haber más de un ciclo atravesando cualquier vértice y estamos interesados en encontrar los mínimos) Y su gráfico no es muy grande, puede intentar usar el método simple a continuación. Es MUY simple pero bastante lento en comparación con el de Johnson.
Por lo tanto, uno de los absolutamente manera más fácil de encontrar ciclos mínima es utilizar el algoritmo de Floyd para encontrar caminos mínimos entre todos los vértices utilizando matriz de adyacencia. Este algoritmo no es tan óptimo como el de Johnson, pero es tan simple y su bucle interno es tan estrecho que para gráficos más pequeños (<= 50-100 nodos) tiene sentido usarlo. La complejidad temporal es O (n ^ 3), la complejidad espacial O (n ^ 2) si usa el seguimiento principal y O (1) si no lo hace. En primer lugar, busquemos la respuesta a la pregunta si hay un ciclo. El algoritmo es muy simple. A continuación se muestra un fragmento en Scala.
Originalmente, este algoritmo funciona en un gráfico de borde ponderado para encontrar todas las rutas más cortas entre todos los pares de nodos (de ahí el argumento de los pesos). Para que funcione correctamente, debe proporcionar 1 si hay un borde dirigido entre los nodos o NO_EDGE de lo contrario. Después de que se ejecute el algoritmo, puede verificar la diagonal principal, si hay valores inferiores a NO_EDGE que este nodo participa en un ciclo de longitud igual al valor. Todos los demás nodos del mismo ciclo tendrán el mismo valor (en la diagonal principal).
Para reconstruir el ciclo en sí mismo, necesitamos usar una versión ligeramente modificada del algoritmo con el seguimiento principal.
La matriz de los padres inicialmente debe contener el índice de vértice de origen en una celda de borde si hay un borde entre los vértices y -1 de lo contrario. Después de que la función regrese, para cada borde tendrá referencia al nodo padre en el árbol de ruta más corto. Y luego es fácil recuperar los ciclos reales.
En general, tenemos el siguiente programa para encontrar todos los ciclos mínimos
y un pequeño método principal solo para probar el resultado
y la salida es
fuente
En el caso del gráfico no dirigido, un artículo publicado recientemente ( Lista óptima de ciclos y rutas st en gráficos no dirigidos ) ofrece una solución asintóticamente óptima. Puede leerlo aquí http://arxiv.org/abs/1205.2766 o aquí http://dl.acm.org/citation.cfm?id=2627951 Sé que no responde a su pregunta, pero desde el título de su pregunta no menciona la dirección, aún podría ser útil para la búsqueda de Google
fuente
Comience en el nodo X y verifique todos los nodos secundarios (los nodos primarios y secundarios son equivalentes si no están dirigidos). Marque esos nodos hijos como hijos de X. Desde cualquier nodo hijo A, marque hijos de ser hijos de A, X ', donde X' está marcado como a 2 pasos de distancia). Si luego presiona X y lo marca como hijo de X '', eso significa que X está en un ciclo de 3 nodos. Retroceder a su padre es fácil (tal como está, el algoritmo no tiene soporte para esto, por lo que encontrará el padre que tenga X ').
Nota: Si el gráfico no está dirigido o tiene bordes bidireccionales, este algoritmo se vuelve más complicado, suponiendo que no desea atravesar el mismo borde dos veces durante un ciclo.
fuente
Si lo que desea es encontrar todos los circuitos elementales en un gráfico, puede usar el algoritmo EC, de JAMES C. TIERNAN, encontrado en un documento desde 1970.
El muy original algoritmo EC que logré implementar en php (espero que no haya errores se muestra a continuación). También puede encontrar bucles si hay alguno. Los circuitos en esta implementación (que intenta clonar el original) son los elementos distintos de cero. Cero aquí significa no existencia (nulo como lo conocemos).
Además de lo que sigue a continuación, otra implementación que le da al algoritmo más independencia, esto significa que los nodos pueden comenzar desde cualquier lugar incluso desde números negativos, por ejemplo -4, -3, -2, etc.
En ambos casos se requiere que los nodos sean secuenciales.
Es posible que deba estudiar el documento original, Algoritmo del circuito de James C. Tiernan Elementary
entonces esta es la otra implementación, más independiente del gráfico, sin goto y sin valores de matriz, en su lugar utiliza claves de matriz, la ruta, el gráfico y los circuitos se almacenan como claves de matriz (use valores de matriz si lo desea, simplemente cambie los valores requeridos líneas). El gráfico de ejemplo comienza desde -4 para mostrar su independencia.
He analizado y documentado la CE, pero desafortunadamente la documentación está en griego.
fuente
Hay dos pasos (algoritmos) involucrados en la búsqueda de todos los ciclos en un DAG.
El primer paso es usar el algoritmo de Tarjan para encontrar el conjunto de componentes fuertemente conectados.
El segundo paso es encontrar ciclos (rutas) dentro de los componentes conectados. Mi sugerencia es usar una versión modificada del algoritmo de Hierholzer.
La idea es:
Aquí está el enlace a una implementación de Java con un caso de prueba:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
fuente
http://www.me.utexas.edu/~bard/IP/Handouts/cycles.pdf
fuente
Me topé con el siguiente algoritmo que parece ser más eficiente que el algoritmo de Johnson (al menos para gráficos más grandes). Sin embargo, no estoy seguro de su rendimiento en comparación con el algoritmo de Tarjan.
Además, solo lo revisé para ver triángulos hasta ahora. Si está interesado, consulte "Algoritmos de listado de arboricidad y subgrafía" por Norishige Chiba y Takao Nishizeki ( http://dx.doi.org/10.1137/0214017 )
fuente
Solución Javascript que utiliza listas enlazadas de conjuntos disjuntos. Se puede actualizar a bosques conjuntos disjuntos para tiempos de ejecución más rápidos.
fuente
DFS desde el nodo de inicio s, realice un seguimiento de la ruta de DFS durante el recorrido y registre la ruta si encuentra un borde desde el nodo v en la ruta hacia s. (v, s) es un back-edge en el árbol DFS y por lo tanto indica un ciclo que contiene s.
fuente
Con respecto a su pregunta sobre el ciclo de permutación , lea más aquí: https://www.codechef.com/problems/PCYCLE
Puede probar este código (ingrese el tamaño y el número de dígitos):
fuente
Versión DFS c ++ para el pseudocódigo en la respuesta del segundo piso:
fuente