Encuentre el camino más corto en un gráfico que visita ciertos nodos

82

Tengo un gráfico no dirigido con aproximadamente 100 nodos y aproximadamente 200 bordes. Un nodo está etiquetado como "inicio", otro es "final" y hay alrededor de una docena con la etiqueta "debe pasar".

Necesito encontrar la ruta más corta a través de este gráfico que comienza en 'inicio', termina en 'final' y pasa por todos los nodos 'mustpass' (en cualquier orden).

( http://3e.org/local/maize-graph.png / http://3e.org/local/maize-graph.dot.txt es el gráfico en cuestión; representa un laberinto de maíz en Lancaster, PA)

Daniel
fuente

Respuestas:

77

Todos los demás que comparan esto con el problema del vendedor ambulante probablemente no hayan leído su pregunta con atención. En TSP, el objetivo es encontrar el ciclo más corto que visita todos los vértices (un ciclo hamiltoniano); corresponde a tener cada nodo etiquetado como 'mustpass'.

En su caso, dado que solo tiene alrededor de una docena etiquetada como 'mustpass', ¡y dado que 12! es bastante pequeño (479001600), simplemente puede probar todas las permutaciones de solo los nodos 'mustpass', y mirar la ruta más corta desde 'start' a 'end' que visita los nodos 'mustpass' en ese orden; simplemente ser la concatenación de las rutas más cortas entre cada dos nodos consecutivos en esa lista.

En otras palabras, primero encuentre la distancia más corta entre cada par de vértices (puede usar el algoritmo de Dijkstra u otros, pero con esos números pequeños (100 nodos), incluso el algoritmo Floyd-Warshall más simple de codificar se ejecutará a tiempo). Luego, una vez que tenga esto en una tabla, pruebe todas las permutaciones de sus nodos 'mustpass' y el resto.

Algo como esto:

//Precomputation: Find all pairs shortest paths, e.g. using Floyd-Warshall
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
    for i=1 to n:
        for j=1 to n:
            d[i][j] = min(d[i][j], d[i][k] + d[k][j])
//That *really* gives the shortest distance between every pair of nodes! :-)

//Now try all permutations
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
    shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest

(Por supuesto, ese no es un código real, y si desea la ruta real, tendrá que realizar un seguimiento de qué permutación proporciona la distancia más corta, y también cuáles son las rutas más cortas de todos los pares, pero ya entiende la idea).

Se ejecutará como máximo en unos pocos segundos en cualquier lenguaje razonable :)
[Si tiene n nodos y k nodos 'mustpass', su tiempo de ejecución es O (n 3 ) para la parte Floyd-Warshall, y O (k! N ) para la parte de todas las permutaciones, y 100 ^ 3 + (¡12!) (100) son prácticamente cacahuetes a menos que tenga algunas restricciones realmente restrictivas.]

ShreevatsaR
fuente
5
Dado el pequeño tamaño de entrada, estoy de acuerdo con la respuesta. Pero me interesa saber por qué rechaza las afirmaciones de otros de que este es un caso de TSP. Según tengo entendido, podría extraer todos los nodos obligatorios y usarlos para crear un subgráfico. Los bordes entre los nodos en el sub-gráfico tienen pesos correspondientes a la solución APSP en el gráfico original. Entonces, ¿la pregunta no se convierte en una instancia de TSP en el subgrafo? Su solución parece ser una solución de fuerza bruta para ese problema (lo cual está bien).
maditya
6
@maditya: En primer lugar, espero que esté de acuerdo en que (para citar el comentario de Steven A Lowe sobre otra respuesta) una respuesta como "TSP es difícil, bwahahaha" no es una respuesta adecuada para alguien que tiene un problema real que resolver, especialmente uno muy fácil. resuelto en cualquier computadora de las últimas décadas. En segundo lugar, esto no es idéntico al TSP por razones triviales (formato de entrada diferente): la pequeña instancia de TSP que contiene es para un gráfico más pequeño, no uno de tamaño de entrada N. Por lo tanto, la completitud NP depende de cuántos nodos 'deben pasar' hay asintóticamente: si siempre es 12, u O (log N), no es NP-completo, etc.
ShreevatsaR
1
No estoy seguro de si el resultado sería un camino. Imagine tener que ir de aa ctravés b. Puede ser que los caminos más cortos de ba ay ccomparten un borde. En ese caso, el borde se repetiría dos veces. Uno de los dos caminos debería ser peor que el óptimo para no generar ciclos.
Spak
1
@PietroSaccardi De la descripción en la pregunta, parece que el objetivo es simplemente encontrar la "forma" más corta de atravesar todos esos nodos, y puede estar bien si se repite alguna ventaja. Es decir, "camino" se usa en un sentido amplio. De hecho, si no se permiten bordes repetidos, es posible que ni siquiera haya una respuesta (por ejemplo, considere un gráfico B — A — C donde se le pide que vaya de A a C mientras pasa por B: no hay forma de no repetir el B: borde A).
ShreevatsaR
El algoritmo Floyd-Warshall no se implementa correctamente aquí: dado que todas las celdas de la matriz se inicializan con INF, la línea se d[i][j] = min(d[i][j], d[i][k] + d[k][j])convierte en d[i][j] = min(INF, INF + INF)y todas las celdas siempre permanecen iguales a INF. Debe agregar un paso para llenar esta matriz con las longitudes de los bordes del gráfico.
Stef
24

