2012-02-17 12 views
5

Tiene el siguiente problema tiene un nombre y hay un algoritmo para resolverlo? : Dado un gráfico, ya sea dirigido o no, encontrar todos los caminos que satisfacen la especificación dada porgráfico: encuentre los subgrafos utilizando la lista de nodos incluyendo los comodines

  1. una lista de nodos exactas o
  2. '*?' que denota sólo 'cualquier nodo o ningún nodo en absoluto', o
  3. '* {n}' que denotan 'Cualquiera N nodos conectados consecutivamente'

por ejemplo

A -> B -> *? -> D which results in ABXD and ABYD and ABD etc. 

o

A -> *{1} -> D -> *? -> E which results in ABXDZE and ABYDZE and ABDZE etc. etc. 

gracias

P. S. ¿Alguien sabe una biblioteca de gráficos haciendo esto en R o perl o C?

+0

Esto es todo lo que pude encontrar: http: //www.vldb.org/conf/1989/P185.PDF – Diego

+0

¿Las rutas requeridas son simples? ¿O pueden tener ciclos? – templatetypedef

+0

Tener ciclo implicaría un conjunto infinito de soluciones. – Faylixe

Respuesta

1

Lo que hice al final fue:

  1. El problema es encontrar todos los caminos de longitud N entre 2 nodos. Los ciclos están excluidos.
  2. lee los datos como edgelist, p. pares de from-> a nodos (se supone que los nombres de los nodos son únicos)
  3. crean una tabla hash (o unordered_map en boost y stl, C++) de nombres de nodo como claves y una tabla hash como un valor.
  4. esta segunda tabla hash contendrá todos los nodos que el primer nodo lleva como claves.

Por ejemplo

A->B 
A->C 
B->D 
C->E 
E->D 

la estructura de datos resultante que contiene los datos de entrada en notación Perl se parece a esto después de leer todos los datos como un 'edgelist':

my %hash = (
'A' => {'B' => 1, 'C' => 1}, 
'B' => {'D' => 1}, 
'C' => {'E' => 1}, 
'E' => {'D' => 1}, 
); 

encontrar si un par de nodos está DIRECTAMENTE conectado se puede hacer más o menos como (perl):

sub search { 
    my ($from,$to) = @_; 
    if($to eq '*'){ return defined($x=$hash{$from}) ? [keys $hash{$from}] : [] } 
    return defined($x=$hash{$from}) && defined($x{$to}) ? [$to] : [] 
} 

En la función anterior hay una provisión para devolver todos los nodos a los que está conectado un nodo 'from', configurando $ to a '*'. El retorno es una referencia de matriz de nodos conectados directamente al parámetro $ from.

La búsqueda de la ruta entre dos nodos requiere el uso recursivo de la función anterior.

p. Ej.

sub path { 
    my ($from,$to, $hops, $save_results) = @_; 
    if($hops < 0){ return 0 } 
    $results = search($from, '*'); 
    if(""[email protected]$results == 0){ return 0 } 
    $found = 0; 
    foreach $result (@$results){ 
     $a_node = new Tree::Nary($result); 
     if(path($result, $to, $hops-1, $a_node) == 1){ 
      $save_results->insert($save_results, -1, $a_node); 
      $found = 1; 
     } 
    } 
    return $found; 

}

Es aceptable utilizar la recursividad si la profundidad no es demasiado (es decir, $ lúpulo < 6?), Debido a desbordamiento de pila [sic].

La parte más complicada es leer los resultados y extraer los nodos para cada ruta. Después de mucha deliberación, decidí usar Tree :: Nary (árbol n-ary) para almacenar los resultados. Al final tenemos el siguiente árbol:

 |-> B -> D 
A -> |-> C -> E -> D 

Con el fin de extraer todos los caminos, hacer:

  1. encontrar todos los nodos hoja
  2. inicio de cada nodo hoja se mueve hacia atrás a través de su matriz al nodo raíz y guardando el nombre del nodo.

Lo anterior se implementó utilizando perl, pero también lo hemos hecho en C++ utilizando boost :: unordered_map for hashtable. Todavía no he agregado una estructura de árbol en el código C++.

Resultados: para 3281415 bordes y 18601 nodos únicos, perl toma 3 minutos para encontrar A -> '*' -> '*' -> B. Daré una actualización sobre el código de C++ cuando esté listo.

+0

Oh, por cierto leer un archivo grande también es un tema en sí mismo. El formato de archivo son pares de nombres de nodo separados por espacios en blanco en una línea propia. En perl, está bien leer línea por línea y luego dividir cada línea a medida que se lee. Primero, leer el archivo en la memoria y luego hacer un bucle con una expresión regular toma más o menos el mismo tiempo. En C++, usé boost :: split para dividir una línea en nombres de nodo.Leer un archivo línea por línea (usando fopen y fgets de C) es un poco más lento que leerlo en la memoria (usando la lectura de C()) y luego dividirlo en memoria usando boost :: split, aproximadamente un 10% más lento. – bliako

1

no sé cualquier biblioteca para eso, pero hay que separar esto en dos partes:

  • la consulta del usuario analizar
  • El algoritmo para encontrar lo que busca

Para el análisis, te dejo encontrar lo que necesitas hacer (utilizando la biblioteca de análisis o por ti mismo)

En cuanto a la parte del algoritmo, sugiero que definas una estructura especial (como una lista vinculada) para representar su consulta, en la cual cada elemento puede denotar un nodo real, x número de nodo o número ilimitado de nodos.

El único problema en su algoritmo es encontrar toda la ruta desde un nodo A a un nodo B, usando un número ilimitado o un número limitado de nodos intermedios. Puede hacerlo utilizando una programación dinámica o un algoritmo de búsqueda como DFS o BFS.

Cuestiones relacionadas