2009-03-07 3 views
6

Estoy cargando datos genealógicos de caballo recursivamente. Para algunos conjuntos de datos incorrectos, mi recursión nunca se detiene ... y eso se debe a que hay ciclos en los datos.Detectar ciclos en un gráfico de genealogía durante una búsqueda de profundidad en primer lugar

¿Cómo puedo detectar esos ciclos para que dejen de repetirse?

Pensé en repetir mientras mantengo un hashTable con todos los caballos "visitados". Pero eso encontrará algunos falsos positivos, porque un caballo PUEDE estar dos veces en un árbol.

Lo que no puede suceder es que un caballo aparezca como padre o abuelo o bisabuelo de SÍ MISMO.

+1

No existe un ciclo en un árbol binario. Incluso los datos genealógicos correctos no serían un árbol binario. Edité la pregunta para leer "gráfico" – Triptych

+0

curioso, ¿esto es para averiguar el Índice de dosificación, Werk Nick Rating o algo similar (como en purasangres)? – nlucaroni

+0

No ... Estoy exportando todo el pedigrí del caballo a un archivo. Lo usaré también para detectar la endogamia, pero como mi producto de software no es específico de raza, no tengo mucha información histórica para analizar. – Romias

Respuesta

6

pseudo código:

void ProcessTree(GenTreeNode currentNode, Stack<GenTreeNode> seen) 
{ 
    if(seen.Contains(currentNode)) return; 
    // Or, do whatever needs to be done when a cycle is detected 

    ProcessHorse(currentNode.Horse); // Or whatever processing you need 

    seen.Push(currentNode); 

    foreach(GenTreeNode childNode in currentNode.Nodes) 
    { 
     ProcessTree(childNode, seen); 
    } 

    seen.Pop(); 
} 

La idea básica es mantener una lista de todos los nodos que ya hemos visto en nuestro camino hacia el nodo actual; si fue volver a un nodo que ya hemos pasado, entonces usted sabe que hemos formado un ciclo (y debemos omitir el valor, o hacer lo que sea necesario)

+0

Parece que funciona ... pegado con el dedo :) Lo intentaré y creo que falta un caso de borde. Te lo haré saber :) – Romias

+0

Bueno ... esto funcionó como un encanto. De todos modos, mis datos genealógicos de prueba eran realmente una basura, pero al menos incluso en esos terribles casos, mi software lo toleraba. También agregué otras condiciones de parada relacionadas con la longitud de la pila ... para establecer una profundidad máxima en el árbol. ¡Gracias! – Romias

+0

@Romias: No hay problema:] –

2

Mantiene una pila de todos los elementos que conducen a la raíz del árbol.

Cada vez que avanza por el árbol, escanee la pila para el elemento secundario. Si encuentra una coincidencia, entonces ha descubierto un ciclo y debe omitirlo. De lo contrario, empuje al niño en la pila y continúe. Cada vez que vuelves al árbol, saca un elemento de la pila y deséchalo.

(En el caso de datos genealógicas, un nodo de "niño" en el árbol es presumiblemente el padre biológico del nodo "padre".)

1

Una forma muy sencilla de detectar esto, es mediante la comprobación de que la restricción sí mismo:

Lo que no puede suceder es que un caballo aparezca como padre o abuelo o bisabuelo de SÍ MISMO.

Siempre que inserte un nodo en su árbol, recorra el árbol hasta la raíz para asegurarse de que el caballo no exista como un tipo de elemento primario.

Para acelerar esto, puede asociar una tabla hash a cada nodo, donde almacena la respuesta en caché de dicha búsqueda. Entonces no tiene que buscar todo el camino la próxima vez que inserte un caballo debajo de ese nodo.

0

Su solución de tabla hash debería funcionar si usted realiza un seguimiento de los nodos en lugar de caballos. Solo asegúrese de que cada vez que lea un nuevo caballo cree un nuevo nodo incluso si el valor/caballo es el mismo que el valor/caballo de un nodo anterior.

0

Estás tratando con un directed acyclic graph, no es un árbol. No debería haber ningún ciclo, ya que el descendiente de un caballo tampoco puede ser su antepasado.

Sabiendo esto, debe aplicar técnicas de código específicas para gráficos acíclicos dirigidos.

+0

Cuando tienes un caballo, siempre obtienes un árbol binario porque un caballo solo tiene 2 padres. Cuando esto no está bien formado, a veces obtienes un ciclo. – Romias

+0

La endogamia hace que esto sea un DAG, no un árbol binario. Me gusta, Court Vision (http://www.pedigreequery.com/court+vision) con Native Dancer 4x5 y Nasrullah 5x5. Aunque se presenta aquí como un árbol binario, en realidad es un DAG. – nlucaroni

+0

Sí ... tienes razón ... No estaba considerando el caso de endogamia como ser el MISMO caballo, solo su posición en el pedigrí. – Romias

2

Esto suena como un caso en el que finalmente puede aplicar la pregunta de preguntas de la entrevista: busque un ciclo en una lista vinculada utilizando solo la memoria O (1).

En este caso, su "lista vinculada" es la secuencia de elementos que enumera.Usa dos enumeradores, ejecuta uno a la mitad de velocidad, y si el rápido alguna vez se encuentra con el lento, entonces tienes un bucle. Este también será el tiempo O (n) en lugar del tiempo O (n^2) requerido para verificar una lista 'vista'. Lo malo es que solo se entera sobre el ciclo después de que algunos de los nodos hayan sido procesados ​​varias veces.

En el ejemplo he reemplazado el método de 'media velocidad' con el método de 'soltar marcadores' más simple de escribir.

class GenTreeNode { 
    ... 

    ///<summary>Wraps an the enumeration of linked data structures such as trees and linked lists with a check for cycles.</summary> 
    private static IEnumerable<T> CheckedEnumerable<T>(IEnumerable<T> sub_enumerable) { 
     long cur_track_count = 0; 
     long high_track_count = 1; 
     T post = default(T); 
     foreach (var e in sub_enumerable) { 
      yield return e; 
      if (++cur_track_count >= high_track_count) { 
       post = e; 
       high_track_count *= 2; 
       cur_track_count = 0; 
      } else if (object.ReferenceEquals(e, post)) { 
       throw new Exception("Infinite Loop"); 
      } 
     } 
    } 

    ... 

    ///<summary>Enumerates the tree's nodes, assuming no cycles</summary> 
    private IEnumerable<GenTreeNode> tree_nodes_unchecked() { 
     yield return this; 
     foreach (var child in this.nodes) 
      foreach (var e in child.tree_nodes_unchecked()) 
       yield return e; 
    } 
    ///<summary>Enumerates the tree's nodes, checking for cycles</summary> 
    public IEnumerable<GenTreeNode> tree_nodes() 
    { 
     return CheckedEnumerable(tree_nodes_unchecked()); 
    } 

    ... 

    void ProcessTree() { 
     foreach (var node in tree_nodes()) 
      proceess(node); 
    } 
} 
+0

Por ahora ... la solución de la lista "visto" está funcionando. Me doy cuenta de que su enfoque debería ser más eficiente. Tengo algunos árboles para ser de Padre a Hijo y puede ser que pueda usar su enfoque. Gracias de cualquier manera. – Romias

Cuestiones relacionadas