ejecute el algoritmo de Djikstra para encontrar las rutas más cortas entre todos los nodos críticos (inicio, final y paso obligado), luego un recorrido de profundidad primero debería indicarle la ruta más corta a través del subgrafo resultante que toca todos los nodos de inicio. .debepasses ... fin

Steven A. Lowe
fuente
Parece que tiene potencial para ser más eficiente que la solución seleccionada. Apuesto a que si lo hubieras desarrollado un poco más, habría obtenido una puntuación más alta. Primero tuve que pensar si una ruta más corta entre todos los pares garantiza una ruta entre todos los nodos requeridos ... pero creo que sí (suponiendo que no esté dirigida).
galactikuh
Creo que esto no funciona si un camino no debe repetir bordes.
tuket
1
¿Cómo encontraría el recorrido en profundidad el camino más corto en el ejemplo del día 24 del Advenimiento del Código? adventofcode.com/2016/day/24
Erwin Rooijakkers
15

Estos son dos problemas ... Steven Lowe señaló esto, pero no dio suficiente respeto a la segunda mitad del problema.

Primero debe descubrir las rutas más cortas entre todos sus nodos críticos (inicio, final, debe pasar). Una vez que se descubren estas rutas, puede construir un gráfico simplificado, donde cada borde en el nuevo gráfico es una ruta de un nodo crítico a otro en el gráfico original. Hay muchos algoritmos de búsqueda de rutas que puede utilizar para encontrar la ruta más corta aquí.

Sin embargo, una vez que tenga este nuevo gráfico, tendrá exactamente el problema del vendedor ambulante (bueno, casi ... No es necesario volver al punto de partida). Se aplicará cualquiera de las publicaciones relativas a esto, mencionadas anteriormente.

Andrew Top
fuente
14

En realidad, el problema que publicaste es similar al del vendedor ambulante, pero creo que se acerca más a un simple problema de búsqueda de caminos. En lugar de tener que visitar todos y cada uno de los nodos, simplemente necesita visitar un conjunto particular de nodos en el menor tiempo (distancia) posible.

La razón de esto es que, a diferencia del problema del vendedor ambulante, un laberinto de maíz no le permitirá viajar directamente de un punto a otro en el mapa sin necesidad de pasar por otros nodos para llegar allí.

De hecho, recomendaría A * pathfinding como técnica a considerar. Esto se configura al decidir qué nodos tienen acceso a qué otros nodos directamente y cuál es el "costo" de cada salto desde un nodo en particular. En este caso, parece que cada "salto" podría tener el mismo costo, ya que sus nodos parecen estar relativamente cerca. A * puede usar esta información para encontrar la ruta de menor costo entre dos puntos. Dado que necesita ir del punto A al punto B y visitar aproximadamente 12 en el medio, incluso un enfoque de fuerza bruta que utilice la búsqueda de caminos no estaría de más.

Solo una alternativa a considerar. Se parece notablemente al problema del viajante de comercio, y esos son buenos papeles para leer, pero mire más de cerca y verá que solo es demasiado complicado. ^ _ ^ Esto viene de la mente de un programador de videojuegos que se ha ocupado de este tipo de cosas antes.

Nicholas Flynt
fuente
2
+1: esta es una respuesta mucho mejor que 'el problema del vendedor ambulante es difícil, bwahahaha'
Steven A. Lowe
5

Andrew Top tiene la idea correcta:

1) Algoritmo de Djikstra 2) Alguna heurística de TSP.

Recomiendo la heurística de Lin-Kernighan: es una de las más conocidas para cualquier problema de NP Complete. Lo único que debe recordar es que después de expandir el gráfico nuevamente después del paso 2, es posible que tenga bucles en su ruta expandida, por lo que debe hacer un cortocircuito en ellos (observe el grado de vértices a lo largo de su ruta).

En realidad, no estoy seguro de qué tan buena será esta solución en relación con la óptima. Probablemente existan algunos casos patológicos relacionados con los cortocircuitos. Después de todo, este problema se parece MUCHO a Steiner Tree: http://en.wikipedia.org/wiki/Steiner_tree y definitivamente no puede aproximarse a Steiner Tree simplemente contrayendo su gráfico y ejecutando el de Kruskal, por ejemplo.

Ying Xiao
fuente
5

Este no es un problema de TSP ni NP-hard porque la pregunta original no requiere que los nodos de paso obligatorio se visiten solo una vez. Esto hace que la respuesta sea mucho, mucho más simple a la fuerza bruta después de compilar una lista de las rutas más cortas entre todos los nodos obligatorios a través del algoritmo de Dijkstra. Puede haber una mejor manera de hacerlo, pero una simple sería simplemente trabajar un árbol binario al revés. Imagina una lista de nodos [inicio, a, b, c, final]. Sume las distancias simples [inicio-> a-> b-> c-> final] esta es su nueva distancia objetivo a batir. Ahora intente [start-> a-> c-> b-> end] y si eso es mejor, establezca eso como el objetivo (y recuerde que vino de ese patrón de nodos). Trabajar al revés sobre las permutaciones:

  • [inicio-> a-> b-> c-> final]
  • [inicio-> a-> c-> b-> final]
  • [inicio-> b-> a-> c-> final]
  • [inicio-> b-> c-> a-> final]
  • [inicio-> c-> a-> b-> final]
  • [inicio-> c-> b-> a-> final]

Uno de ellos será el más corto.

(¿Dónde están los nodos 'visitados varias veces', si los hay? Simplemente están ocultos en el paso de inicialización de la ruta más corta. La ruta más corta entre ayb puede contener c o incluso el punto final. No es necesario que te importe )

Bjorke
fuente
Se requiere una visita única por la condición de que sea un camino más corto.
Aziuth
¿Eh? Estaba bastante seguro hace un minuto, pero sí, tienes razón. Evidentemente, no en un árbol con varias ramas. Pero, la cosa es que si abstraemos el problema en un nuevo gráfico que contiene solo los nodos mustpass que están completamente conectados, con los nodos que tienen la distancia del camino más corto en el gráfico original, llegamos a TSP. Entonces, ¿estás seguro de que no es NP-hard? Asumiría que en el problema general, la cantidad de nodos de paso obligado depende de la cantidad de nodos totales, y si, por ejemplo, el total es un polinomio del paso obligado, obtenemos la dureza NP, ¿verdad?
Aziuth
La ruta de, digamos, a-> b puede pasar por c. Así que ningún brnach impide a otro. Es solo una permutación.
bjorke
¿Si? Pero la permutación es O (n!) Si asumimos que la cantidad de nodos mustpass tiene alguna conexión con la cantidad de nodos totales, como "los nodos totales son un polinomio de nodos mustpass". Acabas de resolver TSP por fuerza bruta.
Aziuth
2

Teniendo en cuenta que la cantidad de nodos y bordes es relativamente finita, probablemente pueda calcular todos los caminos posibles y tomar el más corto.

Generalmente esto se conoce como el problema del viajante de comercio y tiene un tiempo de ejecución polinomial no determinista, independientemente del algoritmo que utilice.

http://en.wikipedia.org/wiki/Traveling_salesman_problem

Rafe
fuente
1

La pregunta habla de un pase obligatorio en CUALQUIER orden . He estado tratando de buscar una solución sobre el orden definido de los nodos obligatorios. Encontré mi respuesta, pero como ninguna pregunta en StackOverflow tenía una pregunta similar, la estoy publicando aquí para que la mayoría de las personas se beneficien de ella.

Si se define el orden o el paso obligado, puede ejecutar el algoritmo de dijkstra varias veces. Por ejemplo supongamos que usted tiene que empezar de spaso a través k1, k2y k3(en orden respectivo) y parada en e. Entonces, lo que podría hacer es ejecutar el algoritmo de dijkstra entre cada par consecutivo de nodos. El costo y la ruta vendrían dados por:

dijkstras(s, k1) + dijkstras(k1, k2) + dijkstras(k2, k3) + dijkstras(k3, 3)

Syed Hissaan
fuente
0

¿Qué tal si se usa la fuerza bruta en la docena de nodos que deben visitarse? Puede cubrir todas las combinaciones posibles de 12 nodos con bastante facilidad, y esto le deja con un circuito óptimo que puede seguir para cubrirlos.

Ahora su problema se simplifica a uno de encontrar rutas óptimas desde el nodo de inicio hasta el circuito, que luego sigue hasta que los ha cubierto, y luego encuentra la ruta desde allí hasta el final.

El camino final está compuesto por:

inicio -> ruta al circuito * -> circuito de nodos de visita obligada -> ruta al final * -> final

Encuentra los caminos que marqué con * así

Haga una búsqueda A * desde el nodo de inicio hasta cada punto del circuito para cada uno de estos haga una búsqueda A * desde el nodo siguiente y anterior en el circuito hasta el final (porque puede seguir el circuito en cualquier dirección) terminan con muchas rutas de búsqueda, y puede elegir la que tenga el costo más bajo.

Hay mucho espacio para la optimización almacenando en caché las búsquedas, pero creo que esto generará buenas soluciones.

Sin embargo, no se acerca a buscar una solución óptima, porque eso podría implicar dejar el circuito de visita obligada dentro de la búsqueda.

justinhj
fuente
0

Una cosa que no se menciona en ninguna parte es si está bien que el mismo vértice se visite más de una vez en la ruta. La mayoría de las respuestas aquí asumen que está bien para visitar el mismo borde varias veces, pero mi versión dada a la pregunta (un camino no debe visitar el mismo vértice más de una vez!) Es que es no está bien para visitar el mismo vértice dos veces.

Por lo tanto, se seguiría aplicando un enfoque de fuerza bruta, pero tendría que eliminar los vértices ya utilizados cuando intente calcular cada subconjunto de la ruta.

kirsch
fuente