ALGORITMOS PARA CALCULAR LA RUTA MÁS CORTA EN LA MALLA VIAL DE LA CIUDAD DE BOGOTÁ LEONARDO RODRIGUEZ CASTAÑEDA UNIVERSIDAD DE LOS ANDES PREGRADO Bogotá, Junio de 2005 ALGORITMOS PARA CALCULAR LA RUTA MÁS CORTA EN LA MALLA VIAL DE LA CIUDAD DE BOGOTÁ Autor: LEONARDO RODRIGUEZ CASTAÑEDA Asesores: FRANSISCO RUEDA FAJARDO RAFAEL ARMANDO GARCIA GOMEZ UNIVERSIDAD DE LOS ANDES FACULTAD DE INGENIERIA DEPARTAMENTO DE INGENIERIA DE SISTEMÁS Y COMPUTACION Desarrollado para cumplir con el requisito de grado para el titulo de Ingeniero de Sistemas y Computación Junio de 2005 INDICE OBJETIVOS ____________________________________________________________ 4 INTRODUCCION ________________________________________________________ 5 DESCRIPCION DEL PROBLEMA _________________________________________ 6 ALGORITMOS DE GRAFOS BASICOS ____________________________________ 8 ALGORITMO DE DIJKSTRA ___________________________________________ 11 Algoritmo de Dijkstra Formalmente _________________________________________________ 14 Análisis de Complejidad. __________________________________________________________ 17 ALGORITMO A-ESTRELLA ____________________________________________ 21 Algoritmo de A Estrella Básico Formalmente _________________________________________ 25 Análisis de Complejidad___________________________________________________________ 29 ESTRELLA POR SECTORES ____________________________________________ 32 Algoritmo de A Estrella Por Zonas Formalmente______________________________________ 39 Análisis de Complejidad. __________________________________________________________ 45 MODELO BÁSICO _____________________________________________________ 48 MODELO DE GRAFO DUAL ____________________________________________ 51 MODELO DE GRAFO DINÁMICO_______________________________________ 56 IMPLEMENTACION ___________________________________________________ 61 RESULTADOS OBTENIDOS Y CONCLUSIONES __________________________ 69 ANEXO I ______________________________________________________________ 78 ANEXO 2. _____________________________________________________________ 82 ANEXO 3. _____________________________________________________________ 85 ANEXO 4. _____________________________________________________________ 87 ANEXO 5. _____________________________________________________________ 90 ANEXO 6. _____________________________________________________________ 93 ANEXO 7. _____________________________________________________________ 95 ANEXO 8. _____________________________________________________________ 97 REFERENCIAS _______________________________________________________ 102 OBJETIVOS • Realizar la comparación entre dos propuestas de solución al problema de encontrar la ruta más corta entre dos puntos en un grafo real. Los algoritmos seleccionados son el algoritmo A-Estrella y un algoritmo propuesto basado en el algoritmo de A-Estrella, pero partiendo el grafo en sectores. • Los parámetros con respecto a los cuales serán comparados los algoritmos son: requerimientos de memoria, tiempo de procesamiento y por último la calidad de la respuesta obtenida. Es decir, serán comparados tanto en eficiencia como en eficacia. • La comparación del desempeño de los algoritmos seleccionados se debe realizar también contra un algoritmo que se ha demostrado que es óptimo en la mayoría de los requerimientos, este es el algoritmo de Dijkstra para el camino más corto. • El propósito principal de este proyecto es evaluar estos dos algoritmos, con el fin de conocer la viabilidad de ser aplicados en aplicaciones de georegerenciación para computación móvil. INTRODUCCION La teoría de grafos es muy significativa a la hora de hablar de estructuras de datos, dado que un grafo permite realizar la representación gráfica de una situación particular; es por esto que se pretende realizar un análisis detallado de la malla vial de Bogotá estudiando y analizando varios modelos y algoritmos que permitan determinar cuál es el modelo y el algoritmo aplicable para una aplicación móvil; aplicación que será descrita en este documento. Existen múltiples problemas y situaciones cotidianas que se pueden modelar por medio de grafos, esto permite que se tenga una mayor abstracción del modelo y de la solución del problema. El uso de grafos para modelar algún problema en particular permite determinar las relaciones existentes entre varios de los componentes de dicho problema, identificando las rutas o posibles caminos entre cada uno de los destinos correspondientes. En este documento se podrá encontrar la descripción del problema a tratar, los diferentes modelos y algoritmos analizados y la propuesta correspondiente para la aplicación de un algoritmo determinado al problema que se manejará: el problema de la malla vial de Bogotá. DESCRIPCION DEL PROBLEMA Se desea proponer un sistema de algoritmos que permitan encontrar la ruta más corta entre dos puntos dentro de la ciudad de Bogotá. La ruta debe ser calculada empleando la malla vial existente en la ciudad de Bogotá. Se desea tener en cuanta el sentido de las vías, así como las restricciones en los cruces que pueden ser realizados. El cálculo del costo de pasar por una vía depende de la capacidad de tráfico que puede soportar, así como de la longitud de la misma. Para el desarrollo de este proyecto se cuenta con una base de datos con información geográfica de la ciudad de Bogotá. Esta base de datos geográficos de la malla vial de la ciudad de Bogotá, fue proporcionada por la empresa Procalculo Prosis S.A. Esta base de datos fue desarrollada bajo el estándar del sistema de archivos de información geográfica Shapefile. Este sistema de archivos de información geográfica es propiedad de la compañía de investigación y desarrollo de software ESRI. De alguna forma esta información debe ser abstraída para construir un grafo, el cual debe representar la malla vial de la ciudad de Bogotá, y debe reflejar las métricas utilizadas en el modelo. Después de tener el modelo representado por un grafo, se deben utilizar algoritmos eficientes que solucionen el problema de calcular la ruta más corta entre dos puntos en este grafo. Para calcular la ruta más corta entre dos puntos dentro de la ciudad se propone analizar y comparar dos algoritmos. El primer algoritmo a ser utilizado y estudiado es el algoritmo de grafos A-Estrella, en el cuál cada intersección de la malla vial es un nodo del grafo. El segundo algoritmo es una modificación del algoritmo A-Estrella, pero trabajando áreas geográficas dentro de la ciudad de Bogotá, con sus diferentes interconexiones, estas áreas pueden ser los diferentes barrios de la ciudad, o las localidades, y las interconexiones son los caminos que interconectan cada uno de estos barrios. Los algoritmos A-Estrella y A-Estrella por sectores deben ser comparados entre si en cuanto a requerimientos de memoria, tiempo de procesamiento, y calidad de la respuesta obtenida. Dada la naturaleza de estos algoritmos, es necesario comparar los resultados contra un algoritmo óptimo en la mayoría de los aspectos. Este algoritmo es el del camino más corto de Dijkstra. El algoritmo de Dijkstra se ha demostrado que es óptimo, y que presenta un buen desempeño en la mayoría de los casos. En el caso de grafos grandes, este algoritmo requiere de un gran tiempo de procesamiento para encontrar la respuesta, es por esto que no escogimos este algoritmo para ser aplicado en este problema. Hay que notar que la maya vial de la ciudad de Bogotá tiene alrededor de 80 mil intersecciones y más de 140 mil segmentos de camino vehicular, luego, dado que el algoritmo de Dijkstra tiene complejidad espacial S (n) y complejidad temporal T (n2), el tiempo estimado de ejecución sería de 15 segundos para calcular un camino que recorra un cuarto de la ciudad, lo que lo hace inaplicable en el caso de dispositivos móviles. El propósito principal de este proyecto es evaluar estos dos algoritmos, con el fin de conocer la viabilidad de ser aplicados en aplicaciones de georegerenciación para computación móvil. ALGORITMOS DE GRAFOS BASICOS Un grafo es una estructura matemática que puede ser usada para modelar redes y una gran cantidad de otros sistemas donde las relaciones entre los objetos juegan un papel muy importante. Un grafo está compuesto por un conjunto de vértices y otro de arcos. Los arcos unen un vértice con otro. El conjunto vértices se denota con “V”, y el de arcos con “E”. Los grafos pueden ser dirigidos o no dirigidos. Un grafo dirigido, es uno en el cuál los arcos sólo tienen una dirección determinada, mientras que uno no dirigido, los arcos tienen dirección en ambos sentidos. Un arco puede tener un costo o impedancia asociado, el cuál representa el costo requerido de pasar de un vértice a otro vértice, utilizando dicho arco. Un grafo está denotado de la siguiente forma: G = (V, E) Donde V es el conjunto de vértices del grafo, y E es el conjunto de arcos. En este trabajo vamos a modelar la malla vial de la ciudad de Bogotá en forma de grafo. El grafo que va a modelar la malla vial tiene las siguientes características: Primero que todo es un grafo planar, lo que significa que el grafo se puede dibujar sobre un plano si que un arco cruce encima de otro. Esta restricción que estamos definiendo en el grafo se debe a que la base de datos geografica de la malla vial de la ciudad de Bogotá, que estamos usando presenta esta particularidad. Otras bases de datos geofraficas de mallas viales no se pueden representar con grafos planares, ya que inclyen información sobre tuneles y puentes. Este grafo es dirigido y cuenta con un conjunto de aproximadamente 80 mil nodos y 130 mil arcos. Sobre este grafo queremos calcular la ruta o camino más corto entre dos nodos. Existen numerosos algoritmos de grafos, para encontrar el camino de un nodo a otro nodo. Si queremos encontrar el camino más corto de un nodo a otro nodo, el algoritmo de Dijkstra para el camino más corto, garantiza que si se encuentra un camino de un nodo a otro nodo dado, este será el camino más corto. Este es un algoritmo muy costoso en cuanto a complejidad espacial que es S (n) y tiempo de proceso, ya que tiene complejidad temporal T (n2). Más adelante profundizaremos sobre este tema. Para disminuir el tiempo necesario para encontrar el camino más corto, entre dos nodos dados, existen otros algoritmos que utilizan heurística, o información adicional, para poder solucionar el problema. El algoritmo que presenta mejores resultados es el A* o A-Estrella, el cuál utiliza una función de distancia entre el nodo inicial y el nodo objetivo. Lo interesante de este algoritmo es que esta distancia, no debe ser exacta para que el algoritmo funcione correctamente, únicamente debe ser una distancia aproximada. Muchas veces en la vida real, encontrar la respuesta óptima a un problema no siempre es lo más importante. En muchas ocasiones es más importante obtener una respuesta “buena” rápidamente, aunque esta respuesta no sea la óptima. El algoritmo BFS (Best First Search) es una optimización del algoritmo breadth first search que es utilizado en grafos arborescentes. Este algoritmo puede ser utilizado en cualquier tipo de grafo. Este algoritmo ordena los arcos de un nodo utilizando una heurística. La heurística utilizada en este algoritmo es la proximidad a la que se encuentra el nodo objetivo. El arco que conduzca más cerca al nodo objetivo es colocado en la primera posición y así sucesivamente. Este algoritmo aunque encuentra un camino mucho más rápidamente que el algoritmo de Dijkstra, no necesariamente encuentra el camino optimo. En este proyecto vamos a trabajar con tres algoritmos. El primer algoritmo con el que vamos a trabajar es el algoritmo de Dijkstra, el cual garantiza que siempre encuentra la respuesta óptima al problema de calcular la ruta más corta entre dos nodos en un grafo. El otro algoritmo con el que vamos a trabajar es el algoritmo A-Estrella. Este algoritmo aunque no siempre encuentra la respuesta óptima, la respuesta es obtenida mucho más rápidamente que la obtenida por el algoritmo de Dijkstra. El último algoritmo fue propuesto por nosotros, y consiste en dividir el grafo en zonas, y sobre estos sectores calcular el camino más corto, usando una versión del algoritmo A-Estrella. Este algoritmo lo vamos a llamar en este documento como A-Estrella por sectores. A continuación vamos a hacer el análisis de estos tres algoritmos, iniciando con el algoritmo de Dijkstra, y terminado con el algoritmo A-Estrella por sectores. ALGORITMO DE DIJKSTRA El algoritmo de Dijkstra es el algoritmo más simple para encontrar la ruta más corta entre un nodo y todos los demás nodos pertenecientes al grafo G. Este algoritmo supone que el grafo es dirigido y que los costos de los arcos son todos positivos. El problema de la ruta más corta se puede resolver utilizando programación lineal sin embargo, debido a que el método simplex es de complejidad exponencial, se prefiere utilizar algoritmos que aprovechen la estructura en red que se tiene para estos problemas. El problema del camino más corto escrito en forma de programación lineal es presentado a continuación. Supongamos que los nodos del grafo G= (V, E), están numerados de 1 a n, N = {1, 2,…, n}, y tambien tenemos una matriz L que contiene los costos de todos los arcos del grafo. La matriz L esta definida de la siguiente forma. L [i, j] = 0 si y solo si i = j, L[i, j] ≥ 0 para i ≠ j si existe el arco (i, j), y L[i, j]= ∞ si no existe el arco (i, j). El principio de optimalidad dice que si el nodo (k) hace parte del camino más corto entre el nodo (i) y el nodo (j), entonces el camino entre el nodo (i) y el nodo (k) y el camino entre el nodo (k) y el nodo (j) tambien tiene que ser optimal. Si se observa la estructura de una red se puede determinar que una red de comunicaciones involucra un conjunto de nodos conectados mediante arcos, que transfieren paquetes de datos desde determinados nodos origen a otros nodos destino. La forma más común para seleccionar la trayectoria (o ruta) de estos paquetes de datos, se basa en la formulación de la ruta más corta. En particular a cada arco se le asigna un escalar positivo el cuál se puede ver como su longitud, costo, o capacidad máxima de tráfico. Este problema particular puede ser solucionado utilizando algoritmos de flujo maximo. Un algoritmo de trayectoria más corta, enruta cada paquete de información a lo largo de la trayectoria de longitud mínima (ruta más corta) entre los nodos origen y destino. Hay varias formas posibles de seleccionar la longitud de los enlaces. La forma más simple es que cada enlace tenga una longitud unitaria, en cuyo caso, la trayectoria más corta es simplemente una trayectoria con el menor número de enlaces. De una manera más general, la longitud de un enlace puede depender de su capacidad de transmisión y su carga de tráfico. La solución siempre es encontrar la trayectoria más corta para el problema dado. Esperando que dicha trayectoria contenga pocos enlaces no congestionados; de esta forma los enlaces menos congestionados son candidatos a pertenecer a la ruta. Hay algoritmos de enrutamiento especializados que también pueden permitir que la longitud de cada enlace cambie en el tiempo, dependiendo del nivel de tráfico de cada enlace. De esta forma un algoritmo de enrutamiento se debe adaptar a sobrecargas temporales y enrutar paquetes omitiendo los nodos congestionados. Dentro de este contexto, el algoritmo de ruta más corta para enrutamiento opera continuamente, determinando la trayectoria más corta con longitudes que varían en el tiempo. El algoritmo de Dijkstra para ruta más corta, en términos generales, encuentran la ruta más corta entre dos nodos, inicial a y final z, de la siguiente manera: Todos los nodos de la red son etiquetados con números. Al principio, todos tienen la etiqueta ∞ excepto el nodo inicial a que tiene la etiqueta 0. Los arcos tienen un peso w (i, j) que representa la distancia del enlace (i, j). El algoritmo de Dijkstra genera un conjunto con todos los nodos del grafo. De este conjunto selecciona el nodo que tenga la etiqueta con el menor valor. Este nodo que ha sido seleccionado queda marcado permanentemente, y es extraido del conjunto inicial y es adicionado al conjunto de nodos marcados permantentemente. Posteriormente se actualizan las etiquetas de los sucesores del nodo seleccionado, de tal forma que el valor de la etiqueta del nodo sucesor nj es igual al valor de la etiqueta del nodo seleccionado ni más el costo w (i,j). Esta actualizacion se realiza unicamente si el sucesor cumple con las siguientes dos condiciones: el nodo no se encuentren en el conjunto de nodos marcados permanentemente, y el valor de la etiqueta del nodo es mayor al valor de la etiqueta actualizada. Dicho de otra manera más específica, el algoritmo de Dijkstra se basa fundamentalmente en marcar los nodos de una forma especial. La forma que emplea el algoritmo de Dijkstra para marcar los nodos es la siguiente: El nodo que va a ser marcado es seleccionado entre todos los nodos candidatos, es decir los que no han sido marcados. La condición que debe cumplir el nodo para ser seleccionado y marcado es que debe tener el menor costo acumulado del camino desde el nodo origen. En el caso en que existan varios nodos con el mismo costo mínimo, se puede seleccionar entre ellos cualquiera al azar, ya que más adelante serán seleccionados y marcados. Esta simple regla de selección nos garantiza que todos los nodos que han sido marcados, tienen el costo mínimo del camino entre ellos y el nodo origen. El algoritmo de Dijkstra es el único algoritmo que garantiza que una vez que un nodo ha sido marcado, también se ha encontrado el camino mínimo entre el nodo origen y este nodo. Los demás algoritmos sólo garantizan que una vez que termina el algoritmo se ha encontrado el camino mínimo. A continuación vamos a explicar el algoritmo de Dijkstra. Primero vamos a escribirlo formalmente y después vamos a hacer un análisis de complejidad. Algoritmo de Dijkstra Formalmente Dijkstra (G, s, W) {Pre1: G = (V, E) ∧ s ∈V ∧ W: EÆℜ +} 1: Inicializar (G, s, W) 2: MÅ∅ 3: Q Å Nodos (G) {P1: Q = V - M} {T1: Tam (Q) +1} 4: DO (Q ≠∅) Æ 5: u Å Extraer-Mínimo-W (Q, W) 6: M Å M ∪ {u} 7: S Å Sucesores (u, G) {Pre2: S: contiene los sucesores de u} {P2: S ⊂ V ∧ (∀ v: v ∈ S| : (∃ e : e ∈ E| : e(u,v))) } {T: Tam (V) + 1} 8: DO (S ≠ ф) Æ 9: v Å Extraer-Cualquiera (S) 10: IF Contiene (M, v) Æ 11: skip Not Contiene (M, v) ÆRelajar (u, v, W) FI OD {Post2: sucesores de u ∩ W tienen costos relajados} OD {Post1: W: tiene los costos del camino mínimo desde s al resto de nodos del grafo} El algoritmo presentado anteriormente es el algoritmo de Dijkstra, que calcula únicamente el costo del camino mínimo entre el nodo origen y los demás nodos del grafo G. Los parámetros de entrada de este algoritmo son un grafo G Dirigido con todos los costo de sus arcos positivos; un nodo origen s, que pertenece al grafo G, y por ultimo un parámetro de salida W el cuál contiene el costo mínimo entre el nodo origen s, y todos los demás nodos del grafo G. En la primera línea de este algoritmo asignamos un costo de camino infinito a todos los nodos de G, menos al nodo inicial al cuál le asignamos un costo de camino igual a cero, esto escrito formalmente es: (∀ n: n ∈ V | n ≠ s: W [n] = ∞) ∧ W [s] = 0 En la segunda línea del algoritmo dejamos vacío el conjunto de los nodos marcados M. En la tercera línea del algoritmo colocamos todos los nodos del grafo en el conjunto Q. En la cuarta línea del algoritmo tenemos un ciclo, el cuál va a ser ejecutado hasta que el conjunto Q no tenga elementos. Como el conjunto Q tiene todos los nodos del grafo G, este ciclo se ejecutara exactamente m veces donde m es el número de nodos que tiene el grafo G. La quinta línea de código es la parte fundamental de nuestro algoritmo. En esta línea es donde se selecciona y se extrae el nodo que va a ser marcado del conjunto de nodos Q. Anteriormente se explicó por qué esta selección del nodo candidato a ser marcado es importante para garantizar que se encuentra el camino mínimo entre el nodo origen y los nodos marcados. En la sexta línea, agregamos el nodo que fue seleccionado (u) para ser marcado permanente, al conjunto de nodos marcados M. En la siguiente línea, generamos un conjunto de nodos V, que son sucesores del nodo u. En la octava línea del algoritmo tenemos otro ciclo, que va a ser ejecutado hasta que el conjunto S este vacío. Este ciclo se ejecutará un número variable de veces ya que el conjunto S puede tener diferente cantidad de nodos. El número de elementos que contiene el conjunto S, depende del grado de conectividad del grafo, entre más alto el grado de conectividad de este, mayor será el número de elementos que tendrá el conjunto S. En la novena, décima y onceava línea, lo que hacemos es seleccionar y extraer un nodo v del conjunto del conjunto S, y lo relajamos. Este proceso de relajación consiste en lo siguiente: Si el nodo v, pertenece al conjunto de nodos marcados M, no hacemos nada, de lo contrario, si el costo del camino W [v] es mayor al costo del camino W [u] + Costo (u, v) actualizamos el costo de W [v]. Este algoritmo de relajación se presenta a continuación. Relajar (u, v, W) {Pre: u, v ∈ nodos (G) ∧ v ∉ M} IF W [v] > W [u] + Costo (u, v) Æ W [v] = W [u] + Costo (u, v) W [v] < W [u] + Costo (u, v) Æ Skip FI {Post: W[v] tiene el costo mínimo parcial} Con esto terminamos el algoritmo de Dijkstra. A continuación vamos a hacer un análisis de complejidad de este algoritmo. Análisis de Complejidad. La complejidad del algoritmo de Dijkstra depende mucho de la estructura de datos seleccionada para la implementación del algoritmo como veremos más adelante. El problema de la ruta más es tipo P, es decir que tiene una solución en tiempo polinomial. Por lo tanto un algoritmo que solucione este problema tiene una comlejidad en el mejor de los casos polinomial. Como mencionamos anteriormente, el algoritmo de Dijkstra no termina hasta que se hallan marcado todos los nodos del grafo G. Por lo tanto la complejidad hasta el momento es de orden n (donde n es el número de nodos del grafo G), ya que tenemos que realizar n veces un sub-algoritmo para cada nodo. El sub-algoritmo está comprendido entre las líneas 5 y 11 del algoritmo de Dijkstra. En la línea 5 tenemos que encontrar el nodo con menor costo de camino en el conjunto Q. Dependiendo de la estructura de datos utilizada para este conjunto Q, la complejidad de este algoritmo cambia. Si por ejemplo tenemos una lista de nodos desordenada, tenemos que buscar sobre todos los elementos de esta para encontrar el nodo que tenga el mínimo costo. Por lo tanto este algoritmo tendría una complejidad de O (m) donde m es el número de elementos en el conjunto Q. hay que tener en cuenta que el número de elementos de este conjunto disminuye cada vez que es ejecutado este algoritmo. Si en vez que esto utilizamos una pila ordenada por el costo del camino la complejidad de este algoritmo sería O (1), ya que el primer elemento es el que tiene el menor costo del camino. En la línea 6 tenemos que adicionar un elemento al conjunto de nodos marcados M. La estructura de datos seleccionada para el conjunto M es muy importante como se vera más adelante. La complejidad de este algoritmo puede variar según la estructura seleccionada entre O (1) en una lista cualquiera a O (n). En la línea 7 tenemos que generar un conjunto con los sucesores de un nodo especifico. Una vez más la estructura de datos es muy importante. En este caso es la estructura de datos utilizada para almacenar el grafo. Si utilizamos una lista de adyacencias, la complejidad es O (1). En la línea 8 tenemos un ciclo que se debe ejecutar otro sub-procedimiento hasta que el conjunto V sea vacío. La cota de este ciclo es el número de elementos del conjunto V. El conjunto V contiene los sucesores de un nodo específico. Como también dijimos anteriormente el número de sucesores depende del grado de conectividad del grafo. Si tenemos una conectividad alta, el número de elementos va a ser también alta. Para grafos con una baja conectividad, podemos decir que este sub-algoritmo se ejecutara sólo una vez. En la línea 9 si para el grafo utilizamos una lista de adyacencias, entonces el conjunto V es por lo tanto también una lista. El procedimiento que realizamos en esta línea es seleccionar y extraer un elemento de este conjunto. La complejidad para esto es O (1). En la línea 10 del algoritmo de Dijkstra, tenemos que buscar dentro del conjunto de nodos marcados M, y responder si se encuentra o no un nodo especifico. Dependiendo de la estructura de datos seleccionada, la complejidad de esta instrucción cambia. La complejidad de esta instrucción puede variar entre O (1) y O (n). Como nos podemos dar cuenta la estructura de datos de del conjunto M es mejor seleccionarla para que tenga una complejidad baja ya que si el grafo tiene una conectividad alta, y esta instrucción también tiene una complejidad alta, podríamos tener en el peor de los casos un algoritmo con complejidad O (n2 m). En la línea 11, tenemos que hacer el algoritmo de relajación. Para calcular la complejidad de esta instrucción tenemos que tener en cuenta la estructura de datos utilizada para almacenar los costos de los caminos de todos los nodos del grafo. La mejor estructura para este caso es un arreglo, el cuál nos garantizaría una complejidad de O (1). La estructura que se escoja en esta parte también es muy importante como en el caso anterior. Este algoritmo aunque es óptimo, no resulta práctico para aplicaciones donde el tiempo de respuesta es crucial. Cuando el tamaño del grafo muy grande, el algoritmo de Dijkstra presenta tiempos de respuesta que no pueden ser tolerados, por ejemplo, cuando el numero de nodos es n=100.000, y la longitud del camino es d = 100, se espera que se realicen 10.000 operaciones correspondientes a marcar los nodos definitivamente, que con un procesador Pentium III de 1.5 GHz toma aproximadamente 13 segundos. Para sortear este inconveniente existen otros algoritmos que son mucho más rápidos. Estos algoritmos cambian velocidad por la calidad de la respuesta. Estos algoritmos usualmente utilizan información adicional para encontrar una solución, que no siempre es la óptima. El mejor de todos estos algoritmos es el A-Estrella, el cuál puede utilizar diferentes heurísticas. A continuación vamos a hablar sobre el algoritmo A-Estrella. ALGORITMO A-ESTRELLA El algoritmo A-Estrella fue desarrollado en 1968 para combinar el enfoque heurístico del algoritmo BFS, y el enfoque optimal del algoritmo de Dijkstra. El algoritmo A-Estrella es el único algoritmo heurístico, que garantiza que encuentra el camino más corto entre dos nodos dados. Este algoritmo es ampliamente utilizado en video juegos, y en aplicaciones de inteligencia artificial, donde la velocidad de respuesta es lo principal. Los juegos de video que más utilizan este algoritmo son los de estrategia en tiempo real, en los cuáles los personajes deben desplazarse de un punto a otro por un mapa determinado. También es utilizado para implementar la inteligencia artificial de los oponentes controlados por el computador, el cuál puede tener en cuenta muchos aspectos, como la cercanía con los oponentes y el rango de visión entre otros. El algoritmo A-Estrella funciona de la siguiente forma. El algoritmo tiene dos conjuntos de vértices, a uno de estos conjuntos lo vamos a llamar Disponibles, y al otro lo vamos a llamar Analizados. En el conjunto Disponibles se colocan los nodos que son candidatos a ser examinados para escoger el camino, y en el conjunto Analizados están los nodos que ya han sido examinados, y que hacen parte de la ruta seleccionada. Inicialmente el conjunto Disponibles contiene únicamente el nodo inicial, y el conjunto Analizados está vacío. Para cada nodo n se definen los siguientes valores: g (n): Es el costo de llegar del nodo inicial al nodo n. h (n): Es un estimado del costo de llegar del nodo n al nodo Final. Este costo es calculado a partir de la función heurística utilizada. F (n) = g (n) + h (n): Este es un estimado del costo de la solución que pasa por el nodo n. El algoritmo A-Estrella es similar al algoritmo de Dijkstra excepto por una pequeña diferencia sutil, que consiste en la inclusión de una función heurística, que tiene como objetivo modificar nuestro criterio de selección del nodo candidato a ser marcado. Como se vera más adelante podemos parafrasear el algoritmo de Dijkstra, para que se vea similar al algoritmo A-Estrella. El algoritmo A-Estrella a diferencia del algoritmo de Dijkstra, no siempre encuentra el camino óptimo, pero tampoco se desvía mucho de la respuesta óptima. La precisión de este algoritmo depende totalmente de la función heurística utilizada. Si la función heurística utilizada es igual a cero, es decir no se utiliza heurística, el algoritmo A-Estrella es exactamente igual al algoritmo de Dijkstra. El algoritmo de A-Estrella presenta su mayor desempeño en cuanto a velocidad y optimalidad, cuando la función heurística nos dice cuanto cuesta llegar desde un nodo dado hasta el nodo destino. Este caso es imposible de lograr de manera efectiva, ya que para esto necesitamos saber cuál es el costo de los caminos entre todos los nodos, lo que requiere haber calculado previamente el costo de la ruta óptima entre todos los nodos, y tenerla almacenada. Por lo tanto para poder solucionar este problema, tenemos que haber solucionado el mismo problema previamente para todas las parejas de nodos del grafo. Adicionalmente la memoria requerida para almacenar las respuestas crece de manera cuadrática, por lo cuál es poco eficiente. Si la función heurística subestima por mucho, la distancia entre un nodo dado y el nodo destino, el tiempo de respuesta se degrada considerablemente. En el peor de los casos obtendremos el mismo tiempo de respuesta que el algoritmo de Dijkstra, pero tendremos la respuesta óptima. Si por el contrario la función heurística sobreestima la distancia entre un nodo dado y el nodo destino, también se degrada el tiempo de respuesta, y al mismo tiempo disminuye la calidad de la respuesta obtenida. Más adelante vamos a hablar sobre las funciones heurísticas que pueden ser utilizadas con este algoritmo. Las funciones heurísticas que son usadas por el algoritmo A-Estrella dependen de las características propias del grafo sobre el cual va a ser aplicado. La funcion heurística es utilizada para estimar el costo óptimo de un camino entre dos nodos. Hay que resaltar que esta funcion es utilizada solo para calcular un estimado del costo del camino, y que no altera de ninguna forma el costo del camino real. En nuestro caso particular, el grafo con el que estamos trabajando es una malla vial real. Uno de los criterios que definimos para fijar el costo de los arcos en este grafo es la distancia geográfica real, que corresponde a la distancia euclidiana entre dos nodos adyacentes. El otro criterio es la capacidad de la via que esta representando el arco. En esta malla vial se definen unicamente dos tipos carreteras: las vias recidenciales y las vias principales. Para este tipo de grafos los cuáles están relacionados geométricamente, en este caso por la distancia euclidiana, se utilizan comúnmente dos tipos de funciones heurísticas. Las dos funciones heurísticas son la longitud Euclidiana y la longitud de Manhattan. La longitud Euclidiana es la distancia en línea recta entre dos puntos. Esta heurística presenta mejores resultados, cuando los costos de los arcos dependen únicamente de la longitud de los mismos. Cuando el costo de los arcos deja de depender mucho de la distancia correspondiente, esta heurística genera errores mucho más grandes. La función heurística euclidiana puede ser calculada de la siguiente forma. hE (inode, endnode) = ( X inode − X endnode ) 2 + (Yinode − yendnode ) 2 En la anterior ecuación, Xinode y Yinode son las coordenadas del nodo (i), y Xendnode y Yendnode son las coordenadas del nodo destino. Si los costos de los arcos dependen totalmente de la distancia, esta función heurística nos puede aproximar muy bien cuál es el costo para llegar de un nodo a otro. La función heurística de Manhattan es la suma de las distancias de los puntos en las coordenadas X y Y. Esta distancia corresponde a la distancia del camino mas corto, utilizando otra metrica. La función heurística de Manhattan es la siguiente. hM ( inode , endnode ) =| X inode − X endnode | + | Yinode − Yendnode | En la anterior ecuación, Xinode y Yinode son las coordenadas del nodo i, y Xendnode y Yendnode son las coordenadas del nodo destino. Aunque la función heurística utilizada no aproxime correctamente el costo del camino entre dos nodos dados, el camino que encontrará el algoritmo de A-Estrella se aproxima mucho al camino óptimo. Adicionalmente estamos reduciendo considerablemente el espacio de búsqueda, al buscar prioritariamente en los nodos que están en la dirección del camino hacia el nodo destino. En el algoritmo de Dijkstra el espacio de búsqueda crece radialmente, y por consiguiente el tiempo de búsqueda es mayor. Hay que resaltar que la funcion heurística de Manhattan entre una pareja de nodos dados es mayor o igual a la funcion heurística Euclidiana entre los nodos de esta pareja. h M (i, j) ≥ h E (i, j) De la anterior relación podemos intuir que si utilizamos la heurística de Manhattan podemos conducir al algoritmo A-Estrella a obtener resultados diferentes al óptimo, con una probabilidad igual o mayor que si utilizamos la funcion heurística Euclidiana. A continuación vamos a presentar el algoritmo A-Estrella formalmente, para poder entender mejor su funcionamiento. Algoritmo de A Estrella Básico Formalmente A-Estrella (G, nodo-Inicial, nodo-Final) {Precondición Q1: G = (V, E) ∧ nodo-Inicial, nodo-Final ∈V) 1: Disponibles: = φ 2: Analizados: = φ 3: Nodo-Actual: = Configurar-Nodo (nodo-Inicial, 0) 4: Nodo-Actual: = Asignar-Padre (nodo-Inicial, nodo-Inicial, Nodo-Actual) 5: Disponibles: = Disponibles ∪ Nodo {Invariante P1: Disponibles + Analizados ≤ G ∧ (∀ v: v ∈ Analizados | : W(nodoInicial, v) ≥ WSP(nodo-Inicial, v)) } {Cota T1: G – (Disponibles + Analizados) +1} 6: DO Disponibles ≠ φ Æ 7: 8: Nodo-Actual: = Extraer-Nodo-Menor (Disponibles) Analizados: = Analizados ∪ Nodo-Actual 9: IF Nodo-Actual. Nodo = nodo-Final Æ 10: Disponibles: = φ 11: Nodo-Actual. Nodo ≠ nodo-Final Æ 12: Sucesores: = Obtener-Sucesores (Nodo-Actual. Nodo) {Precondición Q2: (∀ v: v∈ Sucesores | : Adyacente(Nodo-Actual, v) )} 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: Contador: = 0 {Invariante P2: (∀ v: v ∈ Analizados | : W(nodo-Inicial, v) ≥ WSP(nodoInicial, v))} {Cota T2: Sucesores. Número – Contador +1} DO Contador < Sucesores. Número Æ Sucesor: = Sucesores [Contador] Contador: = Contador +1 IF Contiene (Analizados, Sucesor) Æ SKIP NOT Contiene (Analizados, Sucesor) Æ Sucesor-Actual: = Configurar-Nodo (Sucesor, NodoActual. Costo + Costo (Nodo-Actual. Nodo, Sucesor)) Sucesor-Actual: = Asignar-Padre (Sucesor, Nodo-Actual. Nodo, Sucesor-Actual) IF NOT Contiene (Disponibles, Sucesor) Æ Disponibles: = Disponibles ∪ Sucesor-Actual Contiene (Disponibles, Sucesor) Æ IF Disponibles. G (Sucesor) > Nodo-Actual. Costo + Costo (Nodo-Actual. Nodo, Sucesor)Æ Eliminar (Disponibles, Sucesor) Disponibles: = Disponibles ∪Sucesor-Actual FI FI FI FI OD {Poscondición R2: (∀ v: v ∈ Analizados | : W(nodo-Inicial, v) ≥ WSP(nodo-Inicial, v)) } OD {Poscondición R1: Analizados: contiene el SP entre nodo-Inicial y el ultimo nodo. ∧ Disponibles: contiene nodos con G mínimo a partir del nodo-Inicial.} En la primera y segunda línea del algoritmo, dejamos vacíos los dos conjuntos Analizados, y Disponibles. El conjunto Disponibles cumple con la misma función que el conjunto Q del algoritmo de Dijkstra, y el conjunto Analizados cumple el mismo propósito del conjunto M de Dijkstra. En la tercera y cuarta línea se configura el nodo inicial. Se asigna el costo del camino a cero, y se coloca como padre del nodo inicial el mismo nodo. Cada uno de los nodos que van a estar contenidos dentro de los conjuntos Disponibles y Analizados, tienen asociados un nodo padre y un costo acumulado g del camino desde el nodo inicial. Y por ultimo en la quinta línea adicionamos el nodo inicial al conjunto de nodos disponibles. En la sexta línea del algoritmo tenemos un ciclo que va a ser ejecutado hasta que no queden elementos dentro del conjunto Disponibles. A diferencia del caso del algoritmo de Dijkstra en este no sabemos exactamente cuantas veces se va a ejecutar este procedimiento, lo único que podemos afirmar es que el peor de los casos se va a ejecutar n veces donde n es el número de nodos del grafo G, que es lo mismo que hace el algoritmo de Dijkstra. En el mejor de los casos, este procedimiento sólo se repite k veces donde k es el número de nodos que comprenden el camino óptimo entre el nodo origen y el nodo destino. La séptima línea del algoritmo A-Estrella es la única diferencia entre el algoritmo de Dijkstra y este. En esta instrucción se selecciona y se extrae el nodo que tenga menor función F, del conjunto de nodos disponibles. Como mencionamos anteriormente si la heurística utilizada para calcular esta función F es cero, esta instrucción sería equivalente a la del algoritmo de Dijkstra. En la octava línea adicionamos el nodo que fue seleccionado anteriormente al conjunto de nodos Analizados. En esta instrucción a diferencia del algoritmo de Dijkstra, es que no se puede garantizar que el nodo que va a ser adicionado tiene un costo y un camino optimo desde el nodo origen, pero si que es uno muy aproximado al óptimo. En el único momento en el cuál podemos afirmar esto es cuando no estamos usando una función heurística. En la novena y décima línea del algoritmo, verificamos que el nodo que acabamos de seleccionar es el nodo destino, en cuyo caso hemos encontrado un camino entre el nodo origen y el nodo destino. Para terminar el algoritmo eliminamos todos los nodos contenidos en disponibles. En la línea doceava obtenemos los sucesores del nodo que ha sido marcado. En las siguientes líneas, la 13 y la 14 tenemos un ciclo que realiza un procedimiento que debe ser ejecutado m veces donde m es el número de sucesores del nodo marcado. Al igual que para el algoritmo de Dijkstra, si el grado de conectividad es bajo, podemos suponer que los procedimientos ejecutados dentro de este ciclo se ejecutan sólo una vez. En las líneas de la 15 a la 17 del algoritmo, estamos seleccionando un sucesor cualquiera de nuestro conjunto de sucesores previamente definido, y en la última línea, verificamos que este sucesor no se encuentre en el conjunto Analizados el cuál contiene los nodos marcados. Si el sucesor no se encuentra entre los nodos marcados realizamos las instrucciones de las líneas 18 a la 26. En estas líneas configuramos el nodo sucesor que seleccionamos anteriormente. Una vez configurado el sucesor, verificamos que el nodo no se encuentre en el conjunto de nodos Disponibles, en tal caso lo agregamos a este conjunto. Si el sucesor ya se encuentra en el conjunto Disponibles, verificamos que el nodo que esta en este conjunto tenga un costo de camino acumulado desde el nodo inicial g, mayor al costo g del sucesor que estamos analizando. Si ocurre esto, tenemos que reemplazar el nodo que se encuentra en el conjunto Disponibles, por el sucesor actual. A continuación vamos a hacer un análisis de la complejidad del algoritmo AEstrella para podernos dar una idea de su comportamiento. Análisis de Complejidad El algoritmo A-Estrella al igual que el algoritmo de Dijkstra se basan principalmente en marcar los nodos de menor costo, en uno hacia la dirección del nodo destino y en el otro en todas las direcciones. En este tipo de algoritmos el número de nodos visitados durante la búsqueda del camino más corto es un buen indicador del tamaño del espacio de búsqueda. Si un algoritmo de búsqueda del camino más corto basado en marcar los nodos, visita menos nodos durante la búsqueda de la respuesta, podemos decir que en términos de velocidad de procesamiento es más eficiente. El número de nodos visitados depende de la profundidad (d), que corresponde al número de nodos que hacen parte del camino más corto entre el nodo origen y el nodo destino y también depende de un factor de ramificación (b), que corresponde al numero de arcos promedio que salen de los nodos del grafo. El número de nodos visitados por el algoritmo BFS (Best First Search) durante la búsqueda de la respuesta es del orden de O (bd). Este crecimiento exponencial en el número de nodos visitados es conocido como explosión combinatoria, y es el principal obstáculo para calcular el camino más corto en grafos grandes. El algoritmo A-Estrella reduce el número de nodos visitados durante la búsqueda de la respuesta a un orden de O (b ed), donde be es el factor efectivo de ramificación. Esta reducción en el número de nodos visitados se debe a que reducimos nuestro espacio de búsqueda, al no tener en cuenta los nodos que no se encuentran en la dirección del nodo destino. En cuanto a la complejidad del algoritmo podemos decir que esta varía considerablemente dependiendo de la función heurística utilizada. La complejidad puede ser tan alta como el propio algoritmo de Dijkstra, y tan baja como el algoritmo BSF. La complejidad del algoritmo de Dijkstra fue tratada anteriormente. Tenemos que la complejidad del algoritmo BFS es O (n) donde n es la profundidad del camino óptimo. Aunque a primera vista este algoritmo parece solucionar eficientemente el problema del camino más corto, esto no es asi ya que el algoritmo BFS no garantiza que el camino encontrado corresponde al camino óptimo. A continuación vamos a presentar un modelo propuesto por nosotros el cuál lo llamamos A-Estrella por sectores. ALGORITMO A-ESTRELLA POR SECTORES El algoritmo que va a ser presentado a continuación fue propuesto como una alternativa para resolver el problema de la ruta más corta en grafos grandes. La idea general es dividir el grafo total en sectores o zonas más pequeñas. El propósito general de esta propuesta es analizar el desempeño de este modelo en grafos grandes y con base a los resultados obtenidos, decidir la viabilidad de aplicarlo en computación móvil. La idea que generó este modelo fue la de reducir el espacio de búsqueda. Para lograr esta reducción en el espacio de búsqueda se propuso segmentar el grafo en zonas interconectadas entre si. En nuestro caso particular las zonas en las que está dividido el grafo son los barrios o localidades de la ciudad de Bogotá. Una vez que el grafo total ha sido dividido en zonas más pequeñas, nuestra propuesta es aplicar un algoritmo que calcule la ruta más corta entre dos nodos del grafo, pero calculando la trayectoria mínima primero usando las zonas y por último refinando la trayectoria. El algoritmo que escogimos para aplicar a este modelo fue el de A-Estrella. La siguiente gráfica nos ilustra un posible ejemplo de cómo podemos segmentar un grafo en zonas. Figura 1. Grafo dividido en cuatro sectores. Como podemos ver en la gráfica anterior, hemos dividido el grafo total en cuatro grafos más pequeños. Una vez que el grafo ha sido dividido en sectores, podemos modelar cada una de estas zonas como un polígono de n lados. Cada uno de los lados de este polígono representa la conexión de un nodo que pertenece a la zona del polígono con un nodo de otra zona diferente por medio de un arco. En el ejemplo presentado en la Figura 2 a), se muestra subgrafo, el cual está dividido en dos sectores. En la parte b) de esta figura, se muestra la representación de este subgrafo en forma de poligono. En general podemos decir, que si tenemos k arcos que salen de una zona determinada, entonces tenemos un polígono de k lados que representa esta zona. De este polígono, cada uno de las aristas representa un arco del grafo verdadero. En la siguiente gráfica ilustramos como podemos aplicar este modelo. Figura 2. a) Dos sectores de un grafo interconectados por cuatro arcos. b) Modelo de polígono que representa el grado de la parte a). Al modelar el grafo de esta forma estamos eliminando todos los nodos de una zona específica, a excepción de los nodos que están interconectados con nodos de otras zonas. Esta es una reducción es significativa cuando estamos trabajando con grafos considerablemente grandes. Supongamos un caso hipotético, en el cuál tengamos un grafo de 30 mil nodos. Supongamos que dividimos este grafo en tres zonas. Cada una de estas zonas va a tener el mismo número de nodos. Adicionalmente suponemos que el grafo es planar, para conseguir una reducción más grande. La distribución de estas zonas es la siguiente: supongamos que la segunda zona está interconectada con la primera y la tercera zona, pero la primera y la tercera zona no se encuentran interconectadas; digamos que en la primera zona hay 100 nodos que tienen arcos que interconectan otros 100 nodos de la segunda zona, y que otros 100 arcos diferentes de la segunda zona tienen arcos que interconectan otros 100 nodos de la tercera zona. Ahora queremos encontrar el camino más corto entro un nodo de la primera zona al de la tercera zona. Lo primero que tendríamos que hacer es calcular la ruta más corta entre el nodo inicial y los nodos que interconectan esta zona con la segunda zona. Una vez estamos en uno de estos nodos frontera, debemos encontrar la ruta más corta entre este nodo y todos los nodos frontera entre la segunda y la tercera zona. Una vez estamos ahí, debemos calcular la ruta más corta entre este nodo frontera y el nodo destino en la tercera zona. Como podemos darnos cuenta, estamos reduciendo el espacio de búsqueda en este caso a un tercio del tamaño original. Si el grafo original lo dividimos en más zonas es posible que la reducción sea considerablemente más grande. Como veremos más adelante es posible reducir mucho más nuestro espacio de búsqueda. Si analizamos el algoritmo que seguimos para encontrar el camino más corto entre estos dos puntos en diferentes zonas encontramos lo siguiente. Estamos dividiendo nuestro problema inicial en tres subproblemas iguales, pero de un tercio del tamaño original. Esta estrategia se conoce comúnmente como dividir y conquistar. Adicionalmente si tenemos precalculada alguna información, que en el proceso anterior requiere de mucho esfuerzo, podemos mejorar la complejidad de este algoritmo, y al mismo tiempo podemos reducir nuestro espacio de búsqueda. La información que tenemos que precalcular para este algoritmo es la siguiente. Debemos precalcular el costo y la ruta del camino más corto entre todos los nodos frontera de cada una de las zonas. Al precalcular el costo y el camino más corto entre los nodos frontera de una zona, estamos reduciendo el número de nodos efectivos de una zona, al número de nodos frontera de esta zona. Esta reducción se hace efectiva, cuando la zona no hace parte de la zona del nodo origen ni de la zona del nodo destino. Para el ejemplo que dimos anteriormente, al precalcular el costo y el camino más corto entre todos los nodos frontera de cada una de las zonas del grafo, en la segunda zona estamos reduciendo el espacio de búsqueda únicamente a 200 nodos, a comparación de los 10 mil nodos que hacen parte de esta zona. A continuación vamos a dar un ejemplo más simple para ilustrar mejor la reducción que estamos realizando. En la siguiente gráfica podemos ver un grafo, el cuál está dividido en 5 zonas. Supongamos que queremos llegar del nodo azul de la zona del extremo izquierdo, al nodo rojo de la zona del extremo derecho. Figura 3. Grafo dividido por zonas para calcular la ruta más corta entre el nodo azul y el nodo rojo. Si precalculamos los costos y los caminos entre los nodos frontera para este ejemplo tendríamos una reducción considerablemente grande en el tamaño del espacio de búsqueda de nuestro problema. En la siguiente gráfica podemos ver como queda el grafo resultante el cuál sólo contiene los nodos frontera. Figura 4. Grafo reducido aplicando el modelo por zonas. Como podemos ver en la figura anterior, la reducción en el espacio de búsqueda ocurre en todas las zonas a excepción de las dos zonas que contienen a los nodos origen y destino. En la gráfica anterior, tenemos que recordar que los arcos en color rojo representan tanto el camino óptimo el cuál pasa por los nodos correspondientes de la zona en cuestión y el costo de dicho camino. En la zona que contiene tanto el nodo origen como el nodo destino, el espacio de búsqueda es reducido al tamaño de la zona, es decir contiene únicamente los nodos de la zona en cuestión. Hay que notar, que si el grafo total ha sido dividido en muchas zonas, el espacio de búsqueda de esta zona se reduce, pero el del modelo por zonas es posible que aumente considerablemente. Este modelo aunque parece solucionar todos nuestros problemas, para encontrar la ruta más corta entre dos nodos en un grafo grande tiene un gran inconveniente. Por un lado tenemos el problema del consumo de memoria requerido para almacenar este grafo adicional. Tenemos que notar que en este grafo sectorizado, tenemos una reducción de nodos con respecto al grafo original, pero al mismo tiempo estamos aumentando el número de arcos totales. El otro gran problema es que tenemos que precalcular toda la información, lo que requiere de un gran esfuerzo computacional previo. Para darnos una idea de cuál es el problema que implica este modelo, supongamos lo siguiente. Supongamos que tenemos una zona que pertenece a un grafo, la cuál contiene n nodos frontera. Cada uno de estos nodos tiene por lo menos n sucesores. De estos n sucesores, tenemos que (n - 1) de estos sucesores son los otros nodos frontera de la misma zona. Como el requisito para ser un nodo frontera es tener por lo menos un sucesor que no pertenezca a su misma zona, entonces debemos tener por lo menos un sucesor más, por lo tanto tenemos que cada nodo frontera tiene por lo menos n sucesores. Tenemos que recordar que para cada uno de estos sucesores debemos calcular tanto el costo, como el camino mínimo que conecta estos dos nodos. Otro problema que debe ser evidente para los lectores es el siguiente. En los sectores que contienen los nodos inicial y final, tenemos que, en el caso en el que una zona contenga el nodo inicial y no contenga el nodo final, es posible que nuestro algoritmo diga que debemos llegar a nuestros nodos frontera y a partir de esto calcular la ruta usando el modelo del grafo por sectores. El hecho que implica llegar a los nodos frontera a primera vista no parece tan complicado, pero si lo pensamos bien aumenta la complejidad de nuestro problema. Supongamos que tenemos el nodo inicial en alguna parte dentro de una zona, pero este nodo inicial no es un nodo frontera. Como nuestro algoritmo se basa en calcular la ruta mínima usando los nodos frontera de la zonas, tenemos que llegar al nodo frontera que nos aproxime más al nodo destino. Como no tenemos la certeza de cuál nodo frontera hace parte de la ruta óptima, tenemos que calcular cuál es el costo en llegar a todos los nodos frontera. Esto implica que tenemos que resolver el problema de la ruta más corta entre dos nodos, el nodo inicial y un nodo frontera, n veces donde n es el número de nodos frontera. Aunque nuestro modelo por zonas reduce el tamaño del espacio de búsqueda en forma extraordinaria de todas las zonas menos en las zonas que contienen el nodo inicial y el nodo final. En estas dos zonas también estamos reduciendo el espacio de búsqueda pero en menor proporción. En estas zonas tenemos un subgrafo que si bien es de menor tamaño al grafo original, tenemos que resolver más veces el una versión del problema original. Más adelante proponemos una solución para este problema, la cuál si bien no nos garantiza que nos encuentra la respuesta óptima, si nos reduce la complejidad y por ende mejora el desempeño en velocidad de ejecución de nuestro algoritmo. En este punto es conveniente concretar este algoritmo. A continuación vamos a presentarlo formalmente. Recordemos que este algoritmo está basado en el algoritmo A-Estrella. Algoritmo de A Estrella Por Zonas Formalmente A-Estrella-Zonas (G, nodo-Inicial, nodo-Final) {Precondición Q: G = (V, E) ∧ nodo-Inicial, nodo-Final ∈ G) 1: Disponibles: = φ 2: Analizados: = φ 3: Nodo-Actual: = Agregar-Camino-SP (nodo-Inicial, nodo-Inicial) 4: Nodo-Actual: = Configurar-Nodo (Nodo-Actual, 0) 5: Nodo-Actual: = Asignar-Padre (nodo-Inicial, nodo-Inicial, Nodo-Actual) 6: Disponibles: = Disponibles ∪ Nodo {Invariante P1: Disponibles: contiene nodos con G mínimo a partir del nodoInicial. ∧ Analizados: Analizados: contiene el SP entre nodo-Inicial y el ultimo nodo. } {Cota T1: G. Número – (Disponibles. Número + Analizados. Número) +1} 7: DO Disponibles ≠ φ Æ 8: 9: Nodo-Actual: = Extraer-Nodo-Menor (Disponibles) Analizados: = Analizados ∪ Nodo-Actual 10: IF Nodo-Actual. Nodo = nodo-Final Æ 11: Disponibles: = φ 12: Nodo-Actual. Nodo ≠ nodo-Final Æ 13: IF Nodo-Actual. Zona = nodo-Final. Zona ∧ Es-Nodo-Frontera (Nodo- Actual. Nodo, Nodo-Actual. Nodo. Zona) 14: 15: Æ Sucesores: = Obtener-Sucesores-Totales (Nodo-Actual. Nodo) Sucesores: = Sucesores ∪ Nodo-Final 16: Nodo-Actual. Zona = nodo-Final. Zona ∧ NOT Es-Nodo-Frontera (Nodo-Actual. Nodo, Nodo-Actual. Nodo. Zona) Æ 17: 18: Sucesores: = Obtener-Sucesores-Frontera (Nodo-Actual. Nodo) Sucesores: = Sucesores ∪ Nodo-Final 19: Nodo-Actual. Zona ≠ nodo-Final. Zona ∧ Es-Nodo-Frontera (Nodo-Actual. Nodo, Nodo-Actual. Nodo. Zona) Æ 20: Sucesores: = Obtener-Sucesores-Totales (Nodo-Actual. Nodo) 21: Nodo-Actual. Zona ≠ nodo-Final. Zona ∧ NOT Es-Nodo-Frontera (Nodo-Actual. Nodo, Nodo-Actual. Nodo. Zona) Æ 22: Sucesores: = Obtener-Sucesores-Frontera (Nodo-Actual. Nodo) FI {Precondición Q2: (∀ v: v∈ Sucesores | : Adyacente(Nodo-Actual, v) )} 23: 24: 25: 26: 27: 28: 29: Contador: = 0 {Invariante P2: (∀ v: v ∈ Analizados | : W(nodo-Inicial, v) ≥ WSP(nodoInicial, v))} {Cota T2: Sucesores. Número – Contador +1} DO Contador < Sucesores. Número Æ Sucesor: = Sucesores [Contador] Contador: = Contador +1 IF Contiene (Analizados, Sucesor) Æ NOT Contiene (Analizados, Sucesor) SKIP Æ Sucesor-Actual: = Agregar-Camino-SP (Nodo-Actual. Nodo, Sucesor-Actual. Nodo) 30: Sucesor-Actual: = Configurar-Nodo (Sucesor-Actual, Sucesor-Actual. G) Sucesor-Actual: = Asignar-Padre (Sucesor-Actual. Nodo, Nodo-Actual. Nodo, Sucesor-Actual) 31: 32: 33: IF NOT Contiene (Disponibles, Sucesor) Æ Disponibles: = Disponibles ∪ Sucesor-Actual 34: 35: 36: 37: Contiene (Disponibles, Sucesor) Æ IF Disponibles. G (Sucesor) > Sucesor-Actual. G Æ Eliminar (Disponibles, Sucesor) Disponibles: = Disponibles ∪ Sucesor-Actual FI FI FI FI OD {Poscondición R2: (∀ v: v ∈ Analizados | : W(nodo-Inicial, v) ≥ WSP(nodo-Inicial, v)} OD {Poscondición R1: Analizados: contiene el SP entre nodo-Inicial y el ultimo nodo. ∧ Disponibles: contiene nodos con G mínimo a partir del nodo-Inicial.} Este algoritmo es básicamente el mismo que el algoritmo A-Estrella, la diferencia radica en la selección de los sucesores correspondientes a cada nodo marcado. Para la selección de los sucesores tenemos cuatro casos. El primer caso está comprendido entre las líneas 13 a la 15 de este algoritmo. En este caso, el último nodo que fue marcado se encuentra en la misma zona que el nodo destino, y adicionalmente este nodo marcado es un nodo frontera de esta zona. Los sucesores para este nodo son todos los nodos frontera de la misma zona más los nodos frontera de otras zonas sobre los cueles incide un arco partiendo desde este nodo. El nodo destino también debe ser sucesor de este nodo, pero para poder colocarlo en los sucesores, tenemos que calcular cuál es el camino más corto hasta el y cuanto es el costo. La siguiente gráfica ilustra este caso. Figura 5. a) Grafo por zonas. El nodo azul es el nodo marcado, y el nodo verde es el nodo destino. b) los nodos de color rojo son los sucesores del nodo marcado azul. El nodo destino también es un sucesor, pero tenemos que calcular el camino más corto hasta el, denotado como la flecha roja. En la gráfica anterior, el nodo azul es el nodo marcado del que estábamos hablando anteriormente, y el nodo verde es el nodo destino. En la parte b) de la figura anterior, los nodos de color rojo son los sucesores totales del nodo marcado. El nodo destino también debe ser un sucesor del nodo marcado. Como no sabemos el camino mínimo, ni el costo entre el nodo marcado y el nodo destino, debemos calcularlo. Este cálculo se hace más adelante en la línea 29 de este algoritmo. En este caso el número de sucesores totales es igual a n + m + 1 donde n es el número de nodos frontera de la zona del nodo marcado excluyéndolo, y m es el número de nodos sucesores directos de otras zonas del nodo marcado, el nodo adicional es el nodo destino. El segundo caso ocurre en las líneas 16 a la 18 del algoritmo. En este caso, el ultimo nodo que fue marcado se encuentra en la misma zona que el nodo destino, y este nodo marcado no es un nodo frontera de esta zona. Este caso ocurre cuando el nodo origen y el nodo destino están en la misma zona. Los sucesores para el nodo marcado que en este caso es también el nodo inicial, son los nodos frontera de la misma zona y el nodo destino. La justificación para tener en cuenta a los nodos frontera de esta zona es poder sortear correctamente casos como el que se muestra en la siguiente gráfica. Figura 6. a) Grafo con dos Zonas. El nodo azul es el nodo inicial y el nodo verde es el nodo final. b) Los nodos marcados en rojo son los sucesores del nodo azul, en el modelo de grafos por zonas. En el caso presentado en la figura anterior, si tenemos en cuenta el camino entre el nodo azul (nodo inicial) y el nodo verde (nodo final), pasando únicamente por los nodos de la region exterior (zona comprendida entre la linea puenteada azul y la linea roja), encontraríamos un camino que no corresponde al óptimo. Si tenemos en cuenta los nodos frontera de la zona azul como sucesores, el algoritmo encontrara un camino mínimo que parara por las dos zonas. El tercer caso ocurre entre las líneas 19 a la 20 de este algoritmo. En este caso el nodo marcado y el nodo destino están en zonas diferentes. Adicionalmente el nodo marcado es un nodo frontera del sector al que pertenece. En la siguiente gráfica se presenta este caso. Figura 7. Grafo modelado por sectores. El nodo azul es el nodo marcado, y el nodo verde es el nodo destino. En la figura anterior, el nodo azul es el nodo marcado, y el nodo verde es el nodo destino. Los arcos de color rojos son arcos que pueden no ser reales, es decir que no existan en el grafo original. Estos arcos representan el camino más corto entre dos nodos dados. Este camino ha sido precalculado anteriormente. Los nodos de color rojo son nodos frontera de la zona correspondiente. Los demás nodos de las zonas intermedio son eliminados de nuestro análisis en este modelo, tal y como se explicó anteriormente. Los sucesores del nodo marcado (el nodo de color azul), en este caso son los demás nodos frontera de la misma zona a la que pertenece, y los nodos de otras zonas con los que se encuentre interconectado directamente. Es decir que exista un arco que los relacione en el grafo original. Este caso es muy similar al primer caso, a diferencia que entre los sucesores no está el nodo destino. El último caso ocurre entre las líneas 21 y 22 del algoritmo. En este caso el nodo marcado y el nodo destino están en zonas diferentes. Adicionalmente el nodo marcado no es un nodo frontera. En este caso, el nodo marcado es el mismo nodo inicial, pero a diferencia del segundo caso, el nodo destino está en otra zona. La siguiente gráfica muestra este caso. Figura 8. Grafo modelado por sectores. El nodo azul es el nodo inicial y el nodo verde es el nodo destino. En la figura anterior, el nodo azul es el nodo marcado, y el nodo verde es el nodo destino. Los arcos de color rojos son arcos que pueden no ser reales, es decir que no existan en el grafo original. Estos arcos representan el camino más corto entre dos nodos dados. Este camino ha sido precalculado anteriormente. Los nodos de color rojo son nodos frontera de la zona correspondiente. Los demás nodos de las zonas intermedio son eliminados de nuestro análisis en este modelo, tal y como se explicó anteriormente. Para este caso los sucesores del nodo marcado son los nodos frontera de su sector. Recordemos que en este algoritmo queremos aprovechar el procesamiento previo que realizamos en los nodos frontera. Es por esto que queremos estar la mayor cantidad de tiempo usando los nodos frontera del grafo sectorizado. El algoritmo entre las líneas 23 a la 37 son las mismas que las del algoritmo A-Estrella tradicional, de esta parte del algoritmo hablamos anteriormente en el capitulo del algoritmo A-Estrella. A continuación vamos a hablar sobre la complejidad de este algoritmo. Análisis de Complejidad. La complejidad del algoritmo A-Estrella por sectores aparentemente es la misma que la del algoritmo A-Estrella normal, pero esto no correcto por lo menos para esta versión del algoritmo. La diferencia en las complejidades entre estos dos algoritmos proviene de línea 29 de este algoritmo. Esta línea realiza el siguiente procedimiento. Si no existe un arco del grafo original que interconecte el nodo marcado y el nodo sucesor, o si tampoco se ha precalculado anteriormente cuál es el costo del camino más corto entre par de nodos, debemos calcular el camino y el costo de este camino. Este proceso de calcular el camino mínimo y el costo de este, tiene una complejidad que depende del algoritmo que sea utilizado. En el algoritmo de Dijkstra y en el de A-Estrella se supone que la complejidad de esta operación es muy baja, es posible que según la implementación usada, puede llegar a ser O (1). Otra gran diferencia que existe entre el algoritmo A-Estrella básico y este algoritmo es que el número de sucesores de cada nodo ha cambiado. Anteriormente habíamos dicho que si la conectividad del grafo era baja, podíamos hacer una aproximación en la complejidad del algoritmo. La aproximación es realizada en la complejidad del ciclo interno que analiza a todos los sucesores de un nodo dado. Como vimos anteriormente ese ciclo tiene una complejidad de por lo menos O(n), donde n es el número de sucesores. En este algoritmo no podemos realizar esta aproximación, ya que el número de sucesores ha aumentado considerablemente. En los grafos que representan mallas viales reales, los nodos normalmente tienen cuatro sucesores, pero en algunos casos pueden llegar a tener hasta ocho sucesores. Podríamos decir, que el número de sucesores en este tipo de grafos es pequeño. Por lo cuál podríamos realizar la aproximación que mencionamos anteriormente. En el caso del algoritmo A-Estrella por sectores, el número de sucesores de cada nodo es mucho mayor. Recordemos que cada nodo frontera tiene por lo menos n sucesores. De estos n sucesores, (n – 1) son los otros nodos frontera de la misma zona del nodo que estamos analizando, y el nodo restante debe ser un nodo frontera de otra zona. Por el momento este número de sucesores n no parece tan problemático. Para podernos dar una idea de lo problemático que puede llegar a hacer, debemos utilizar un ejemplo real. Supongamos que una de nuestras zonas es un barrio pequeño de la ciudad de Bogotá. Supongamos que el perímetro de este barrio es de aproximadamente 30 cuadras por 30 cuadras (un cambio de diez números en la nomenclatura). Esta zona podría llegar a tener aproximadamente 900 nodos, de los cuáles 120 podrían ser nodos frontera. En el ejemplo anterior el número de sucesores aumento aproximadamente 20 veces. Con todo lo anterior podemos decir que el algoritmo A-Estrella por zonas tiene una complejidad mayor a la del algoritmo A-Estrella básico. Si hacemos una evaluación teniendo en cuenta sólo las complejidades de los algoritmos, el algoritmo A-Estrella es mejor al algoritmo A-Estrella por sectores. Pero si los comparamos según el desempeño obtenido en cuanto a velocidad de procesamiento para resolver una misma pregunta, el resultado puede cambiar. MODELO BÁSICO Los grafos usualmente son usados para representar modelos reales. En este documento vamos a utilizar los grafos para modelar la malla vial real de la ciudad de Bogotá. Existen diferentes formas de modelar una malla vial en forma de grafo. La forma más simple de modelar una malla vial utilizando un grafo es la siguiente. Los nodos son modelados como las intersecciones o las terminaciones de los segmentos de las vías, y los arcos son los segmentos de las vías. Por lo tanto una esquina de una malla vial es un nodo, y la vía en si es un arco. La siguiente gráfica muestra como un ejemplo de este modelo. Figura 9. a) Segmento de una mala vial. b) Representación del segmento de la malla vial de a) en forma de grafo. En este modelo se supone que los arcos son no dirijidos. Este modelo es el más simple que podemos utilizar. Puede ser utilizado para problemas simples, en los cuáles no tenemos restricciones. Este modelo, aunque muy utilizado para modelar mallas viales, no está en la capacidad de modelar mallas viales reales. En la vida real tenemos restricciones de todo tipo. Las restricciones que vamos a tener en cuenta en nuestro trabajo son los cruces y los retornos prohibidos. A primera vista el problema de los cruces y los retornos prohibidos no parecen ser tan difíciles de modelar. Una primera aproximación inocente es la de generar una lista arcos ilegales para cada nodo en nuestro grafo. Esta lista sería consultada en cada nodo, a la hora de seleccionar los sucesores válidos, cuando estamos buscando el camino más corto entre dos nodos dados. Supongamos un grafo como el de la siguiente gráfica. A B C D E Figura 10. Grafo con restricciones. El nodo azul es el nodo inicial, y el nodo final es nodo rojo. En el grafo de la figura anterior, supongamos que queremos encontrar el camino más corto entre el nodo E y el nodo C. Supongamos también que todos los arcos tienen el mismo costo. Supongamos que para encontrar el camino más corto decidimos usar el algoritmo de Dijkstra. El algoritmo de Dijkstra primero marcaría el nodo “E”, después consultaría en la lista de arcos ilegales. Como no hay arcos ilegales, los sucesores “D” y “E” son relajados. Después marcamos el nodo “D”, verificamos en la lista, y relajamos el nodo “A”. A continuación marcaríamos el nodo “B”, y consultamos en la lista de restricciones. Como existe una restricción en el arco entre “B” y “C” al llegar por el nodo “E”, no tendríamos ningún sucesor que relajar. Todo parece bien hasta este punto. Después marcamos el nodo “A”. Los sucesores del nodo “A” son el nodo “B” y el nodo “D”, como estos dos nodos ya están marcados los ignoramos. Después marcamos el siguiente nodo con menor costo en la lista de disponibles. Esta lista contiene sólo al nodo “C”. Como este nodo no ha sido relajado anteriormente, tiene un costo infinito. Con esto terminaríamos la ejecución del algoritmo de Dijkstra. Como el costo del nodo “C” al terminar el algoritmo es infinito, esto quiere decir que no se encontró ningún camino entre el nodo “E” y el nodo “C”. La respuesta que encontramos con el algoritmo de Dijkstra es incorrecta, ya que si existe un camino, el cuál es “E-D-A-B-C”. La siguiente gráfica ilustra el problema anterior. A B C D A B C E D E Figura 11. a) Caminos mínimos encontrados por el algoritmo de Dijkstra en este modelo con restricciones. b) Camino mínimo entre el nodo azul y el nodo rojo. Como se mostró en el ejemplo anterior, este modelo no puede ser usado para modelar una malla vial real con restricciones. Para lograr modelar grafos con este tipo de restricciones, existen otros modelos los cuáles van a ser analizados a continuación MODELO DE GRAFO DUAL Como se mostró en el modelo básico anterior, existe la necesidad de emplear otro modelo más sofisticado que sea capaz de modelar adecuadamente algunas de las restricciones que existen en la vida real. Las restricciones en las que estamos interesados son los giros prohibidos y los retornos en la vía restringidos. El modelo que comúnmente es utilizado cuando se están modelando mallas viales urbanas, consiste principalmente en generar un Grafo Dual. Este grafo es construido de la siguiente forma. Los arcos del grafo original son convertidos en nodos en el Grafo Dual, y los arcos son generados a partir de las restricciones existentes en cada uno de los nodos del grafo original. En el proceso de transformación hay que tener en cuenta lo siguiente. Cuando estamos hablando de convertir los arcos en nodos, esto no se hace directamente. La transformación que se realiza es la siguiente. Dados dos nodos A y B del grafo original, si existe un arco o más que interconecten este par de nodos, este conjunto de arcos son convertidos en un único nodo en nuestro Grafo Dual. La generación de arcos en nuestro Grafo Dual ocurre de la siguiente forma. Supongamos que tenemos un grafo como el mostrado en la Figura 12. Este grafo consta de 5 nodos, 8 arcos. El nodo central está interconectado con los nodos exteriores por medio de dos arcos. Suponemos que no existen restricciones en los cruces. B A D C E Figura 12. Gráfica de un grafo, el cuál puede ser interpretado como una intersección en una malla vial. La construcción del Grafo Dual es la siguiente. Primero generamos los nodos. Para esto seleccionamos parejas de nodos que estén interconectadas. Por cada pareja de nodos que sea seleccionada vamos a generar un nodo en nuestro modelo dual. Para el grafo mostrado en la Figura 12, hay 4 parejas de nodos. Estas parejas de nodos son: PN = {(A, D), (B, D), (C, D), (E, D)}. Para la generación de arcos en nuestro Grafo Dual, el procedimiento es el siguiente. Se deben verificar las restricciones existentes en los giros. Si existe una restricción en un giro se omite el arco entre los nodos del Grafo Dual que están involucrados, de lo contrario se genera un arco que los interconecta. En la Figura 13, se muestra como es el Grafo Dual para el grafo mostrado en la Figura 12. Este grafo tiene un menor número de nodos que el grafo original, pero el número de arcos aumenta considerablemente. Para cada intersección similar a la representada por el grafo mostrado en la Figura A. el número de arcos que serán creados al no existir restricciones puede llegar a ser igual a la combinación de los sucesores y los predecesores del nodo central. a) b) B BD BD AD CD A AD D ED CD C ED E Figura 13. Representación del Grafo Dual. a) Grafo dual. b) Grafo Dual sobrepuesto al grafo original. Este modelo aunque parece muy simple, nos puede ayudar a modelar algunas de las restricciones que existen en la vida real. Este Grafo Dual es mejor que se encuentre preconstruido antes de realizar algún cálculo. Aunque este modelo parece solucionar todos nuestros problemas, esto no es verdad. Este modelo no está en la capacidad de modelar restricciones en los retornos. El siguiente ejemplo nos va a lustrar mejor este problema. Supongamos un grafo como el mostrado en la Figura 14. En este grafo existen restricciones en tanto de giro como de retorno. La restricción en el giro aparece en el nodo B, cuando entramos por el nodo A y queremos llegar al nodo F. La restricción en el retorno existe en el nodo C, cuando entramos por el nodo B y queremos regresar al mismo nodo B. A 1 B 1 C 1 D 1 F Figura 14. Grafo con restricciones de giro y de retorno. Supongamos que queremos encontrar el camino más corto entre el nodo A y el nodo F. El camino más corto entre estos dos nodos es SP1= {A, B, C, D, C, B, F}. Sorprendentemente el camino encontrado usando el grafo dual es diferente. El grafo dual es mostrado en la Figura 15. 2 AB 2 2 CB 2 2 2 DC 2 FB Figura 15. Grafo dual correspondiente al grafo mostrado en la Figura 14. Utilizando el Grafo Dual para encontrar el camino más corto entre los nodos A y F del grafo original, los cuáles corresponden a los nodos AB y FB respectivamente de este grafo. El camino encontrado es SP2 = {AB, CB, FB}. Como se puede observar, el camino encontrado utilizando el grafo dual, es diferente al camino óptimo encontrado en el grafo original. El camino correcto utilizando este modelo y teniendo en cuenta todas las restricciones del modelo original debería ser SP3= {AB, CB, DC, CB, FB}. El error en este modelo ocurre gracias a que el modelo del Grafo Dual, no es capaz de representar las restricciones en los retornos. En este modelo no estamos teniendo en cuenta la restricción en el retorno existente en el nodo C del grafo original. Lo primero que uno propondría en este caso es colocar una lista de restricciones en cada nodo de nuestro Grafo Dual, el cuál restringiría las opciones a la hora seleccionar los sucesores de un nodo dado. Este caso sería similar al propuesto en el modelo básico, explicado anteriormente. En este caso ocurriría lo mismo, tendríamos que modificar el algoritmo utilizado para encontrar el camino óptimo, para que pueda manejar adecuadamente las restricciones que estamos imponiendo. Adicionalmente también tendríamos que modificarlos para que puedan encontrar caminos óptimos que pasan dos o más veces por un mismo nodo. Las modificaciones que deben sufrir los algoritmos tradicionales para tener en cuenta estas restricciones, en especial la de encontrar el camino óptimo el cuál puede pasar por un nodo más de una vez, es muy complicado y requiere de un gran esfuerzo, el cuál en este caso no vale la pena asumir, ya que existen otras alternativas menos costosas de desarrollar, pero al mismo tiempo más complejas y que consumen más recursos, como se mostrará a continuación. MODELO DE GRAFO DINÁMICO Después de analizar diferentes opciones, se logró desarrollar un modelo que es capaz de representar las restricciones típicas a las que nos enfrentamos al modelar mallas viales reales. Como lo habíamos mencionado anteriormente las principales restricciones a las que nos tenemos que enfrentar son los cruces prohibidos y los retornos en la vía. El modelo que presentamos en la sección anterior, nos permite modelar cruces prohibidos, pero es incapaz de modelar restricciones en los retornos. Como todos sabemos el camino más corto entre dos nodos de un grafo está compuesto por una sucesión de nodos partiendo desde el nodo inicial, hasta llegar al nodo final. Sobre esta sucesión de nodos, hay que notar que normalmente cada uno de los nodos que componen este camino es único, es decir que no se pasa dos veces o más por el mismo nodo, para alcanzar nuestro nodo objetivo. Al tener en cuenta los retornos en la vía y los cruces prohibidos, se presenta el siguiente paradigma. Este paradigma consiste en la posibilidad de encontrar un camino óptimo entre dos nodos dados (un nodo de origen y un nodo de destino), el cuál pasará dos o más veces por un mismo nodo. Este problema se puede visualizar más fácilmente con el siguiente ejemplo. Suponga un grafo como el que se muestra en la Figura 16. A 1 B 1 C 1 D 1 1 E F Figura 16. Grafo con restricciones en los cruces y en los retoros. En este grafo encontramos una restricción de giro en el nodo B, y una restricción en el retorno en el nodo C. Supongamos que queremos encontrar el camino mínimo entre el nodo A y el nodo E. Con el modelo que vimos anteriormente, el del Grafo Dual, obtenemos el siguiente camino, SP1 = {A, B, C, B, F, E}, o en notación del Grafo Dual. SPD1= {AB, BC, BF, FE}, muy similar al camino encontrado para la Figura 15, en la sección anterior. Como se puede observar al aplicar este modelo se obtiene una respuesta equivocada, ya que estaríamos violando la restricción en el retorno en el nodo C. La respuesta correcta es SP2 = {A, B, C, D, C, B, F, E}. Para lograr llegar a encontrar está respuesta es necesario realizar cambios en los algoritmos para que tengan en cuenta estas restricciones, o también es posible diseñar un nuevo modelo, el cuál tenga en cuenta estas restricciones. El siguiente modelo va a ser llamado Grafo Dinámico. La idea principal de este modelo es generar un grafo paralelo al grafo original, el cuál es generado en paralelo a la ejecución de un algoritmo que resuelva el problema de encontrar la ruta más corta entre dos nodos dados. Este Grafo Dinámico, tiene las siguientes características. Para cada nodo del grafo original, el Grafo Dinámico, tendrá n nodos. Este número n de nodos, no es constante y como máximo es igual al número de predecesores del nodo. Estos nodos son generados la primera vez que un predecesor hace un llamado para saber cuáles son sus sucesores válidos, es decir los sucesores con los cuáles no existe ninguna restricción. Si bien este Grafo Dinámico, logra modelar correctamente las restricciones con las que estamos trabajando, el costo que estamos pagando en espacio de almacenamiento, ya sea memoria o disco duro, se ha incrementado considerablemente. En el peor de los casos, el cuál ocurre cuando queremos encontrar el camino más corto desde un nodo fuente, hasta el resto de nodos del grafo, tendríamos que generar un Grafo Dinámico con un conjunto de nodos n veces más grande que el grafo original, ya que para cada nodo del grafo original, necesitamos n nodos que lo representarían en nuestro Grafo Dinámico. En este caso nuestro Grafo Dinámico, representaría todo el grafo original. En el caso en el que sólo se requiera encontrar el encontrar el camino óptimo entre dos nodos dados, nuestro Grafo Dinámico, sólo representaría una porción del grafo original correspondiente a la zona analizada por el algoritmo utilizado. El número de arcos requeridos para nuestro Grafo Dinámico, en el peor de los casos sería el doble de los requeridos en el modelo dual analizado anteriormente. Hay que recordar en este punto, que cada nodo del grafo original es representado en nuestro Modelo Dinámico, como un grupo de k nodos. El camino con costo mínimo corresponde al camino que conduzca desde el nodo inicial, hasta al nodo final j (0< i < k+1), que tenga costo mínimo. Si utilizamos el algoritmo A-Estrella para calcular el camino más corto entre dos nodos usando este modelo de grafo, el tamaño del grafo generado es menor al que genera el algoritmo de Dijkstra, el cuál se expone más adelante. Para el algoritmo de Dijkstra, nuestro modelo alcanza casi el peor de los casos, gracias a que este algoritmo analiza en orden los caminos con menor costo. Para estos dos algoritmos, una vez se ha alcanzado alguno de los k nodos destino, el algoritmo termina, y se garantiza que el camino encontrado es mínimo. El siguiente ejemplo ilustra como es la generación del Grafo Dinámico. Suponga que se tiene un grafo como el mostrado en la Figura 17, en el cuál queremos encontrar el camino mínimo entre A y E. a) b) A 1 B 1 C 1 D AA AB 1 1 1 Figura 17. a) Grafo original. b) Primer paso de la generación del Grafo Dinámico. Como se pude ver en la Figura 17 a), en el grafo original tenemos una restricción de giro en el nodo B, y un retorno prohibido en el nodo C. En la parte b), podemos ver que partimos desde nuestro nodo inicial A, llamado ahora en nuestro Grafo Dinámico AA. Miramos cuáles son sus sucesores válidos y los adicionamos a nuestro grafo. En este caso adicionamos un nodo llamado AB, el cuál está conectado por un arco que parte desde el nodo AA. Analizamos ahora el nodo AB, y adicionamos los sucesores válidos de este a nuestro grafo. De esta misma forma continuamos generando nuestro Grafo Dinámico. Esto se encuentra ilustrado en la Figura 18. BA BA AB BC AA BC AB BC BC AB AA AB BA CD CD DC AA CB DC BC BA AB BC CD AA CD AA BA BA AB CB DC CD AA CB DC FB BF FE BF Figura 18. Proceso de generación del Grafo Dinámico. En la figura Z, se puede observar que el camino mínimo ente el nodo A y el nodo E es SP3 = {AA, AB, BC, CD, DC, CB, BF, FE}, el cuál es equivalente al camino que habíamos mencionado anteriormente SP2 = {A, B, C, D, C, B, F, E}. Todos los algoritmos de grafos son aplicables a este modelo. Lo único que hay que tener en cuenta a la hora de emplear un algoritmo para este modelo, es que hay que generar el grafo a medida que el algoritmo va analizando el grafo original. IMPLEMENTACION La base de datos geográficos de la malla vial de la ciudad de Bogotá, fue proporcionada por la empresa Procalculo Prosis S.A. Esta base de datos fue desarrollada bajo el estándar del sistema de archivos de información geográfica Shapefile. Este sistema de archivos de información geográfica es propiedad de la compañía de investigación y desarrollo de software ESRI. Esta compañía desarrolla aplicaciones para sistemas de información geográfica GIS. La compañía ESRI desarrolló una aplicación llamada ArcGIS. Esta aplicación es una familia de integrada de productos de software de información geográfica GIS. La Universidad de los Andes cuenta con la licencia ArcView para ArcGIS Desktop. ArcGIS está desarrollada a base de ArcObjects, los cuáles son un grupo de componentes de software de plataforma independiente, los cuáles están desarrollados en C++. Adicionalmente estos componentes de software hacen uso de la arquitectura COM “Microsoft Component Object Model”. La arquitectura COM se puede ver simplemente como una forma simple de especificar la forma en que los objetos son implementados y construidos en la memoria y también la forma en que se comunican entre si los objetos. La arquitectura COM incluso provee una sólida infraestructura a nivel del sistema operativo, al soportar cualquier componente construido usando esta arquitectura. En el sistema operativo Microsoft Windows, la infraestructura COM está implementada directamente a nivel del sistema operativo. En otros sistemas operativos, esta infraestructura deberá ser proveída para que los ArcObjects funcionen. La funcionalidad de los ArcObjects puede ser accedida usando cuatro tipos de API´s. estos cuatro tipos son COM, .NET, Java y C++. Con el tipo de licencia que posee la Universidad de los Andes, sólo se puede tener acceso a los API´s COM y .NET. Los API´s de COM pueden ser accedidos por cualquier lenguaje que compile COM. Algunos de los lenguajes que compilan COM son Visual Basic, Visual C++, y Delphi. Los API´s de .NET pueden ser accedidos por los lenguajes Visual Basic .NET y C#. Debido a que la base de datos con la información geográfica de la malla vial de la ciudad de Bogotá esta en el formato Shapefile, y que la aplicación ArcGIS provee los componentes ArcObjects, resulta más conveniente utilizarlos para procesar y extraer la información de la base de datos geográfica, que desarrollar toda una aplicación que procese esta base de datos. Los API´s que se utilizaron fueron los COM a través del lenguaje Visual Basic 6. En este lenguaje se desarrolló un DLL, el cuál utiliza ArcObjects para interactuar con la base de datos geográfica. Este DLL genera un grafo con la información relevante de la base de datos geográfica. La información geográfica que se consideró relevante es la identificación y la ubicación geográfica de las intersecciones, el sentido de las vías, las restricciones en los cruces, el tipo de la vía, la longitud de la misma entre otras. Se decidió generar un grafo a partir de la base de datos geográfica, en lugar de acceder directamente a esta, debido a la velocidad de acceso a la información. Las consultas en la base de datos geográfica a través de los ArcObjects, tienen un tiempo de consulta alrededor de 500 milisegundos según la complejidad de la consulta realizada. Mientras que si generamos un grafo en memoria RAM, el tiempo de consulta es mucho menor. La base de datos geográfica de la ciudad de Bogotá a la que tuvimos acceso cuenta con la siguiente información. Tabla X. Descripción de la base de datos geográfica de la ciudad de Bogotá. Nombre FID Shape FNODE_ TNODE_ Descripción Es un número entero usado como llave primaria de la base de datos geográfica. Es usado para identificar cada segmento de vía. Es una cadena de caracteres que dice el tipo de base de datos que es. En este caso en una base de datos de poli líneas. En un número entero usado para identificar la terminal inicial de la poli línea. En un número entero usado para identificar la terminal final de la polilinea. LPOLY_ Es un número, que no tiene uso. RPOLY_ Es un número, que no tiene uso LENGHT Es un número que representa la longitud real de la poli línea. Está vació BOG_MALLA Es un número, que no tiene uso. OBJECTID Es un número, que no tiene uso. ID_EDIT Es un número, que no tiene uso. TIPO_VIA Es una cadena de caracteres usada para identificar el tipo de vía. STREET_NAM Es la nomenclatura de la vía. PREDIR No es usado. Se encuentra vació. SUFDIR No es usado. Se encuentra vació. NOMB_COMUN Es una cadena de caracteres con el nombre de la vía. ONE_WAY Es una cadena de caracteres que identifica si el segmento de vía es bidireccional o unidireccional. FROMLEFT Es usado para generar restricciones de giro. FROMRIGHT Es usado para generar restricciones de giro. TOLEFT Es usado para generar restricciones de giro. TORIGHT Es usado para generar restricciones de giro. NOMENCLA Es una cadena de caracteres con la nomenclatura principal de la vía. SHAPE_FID Es un número, que no tiene uso. SHAPE_Lenght Es un número que representa la longitud real de la vía. La anterior tabla contiene todos los campos de la base de datos geográfica. Esta base de datos geográfica está incompleta, le hacen falta información y campos. La base de datos de información geográfica de la ciudad de Bogotá tiene los siguientes problemas. Los campos FNODE_ y TNODE_ los cuáles son usados como identificadores únicos para los segmentos de las vías, se encontraban sin calcular, es decir que todos tenían asignados el valor cero. Se desarrolló un script que generó identificadores únicos para estos campos. Los campos TIPO_VIA, STREET_NAM, y NOMENCLA, no tienen asignados valores en todos los arcos de la base de datos geográfica. El campo TIPO_VIA pude tener los siguientes valores, KR, DG, TV, CL, AK, AC, o vacío. Este campo lo utilizamos para cambiar los pesos de los arcos del grafo. Las vías con identificador AK y AC, son rutas de alta capacidad de tráfico, como autopistas y vías arteria. Los demás identificadores son usados para vías residenciales. El campo ONE_WAY, es usado para identificar si un segmento de vía es unidireccional o bidireccional. En la base de datos geográfica de la ciudad de Bogotá no existe un estándar para los valores que este campo puede tener. El valor que más se encuentra en este campo es “B”, nosotros suponemos que este valor significa que el segmento de vía es bidireccional. Los demás segmentos de vía son considerados unidireccionales, y suponemos que tienen el sentido del nodo inicial al nodo final, es decir de FNODE_ a TNODE_. Los campos FROMLEFT, FROMRIGHT, TOLEFT, TORIGHT, son usados para representar restricciones en los giros de las vías. Como sólo existen cuatro campos para representar las restricciones es posible que no podamos modelar todas las restricciones posibles. Podemos decir que la malla vial de la ciudad de Bogotá es relativamente victoriana, ya que en sí se podría decir que la podemos aproximar a una cuadrícula. Podemos decir que los arcos verticales son identificados por el campo tipo vía que poseen el valor de AC, CL, TV, y los arcos horizontales los podemos identificar también con este campo pero con los valores AK, KR, DG. Ya con esto definido podemos interpretar los campos que representan las restricciones. La siguiente gráfica muestra los posibles escenarios para modelar las restricciones FROMLEFT y FROMRIGHT. TNODE TORIGHT FNODE TORIGHT TOLEFT TOLEFT TNODE FNODE TNODE FNODE TOLEFT TORIGHT FNODE TOLEFT TNODE TORIGHT Figura 19. Los cuatro casos posibles para las restricciones de la base de datos geográfica de la ciudad de Bogotá. Las restricciones en los cruces representadas por TOLEFT, y TORIGHT, son análogas a las restricciones anteriores, pero cambiando el nodo FNODE por el nodo TNODE. Para que estas restricciones sean válidas, el arco entre FNODE y TNODE debe ser bidireccional. El campo SHAPE_Lenght fue usado para ayudar a modelar el costo de los arcos en el grafo. Este campo cumple con la misma función que el campo LENGHT, pero este se encuentra vacío en la base de datos geográfica. En el DLL desarrollado, se implementaron diferentes algoritmos, unos para la generación del grafo que representa la malla vial de la ciudad de Bogotá, y otros para solucionar el problema de la ruta más corta entre dos puntos dados en la ciudad de Bogotá. Se desarrollaron cuatro diferentes algoritmos para la generación de un grafo que representara la malla vial de la ciudad de Bogotá. Estos algoritmos son los siguientes: • Grafo Simple. Este grafo utiliza el modelo básico expuesto anteriormente. En este grafo el costo de todos los arcos es el mismo. • Grafo Modificado. Este grafo también utiliza el modelo básico, pero los costos de los arcos han sido modificados teniendo en cuenta el tipo de vía que representan. • Grafo Con Restricciones. Este grafo se basa en el modelo simple, pero adicionalmente calcula una lista con las restricciones de los cruces. • Grafo por Sectores. Este grafo sólo contiene los nodos frontera de cada sector, para ser usado por el algoritmo A-Estrella por sectores; debe usar uno de los grafos anteriores adicionalmente. Se desarrollaron también los siguientes algoritmos de grafos de la ruta más corta. • Algoritmo Dijkstra Simple. Este algoritmo puede ser usado en el grafo simple y en el grafo modificado. • Algoritmo A-Estrella Simple. Este algoritmo puede ser usado en el grafo simple y en el grafo modificado. • Algoritmo Dijkstra Con Restricciones. Este algoritmo pude ser usado únicamente con el grafo con restricciones. • Algoritmo A-Estrella Con Restricciones. Este algoritmo pude ser usado únicamente con el grafo con restricciones. • Algoritmo A-Estrella Por Sectores. Este algoritmo sólo puede ser usado en el grafo por sectores. El código fuente de estos algoritmos se encuentra en los anexos, al final del documento. El código fuente se encuentra en el lenguaje Visual Basic 6. Se desarrolló también un modulo que interactúa con los ArcObjects, para dibujar sobre el mapa de la ciudad de Bogotá el camino encontrado por los algoritmos. En la siguiente gráfica se muestra un ejemplo de la forma de visualización del mapa y del camino encontrado por los algoritmos. Figura 20. Ejemplo de visualización del camino más corto entre dos puntos en la ciudad de Bogotá. Los algoritmos de cálculo de la ruta más corta adicionalmente a la información del camino calculado, retornan un ArcObject el cuál tiene una interfase llamada IPolyline. Este objeto contiene la información geográfica y gráfica del camino más corto encontrado. Este objeto es utilizado para dibujar la ruta más corta calculada sobre el mapa de la ciudad de Bogotá. En la figura anterior, la línea de color azul es el camino encontrado por el algoritmo utilizado. Las otras líneas de color naranja son vías de parte del mapa de Bogotá. Los algoritmos desarrollados calculan la siguiente información, que permite hacer una comparación entre ellos. Calculan la longitud en kilómetros del camino más corto encontrado. También calculan el número de nodos que componen el camino más corto calculado, el tiempo requerido para calcular dicho camino, y por último el número de nodos marcados por cada algoritmo. A continuación vamos a presentar los resultados obtenidos con estos algoritmos. RESULTADOS OBTENIDOS Y CONCLUSIONES Para poder probar los diferentes algoritmos, fue necesario hacer algunas modificaciones en la base de datos geográficos de la ciudad de Bogotá. Adicionalmente fue necesario realizar un grupo de supuestos, ya que la información contenida en esta base de datos está incompleta. En la base de datos geográfica de la malla vial de la ciudad de Bogotá, cuenta con las siguientes características. Esta base de datos geográfica, tiene mezclada la malla vial vehicular con la malla vial peatonal, y no se hace ninguna diferenciación entre ellas. Los campos FNODE y TNODE no se encontraban calculados, por lo que fue necesario desarrollar un script que los calculara y los adicionara a la base de datos geográfica de la malla vial de la ciudad de Bogotá. El campo ONE_WAY el cuál es usado para decir si un arco es deterministico o no deterministico, se encontró que puede tener los siguientes valores. Los valores para este campo son “B”, “TF”, “FT”, “N”, “”. Como podemos ver, es necesario establecer algunos supuestos para poder generar adecuadamente el grafo. Los supuestos son. El valor “B” significa que el arco es bidireccional, es decir no deterministico. El valor “TF” se supone que es un arco entre el nodo TNODE, al nodo FNODE, y el valor “FT” corresponde a un arco entre el nodo FNODE, al TNODE. Los valores “N” y “” fueron tomados como un arco entre el nodo FNODE, y el nodo TNODE. En los campos TOLEFT, TORIGHT, FROMLEFT, FROMRIGHT no se encuentran actualizados, y no cuentan con un estándar para los valores que puede tomar. Para los valores de estos campos los supuestos son los siguientes. El valor -1 es interpretado como una restricción en un giro, y los valores diferentes son tomados como giros permitidos. Para el campo TIPO_VIA, se suponemos que los valores AC y AK representan segmentos de vía de alta capacidad de tráfico, por lo cuál el costo relacionado a estos arcos es menor al costo de las vías residenciales. Como la base de datos geográfica de la malla vial de la ciudad de Bogotá, no cuenta con un campo reservado para separar la ciudad geográficamente por sectores, tales como localidades o barrios, es necesario generar un script que divida la ciudad en zonas. Este script realiza una división de la ciudad en zonas rectangulares, de igual tamaño. Aunque todos los algoritmos fueron desarrollados, no todos pudieron ser probados. El algoritmo A-Estrella por sectores no pudo ser probado, debido a limitaciones de tiempo. Hay que recordar que para poder ejecutar este algoritmo es necesario precalcular mucha información, como se explicó anteriormente. La dificultad para realizar el cálculo de la información necesaria para que funcione correctamente el algoritmo A-Estrella por sectores es la siguiente. La malla vial de la ciudad de Bogotá fue dividida en 80 zonas geográficas rectangulares del mismo tamaño. El número promedio de nodos frontera en cada zona es aproximadamente 500. El tiempo promedio para calcular la ruta más corta entre una pareja de nodos frontera de una misma zona utilizando el algoritmo de Dijkstra es aproximadamente 10 segundos. Tenemos que recordar que para cada nodo frontera debemos calcular la ruta más corta entre él y los demás nodos frontera de su misma zona. Con los datos anteriores, podemos saber aproximadamente cuántas veces tenemos que calcular la ruta más corta para un nodo. Para cada nodo frontera debemos ejecutar el algoritmo de Dijkstra 500 veces. El tiempo estimado para este cálculo es de 5 mil segundos, lo que corresponde a unos 84 minutos aproximadamente. Como tenemos que realizar este cálculo para cada uno de los nodos frontera de una zona, el tiempo necesario para procesar una zona es de 42 mil minutos, lo que corresponde a unas 700 horas. Como se deben procesar 80 diferentes zonas, el tiempo requerido es aproximadamente de 56 mil horas, lo que corresponde a 2300 días. Por lo tanto el tiempo requerido para procesar la información requerida para que el algoritmo A-Estrella pueda ser utilizado es de aproximadamente 70 meses. El tiempo requerido para procesar la información anterior se puede reducir, si realizamos los calculos necesarios en paralelo. Esto se puede realizar, ya que estamos resolviendo problemas indendientes. La solucion del problema de calcular la ruta más corta entre dos nodos nodos frontera, no depende de la solucion de este problema para otro par de nodos. Teniendo en cuenta lo anterior podemos disminuir el tiempo requerido en una proporcion inversamente proporcional al numero de procesadores empleados para el calculo de esta información. Se realizaron algunas comparaciones para poder determinar cuál algoritmo representa una mejor opción para resolver el problema de calcular la ruta más corta en la malla vial de la ciudad de Bogotá. La primera comparación que se realizo fue la de consumo de memoria. Para poder realizar esta comparación, fue necesario utilizar las mismas estructuras de datos en los algoritmos en cuestión. Adicionalmente se realizaron adaptaciones a los dos algoritmos de tal forma que los dos fueran casi idénticos. El algoritmo que más cambió fue el algoritmo de Dijkstra. Este algoritmo se muestra a continuación escrito formalmente. Dijkstra (G, nodo-Inicial, nodo-Final) {Precondición Q: G: Grafo ∧ nodo-Inicial, nodo-Final ∈ G) 1: Disponibles: = φ 2: Analizados: = φ 3: Nodo-Actual: = Configurar-Nodo (nodo-Inicial, 0) 4: Nodo-Actual: = Asignar-Padre (nodo-Inicial, nodo-Inicial, Nodo-Actual) 5: Disponibles: = Disponibles ∪ Nodo {Invariante P: Disponibles: contiene nodos con G mínimo a partir del nodoInicial. ∧ Analizados: Analizados: contiene el SP entre nodo-Inicial y el ultimo nodo. } {Cota t: G. Número – (Disponibles. Número + Analizados. Número) +1} 6: DO Disponibles ≠ φ Æ 7: 8: Nodo-Actual: = Extraer-Nodo-Menor-W (Disponibles) Analizados: = Analizados ∪ Nodo-Actual 9: IF Nodo-Actual. Nodo = nodo-Final Æ 10: Disponibles: = φ 11: Nodo-Actual. Nodo ≠ nodo-Final Æ 12: Sucesores: = Obtener-Sucesores (Nodo-Actual. Nodo) {Precondición Q:} 13: 14: 15: 16: Contador: = 0 {Invariante P:} {Cota T: Sucesores. Número – Contador +1} DO Contador < Sucesores. Número Æ Sucesor: = Sucesores [Contador] Contador: = Contador +1 17: IF Contiene (Analizados, Sucesor) 18: 19: Æ SKIP NOT Contiene (Analizados, Sucesor) Æ Sucesor-Actual: = Configurar-Nodo (Sucesor, NodoActual. Costo + Costo (Nodo-Actual. Nodo, Sucesor)) Sucesor-Actual: = Asignar-Padre (Sucesor, Nodo-Actual. Nodo, Sucesor-Actual) 20: 21: 22: IF NOT Contiene (Disponibles, Sucesor) Æ Disponibles: = Disponibles ∪ Sucesor-Actual 23: 24: Contiene (Disponibles, Sucesor) Æ IF Disponibles. G (Sucesor) > Nodo-Actual. Costo + Costo (Nodo-Actual. Nodo, Sucesor)Æ Eliminar (Disponibles, Sucesor) Disponibles: = Disponibles ∪Sucesor-Actual FI 25: 26: FI FI FI OD {Poscondición R:} OD {Poscondición R: Analizados: contiene el SP entre nodo-Inicial y el ultimo nodo. ∧ Disponibles: contiene nodos con G mínimo a partir del nodo-Inicial.} Al escribir el algoritmo de Dijkstra de esta forma, la única diferencia que existe con respecto al algoritmo A-Estrella es la séptima línea. En esta línea se selecciona el nodo que tenga menor costo acumulado en la lista de nodos disponibles. En el algoritmo A-Estrella, en esta línea se utiliza la heurística para seleccionar cuál nodo debe ser seleccionado. Como logramos reescribir el algoritmo de Dijkstra en forma del algoritmo AEstrella, y adicionalmente usamos las mismas estructuras de datos en los dos algoritmos, podemos concluir que utilizan la misma cantidad de memoria. Otro método que utilizamos para realizar una comparación entre los dos algoritmos fue la de analizar el espacio de búsqueda de los dos algoritmos. Con el espacio de búsqueda nos referimos al número de nodos que fueron marcados para encontrar la solución del problema. Las condiciones de la simulación fueron las siguientes. Se realizaron alrededor de 50 simulaciones. En estas simulaciones, se calculó el camino más corto entre dos puntos separados por una distancia (número de nodos en la ruta más corta), que varía de los 100 a los 200 nodos. Las condiciones presentes en estos 50 caminos son muy similares en topología, ya que se escogieron zonas geográficas dentro de la malla vía de la ciudad de Bogotá, que estuvieran pobladas en lo posible uniformemente. La siguiente gráfica muestra los resultados obtenidos de esta comparación. Espacio de Busqueda 30000 Nodos Analiz ados 25000 20000 Dijk stra 15000 A -E strella 10000 5000 0 Distancia del Objetivo Figura 21. Espacio de búsqueda del algoritmo de Dijkstra y el algoritmo A-Estrella. En esta gráfica podemos observar que el espacio de búsqueda del algoritmo de Dijkstra es siempre mayor al espacio de búsqueda del algoritmo A-Estrella. Relac ion del Espac io de Bus queda de Dijk s tra y A-Es trella 4 3,5 3 2,5 2 d 1,5 1 0,5 0 Dista ncia del Objetivo Figura 22. Gráfica de comparación entre el espacio de búsqueda del algoritmo de Dijkstra y el algoritmo A-Estrella. En la gráfica anterior podemos ver que el espacio de búsqueda del algoritmo de Dijkstra en la mayoria de las veces es por lo menos el doble que el del algoritmo AEstrella. Si cambiamos un poco las condiciones de la simulación de estos algoritmos, la relación en el tamaño de búsqueda de estos dos algoritmos cambia considerablemente. Por ejemplo, si queremos calcular la ruta más corta entre los puntos los cuales corresponden a extremos opuestos del mapa, el espacio de busqueda para el algoritmo de Dijkstra pude llegar a abarcar la totalidad del grafo, ya que el espacio de busqueda de este algoritmo crece en forma radial como lo habiamos mencionado anteriormente. Para el algoritmo de A-Estrella, el espacio de busqueda para este caso podria llegar a ser del mismo tamaño que el del algoritmo de Dijkstra, ya que aunque el espacio de busqueda de este algoritmo crece en direccion del nodo destino, en el caso de dos nodos en extremos opuestos podria abarcar casi la totalidad del los nodos del grafo. Un ejemplo extremo para este caso es un grafo en el cual representa una linea, la cual no se intersecta consigo misma en ninguna parte, y queremos calcular la ruta optima entre los dos extremos del grafo. En casos como estos el algoritmo de A-Estrella sufre una baja en su desempeño, hasta un punto en el cual se comporta igual que el algoritmo de Dijkstra. Recordemos que el espacio de búsqueda tiene una relación directa con el desempeño en velocidad del algoritmo. Entre menor sea el espacio de búsqueda, menor será el tiempo necesario para encontrar la solución del problema propuesto. A continuación vamos a hacer una comparación entre los dos algoritmos con respecto al tiempo empleado para la solución del problema, y la distancia del camino objetivo. El ambiente de esta simulación comprende 100 casos, los cuáles contemplan todos los posibles ambientes, para lograr obtener un aproximado para el caso promedio. Tiempo Vs Distancia 300 Tiempo (Segundos) 250 200 150 Dijkstra A -Estrella 100 50 0 -50 Distancia del Objetivo Figura 23. Gráfica de comparación de tiempos con respecto a la distancia del camino optimo. En esta gráfica podemos ver que el tiempo requerido para solucionar el problema del camino más corto en la malla vial de la ciudad de Bogotá, es mucho mayor para el algoritmo de Dijkstra en el caso promedio. A continuación vamos a presentar una gráfica donde realizamos una comparación entre el tiempo requerido para calcular el camino más corto usando el algoritmo de Dijkstra y el algoritmo A-Estrella. Relacion de T iempo Vs Distancia 40 35 30 25 20 15 10 5 0 -5 Distancia Figura 24. Relación entre el tiempo empleado por el algoritmo de Dijkstra y el algoritmo A-Estrella. Como podemos ver, la relación entre el tiempo requerido para solucionar el problema propuesto en el caso promedio es mayor siempre para el algoritmo de Dijkstra. De esta gráfica podemos concluir que el algoritmo de Dijkstra necesita alrededor de 8.5 veces más tiempo para solucionar el problema de calcular la ruta más corta entre dos puntos en la malla vial de la ciudad de Bogotá, en el caso promedio. ANEXO I Código fuente del algoritmo que genera el grafo correspondiente a la malla vial de la ciudad de Bogotá, usando el modelo simple. Precondición: featureLayer debe referenciar el FeatureLayer del mapa de la malla vial de la ciudad de Bogotá. Postcondición: Se crea un grafo con la informacion geografica de la ciudad de Bogotá. Descripción: El grafo generado es almacenado en un objeto tipo directorio desarrollada por Microsoft, llamado “ Scripting Dictionary”. Este grafo no tiene encuenta restricciones en los giros. El modelo utilizado por el grafo es el modelo simple. Public Sub generarGrafoSimple(featurelay er As IFeatureLay er) Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim Dim BogotáFeatureClass As IFeatureClass resultQuery As IFeatureCursor filtro As IQuery Filter fila As IFeature campos As IFields posFNODE As Integer posTNODE As Integer posLENGHT As Integer posFromLeft As Integer posToLeft As Integer posFromRight As Integer posToRight As Integer posOneWay As Integer posTipoVia As Integer nodo1 As Nodo nodoAdj As Adjacente poly As IPoly line fpoint As IPoint tpoint As IPoint startTime As Single endTime As Single startTime = Timer ' Definimos todo lo necesario para generar el grafo Set grafoSimple = CreateObject("Scripting.Dictionary ") Set BogotáFeatureClass = featurelay er.FeatureClass Set campos = BogotáFeatureClass.Fields posFNODE = campos.FindField("FNODE_") posTNODE = campos.FindField("TNODE_") posLENGHT = campos.FindField("SHAPE_Leng") posOneWay = campos.FindField("ONE_WAY") posTipoVia = campos.FindField("TIPO_VIA") Set filtro = New Query Filter Set resultQuery = BogotáFeatureClass.Search(filtro, False) Set fila = resultQuery .NextFeature Do Until fila Is Nothing Set poly = fila.shape Set fpoint = poly .FromPoint Set tpoint = poly .ToPoint If grafoSimple.Exists(fila.Value(posFNODE)) And grafoSimple.Exists(fila.Value(posTNODE)) Then ' NODO FROM Y NODO T EXISTEN If fila.Value(posFNODE) <> fila.Value(posTNODE) Then ' MODIFICAMOS EN NODO FROM Set nodo1 = grafoSimple.Item(fila.Value(posFNODE)) Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posTNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(tpoint.X, tpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarViejoNodoSucesor fila, nodoAdj, posTipoVia ' MODIFICAMOS EL NODO FROM EN EL DICCIONARIO Set grafoSimple.Item(fila.Value(posFNODE)) = nodo1 ' MODIFICAMOS EN NODO TO If fila.Value(posOneWay ) = "B" Then Set nodo1 = grafoSimple.Item(fila.Value(posTNODE)) Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posFNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(fpoint.X, fpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarViejoNodoSucesor fila, nodoAdj, posTipoVia ' MODIFICAMOS EL NODO TO EN EL DICCIONARIO Set grafoSimple.Item(fila.Value(posTNODE)) = nodo1 End If End If Else If grafoSimple.Exists(fila.Value(posFNODE)) And Not grafoSimple.Exists(fila.Value(posTNODE)) Then ' NODO FROM EXISTE Y NODO TO NO EXISTE ' MODIFICAMOS EN NODO FROM Set nodo1 = grafoSimple.Item(fila.Value(posFNODE)) Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posTNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(tpoint.X, tpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarViejoNodoSucesor fila, nodoAdj, posTipoVia ' MODIFICAMOS EL NODO FROM A EL DICCIONARIO Set grafoSimple.Item(fila.Value(posFNODE)) = nodo1 ' CREAMOS EL NODO TO Set nodo1 = New Nodo With nodo1 .idEsri = fila.Value(posTNODE) .posX = tpoint.X .posY = tpoint.Y .idZona = generarZona(tpoint.X, tpoint.Y) End With If fila.Value(posOneWay ) = "B" Then Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posFNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(fpoint.X, fpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarNuevoNodoSucesor fila, nodoAdj End If ' AGREGAMOS EL NODO TO A EL DICCIONARIO grafoSimple.Add fila.Value(posTNODE), nodo1 Else If Not grafoSimple.Exists(fila.Value(posFNODE)) And grafoSimple.Exists(fila.Value(posTNODE)) Then ' NODO FROM NO EXISTE Y NODO TO EXISTE ' CREAMOS EL NODO FROM Set nodo1 = New Nodo With nodo1 .idEsri = fila.Value(posFNODE) .posX = fpoint.X .posY = fpoint.Y .idZona = generarZona(fpoint.X, fpoint.Y) End With Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posTNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(tpoint.X, tpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarNuevoNodoSucesor fila, nodoAdj ' AGREGAMOS EL NODO FROM A EL DICCIONARIO grafoSimple.Add fila.Value(posFNODE), nodo1 ' MODIFICAMOS EN NODO TO If fila.Value(posOneWay ) = "B" Then Set nodo1 = grafoSimple.Item(fila.Value(posTNODE)) Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posFNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(fpoint.X, fpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarViejoNodoSucesor fila, nodoAdj, posTipoVia ' MODIFICAMOS EL NODO TO EN EL DICCIONARIO Set grafoSimple.Item(fila.Value(posTNODE)) = nodo1 End If Else ' NO EXISTE NINGUNO DE LOS NODOS If fila.Value(posFNODE) <> fila.Value(posTNODE) Then ' CREAMOS EL NODO FROM Set nodo1 = New Nodo With nodo1 .idEsri = fila.Value(posFNODE) .posX = fpoint.X .posY = fpoint.Y .idZona = generarZona(fpoint.X, fpoint.Y) End With Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posTNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(tpoint.X, tpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarNuevoNodoSucesor fila, nodoAdj ' AGREGAMOS EL NODO FROM A EL DICCIONARIO grafoSimple.Add fila.Value(posFNODE), nodo1 ' CREAMOS EL NODO TO Set nodo1 = New Nodo With nodo1 .idEsri = fila.Value(posTNODE) .posX = tpoint.X .posY = tpoint.Y .idZona = generarZona(tpoint.X, tpoint.Y) End With If fila.Value(posOneWay ) = "B" Then Set nodoAdj = New Adjacente With nodoAdj .idEsri = fila.Value(posFNODE) .costo = fila.Value(posLENGHT) .idZona = generarZona(fpoint.X, fpoint.Y) Set .shape = fila.shape .direccion = fila.Value(posTipoVia) End With nodo1.agregarNuevoNodoSucesor fila, nodoAdj End If ' AGREGAMOS EL NODO TO A EL DICCIONARIO grafoSimple.Add fila.Value(posTNODE), nodo1 End If End If End If End If Set fila = resultQuery .NextFeature Loop endTime = Timer MsgBox "tiempo de proceso " & endTime - startTime & " segundos" End Sub ANEXO 2. Código fuente del grafo que representa la malla vial de la ciudad de Bogotá, por sectores geográficos. Precondición: grafoSimple es una variable global que referencia el grafo de la ciudad de Bogotá. Este grafo es generado por el algoritmo que se encuentra en el Anexo 1. Postcondición: Se crea un grafo con la informacion geografica de la ciudad de Bogotá, los nodos son agrupados por zonas geograficas. Descripción: El grafo generado es almacenado en un objeto tipo directorio desarrollada por Microsoft, llamado “ Scripting Dictionary”. Este grafo no tiene encuenta restricciones en los giros. El modelo utilizado por el grafo es el modelo simple. Los nodos de este grafo tienen un atributo que identifica a que zona pertenecen. Public Sub generarGrafoZonas() Dim arregloGrafoSimpleKey s Dim nodoGrafoSimpleKey As Long Dim nodoSimple As Nodo Dim sucesoresKey s Dim sucesorKey As Long Dim sucesorAdj As Adjacente Dim numSucesores As Integer Dim nodosZona Dim nodosZonaKey s Dim nodoZonaIDKey As Long Dim nodoZonaEsriKey As Long Dim nodoSucesorZonaKey As Long Dim nodoZA As NodoZonaSimple Dim nodoActualZona As NodoZona Dim costo As PathResultado Dim i As Long Dim j As Long Dim z As Long Dim k As Integer Set grafoZonas = CreateObject("Scripting.Dictionary ") Set nodosAgrupadosPorZona = CreateObject("Scripting.Dictionary ") ' Iteramos sobre todos los nodos del grafo, para seleccionar y agrupar los nodos que sean de frontera arregloGrafoSimpleKey s = grafoSimple.key s For i = 0 To grafoSimple.Count - 1 nodoGrafoSimpleKey = arregloGrafoSimpleKey s(i) Set nodoSimple = grafoSimple.Item(nodoGrafoSimpleKey ) If nodoSimple.esNodoFrontera Then Set nodoZA = New NodoZonaSimple With nodoZA .idEsri = nodoSimple.idEsri End With If nodosAgrupadosPorZona.Exists(nodoSimple.idZona) Then ' YA EXISTE LA ZONA ' obtenemos el dirZona Set nodosZona = nodosAgrupadosPorZona.Item(nodoSimple.idZona) ' adicionamos el nodoZona a dirZona nodosZona.Add nodoZA.idEsri, nodoZA ' modificamos el dirZona que esta en grafoZonas Set nodosAgrupadosPorZona.Item(nodoSimple.idZona) = nodosZona Else ' NO EXISTE LA ZONA ' creamos el dirZona Set nodosZona = CreateObject("Scripting.Dictionary ") ' adicionamos el nodoZona a dirZona nodosZona.Add nodoZA.idEsri, nodoZA 'adicionamos el dirZona a grafoZonas nodosAgrupadosPorZona.Add nodoSimple.idZona, nodosZona End If End If Next ' Falta calcular los costos entre todos los nodos frontera ' nodosZonaKey s es un arreglo con las zonas nodosZonaKey s = nodosAgrupadosPorZona.key s MsgBox "número de zonas = " & nodosAgrupadosPorZona.Count For i = 0 To nodosAgrupadosPorZona.Count - 1 nodoZonaIDKey = nodosZonaKey s(i) ' nodosZona tiene todos los nodos frontera de una zona Set nodosZona = nodosAgrupadosPorZona.Item(nodoZonaIDKey ) ' nodosZonaActualKey s es un arreglo con los ids de los nodos frontera de la zona nodosZonaKey s = nodosZona.key s For j = 0 To nodosZona.Count - 1 nodoZonaEsriKey = nodosZonaKey s(j) ' nodoZonaActual es un nodo frontera de la zona Set nodoZA = nodosZona.Item(nodoZonaEsriKey ) Set nodoActualZona = New NodoZona With nodoActualZona .idEsri = nodoZA.idEsri .idZona = nodoZonaIDKey Set .sucesores = CreateObject("Scripting.Dictionary ") End With For z = 0 To nodosZona.Count - 1 nodoSucesorZonaKey = nodosZonaKey s(z) ' es un nodo frontera diferente de la misma zona ' estos dos son ID's If nodoSucesorZonaKey <> nodoZonaEsriKey Then Set sucesorAdj = New Adjacente ' calculamos el costo del path entre nodoZonaActual y nodoSucesorZonaKey Set costo = pathFinder_G_Path_AEstrellaZona(nodoZA.idEsri, nodoSucesorZonaKey ) If Not (costo Is Nothing) Then With sucesorAdj .idEsri = nodoSucesorZonaKey .idZona = nodoActualZona.idZona .costo = costo.costo Set .shape = costo.path Set .shape = Nothing End With If nodoActualZona.sucesores.Exists(nodoSucesorZonaKey ) Then MsgBox "hay un error se intento agregar un sucesor existente en el grafo de zonas" Else ' agregamos el sucesor frontera al nodo frontera actual nodoActualZona.sucesores.Add sucesorAdj.idEsri, sucesorAdj End If End If End If Next Set nodoSimple = grafoSimple.Item(nodoActualZona.idEsri) 'tenemos que adicionar los costos de los arcos que son sucesores de otra zona numSucesores = nodoSimple.númeroSucesores For k = 0 To numSucesores - 1 Set sucesorAdj = nodoSimple.obtenerSucesor(k) ' el sucesor debe ser de otra zona If sucesorAdj.idZona <> nodoActualZona.idZona Then ' agregamos uno de los nodos de otra sona nodoActualZona.sucesores.Add sucesorAdj.idEsri, sucesorAdj End If Next If grafoZonas.Exists(nodoActualZona.idEsri) Then MsgBox "hay un error se intento adicionar un nodo existente en el grafo de zonas" Else ' adicionamos el nodo totalmente listo al grafo de zonas grafoZonas.Add nodoActualZona.idZona, nodoActualZona End If Next Next End Sub ANEXO 3. Código fuente del cálculo de restricciones de giro. Precondición: featureLayer debe referenciar el FeatureLayer del mapa de la malla vial de la ciudad de Bogotá. Postcondición: Se genera una lista con las restricciones en los giros de la malla vial de la ciudad de Bogotá. Descripción: El grafo generado es almacenado en un objeto tipo directorio desarrollada por Microsoft, llamado “ Scripting Dictionary”. Este grafo no tiene encuenta restricciones en los giros. El modelo utilizado por el grafo es el modelo simple. Public Sub generarListaRestricciones(featurelayer As IFeatureLayer) Dim BogotáFeatureClass As IFeatureClass Dim resultQuery As IFeatureCursor Dim filtro As IQueryFilter Dim fila As IFeature Dim campos As IFields Dim posToLeft As Integer Dim posToRight As Integer Dim posFNODE As Integer Dim posTNODE As Integer Dim posTipoVia As Integer Dim nodoFromSimple As Nodo Dim nodoToSimple As Nodo Dim nodoLeft As Nodo Dim nodoRight As Nodo Dim idLeft As Long Dim idRight As Long Set listaRestricciones = CreateObject("Scripting.Dictionary") Set BogotáFeatureClass = featurelayer.FeatureClass Set campos = BogotáFeatureClass.Fields posToRight = campos.FindField("TORIGHT") posToLeft = campos.FindField("TOLEFT") posFNODE = campos.FindField("FNODE_") posTNODE = campos.FindField("TNODE_") posTipoVia = campos.FindField("TIPO_VIA") Set filtro = New QueryFilter Set resultQuery = BogotáFeatureClass.Search(filtro, False) Set fila = resultQuery.NextFeature Do Until fila Is Nothing If fila.Value(posFNODE) <> fila.Value(posTNODE) Then Set nodoFromSimple = grafoSimple.Item(fila.Value(posFNODE)) Set nodoToSimple = grafoSimple.Item(fila.Value(posTNODE)) If fila.Value(posToLeft) = -1 Then idLeft = idSucesorLeft(nodoFromSimple, nodoToSimple, fila.Value(posTipoVia)) If idLeft <> -1 Then If Not listaRestricciones.Exists(nodoFromSimple.idEsri & "," & nodoToSimple.idEsri & "," & idLeft) Then listaRestricciones.Add nodoFromSimple.idEsri & "," & nodoToSimple.idEsri & "," & idLeft, True End If End If End If If fila.Value(posToRight) = -1 Then idRight = idSucesorRight(nodoFromSimple, nodoToSimple, fila.Value(posTipoVia)) If idRight <> -1 Then If Not listaRestricciones.Exists(nodoFromSimple.idEsri & "," & nodoToSimple.idEsri & "," & idRight) Then listaRestricciones.Add nodoFromSimple.idEsri & "," & nodoToSimple.idEsri & "," & idRight, True End If End If End If End If Set fila = resultQuery.NextFeature Loop End Sub ANEXO 4. Código fuente del algoritmo de Dijkstra. Precondición: idInitNode, idEndNode deben tener un valor entero positivo, menor a 132.538 grafoSimple es una variable global, la cual debe hacer referencia al grafo simple generado por el algoritmo del Anexo 1. Postcondición: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Descripción: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Para soluciuonarlo utiliza el algoritmo de Dijkstra. Se utiliza el modelo simple del grafo. Public Function pathFinderDijkstra(idInitNode As Long, idEndNode As Long) As IPoly line Dim disponibles Dim analizados Dim nodoActual As NodoDijkstra Dim sucesorActual As NodoDijkstra Dim geometry As IGeometry Dim segmentos As ISegmentCollection Dim poly line As IPoly line Dim i As Integer Dim numSucesores As Integer Dim contador As Long Dim startTime As Single Dim endTime As Single startTime = Timer If grafoSimple Is Nothing Then MsgBox "ERROR GRAFO SIMPLE ES NULL" Set pathFinderDijkstra = Nothing Exit Function End If Set disponibles = CreateObject("Scripting.Dictionary ") Set analizados = CreateObject("Scripting.Dictionary ") Set nodoActual = New NodoDijkstra With nodoActual .costoG = 0 Set .nodoGrafo = grafoSimple.Item(idInitNode) Set .nodoPadre = Nothing Set .shapePadre = Nothing End With disponibles.Add nodoActual.nodoGrafo.idEsri, nodoActual Do Until disponibles.Count = 0 Set nodoActual = extraerNodoConMenorW(disponibles) disponibles.Remove (nodoActual.nodoGrafo.idEsri) analizados.Add nodoActual.nodoGrafo.idEsri, nodoActual If nodoActual.nodoGrafo.idEsri = idEndNode Then disponibles.RemoveAll Else numSucesores = nodoActual.nodoGrafo.númeroSucesores For i = 0 To numSucesores - 1 If Not analizados.Exists(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Then Set sucesorActual = New NodoDijkstra With sucesorActual Set .nodoGrafo = grafoSimple.Item(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Set .nodoPadre = nodoActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesor(i).shape .costoG = nodoActual.costoG + nodoActual.nodoGrafo.obtenerSucesor(i).costo End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next End If Loop If nodoActual.nodoGrafo.idEsri = idEndNode Then endTime = Timer MsgBox "tiempo de proceso " & endTime - startTime & " segundos" MsgBox "el número de nodos analizados fue " & analizados.Count Set segmentos = New poly line Set sucesorActual = analizados.Item(idEndNode) Set nodoActual = analizados.Item(sucesorActual.nodoPadre.nodoGrafo.idEsri) contador = 1 MsgBox "El costo total del camino es " & sucesorActual.costoG Do Until nodoActual Is Nothing Set geometry = sucesorActual.shapePadre segmentos.AddSegmentCollection geometry Set sucesorActual = nodoActual If Not (nodoActual.nodoPadre Is Nothing) Then Set nodoActual = analizados.Item(nodoActual.nodoPadre.nodoGrafo.idEsri) Else Set nodoActual = Nothing End If contador = contador + 1 Loop Set poly line = segmentos poly line.Simplify Network Set pathFinderDijkstra = poly line MsgBox "la distancia es " & contador Else MsgBox "NO EXISTE UN CAMINO ENTRE EL NODO INICIAL Y EL NODO FINAL" Set pathFinderDijkstra = Nothing End If End Function Private Function extraerNodoConMenorW(disponibles) As NodoDijkstra Dim arregloKey s Dim i As Long Dim idActual As Long Dim nodoActual As NodoDijkstra Dim wMinimo As Double Dim nodoMinimo As NodoDijkstra wMinimo = 50000 'infinito arregloKey s = disponibles.key s For i = 0 To disponibles.Count - 1 idActual = arregloKey s(i) Set nodoActual = disponibles.Item(idActual) If wMinimo > nodoActual.costoG Then Set nodoMinimo = nodoActual wMinimo = nodoActual.costoG End If Next Set extraerNodoConMenorW = nodoMinimo End Function ANEXO 5. Código fuente del algoritmo A-Estrella simple. Precondición: idInitNode, idEndNode deben tener un valor entero positivo, menor a 132.538 grafoSimple es una variable global, la cual debe hacer referencia al grafo simple generado por el algoritmo del Anexo 1. Postcondición: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Descripción: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Para soluciuonarlo utiliza el algoritmo de A-Estrella. Se utiliza el modelo simple del grafo. Public Function pathFinderAEstrella(idInitNode As Long, idEndNode As Long) As IPoly line Dim geometry As IGeometry Dim segmentos As ISegmentCollection Dim poly line As IPoly line Dim disponibles Dim analizados Dim nodoActual As NodoAStar Dim sucesorActual As NodoAStar Dim nodoFinal As Nodo Dim i As Integer Dim numSucesores As Integer Dim contador As Long Dim startTime As Single Dim endTime As Single startTime = Timer If grafoSimple Is Nothing Then MsgBox "ERROR GRAFO SIMPLE ES NULL" Set pathFinderAEstrella = Nothing Exit Function End If Set nodoFinal = grafoSimple.Item(idEndNode) Set disponibles = CreateObject("Scripting.Dictionary ") Set analizados = CreateObject("Scripting.Dictionary ") Set nodoActual = New NodoAStar With nodoActual .costoG = 0 Set .nodoGrafo = grafoSimple.Item(idInitNode) Set .nodoPadre = Nothing Set .shapePadre = Nothing End With disponibles.Add nodoActual.nodoGrafo.idEsri, nodoActual Do Until disponibles.Count = 0 Set nodoActual = extraerNodoConMenorF(disponibles, nodoFinal) disponibles.Remove (nodoActual.nodoGrafo.idEsri) analizados.Add nodoActual.nodoGrafo.idEsri, nodoActual If nodoActual.nodoGrafo.idEsri = idEndNode Then disponibles.RemoveAll Else numSucesores = nodoActual.nodoGrafo.númeroSucesores For i = 0 To numSucesores - 1 If Not analizados.Exists(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Then Set sucesorActual = New NodoAStar With sucesorActual Set .nodoGrafo = grafoSimple.Item(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Set .nodoPadre = nodoActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesor(i).shape .costoG = nodoActual.costoG + nodoActual.nodoGrafo.obtenerSucesor(i).costo End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next End If Loop If nodoActual.nodoGrafo.idEsri = idEndNode Then endTime = Timer MsgBox "tiempo de proceso " & endTime - startTime & " segundos" MsgBox "el número de nodos analizados fue " & analizados.Count Set segmentos = New poly line Set sucesorActual = analizados.Item(idEndNode) Set nodoActual = analizados.Item(sucesorActual.nodoPadre.nodoGrafo.idEsri) MsgBox "El costo total del camino es " & sucesorActual.costoG contador = 1 Do Until nodoActual Is Nothing Set geometry = sucesorActual.shapePadre segmentos.AddSegmentCollection geometry Set sucesorActual = nodoActual If Not (nodoActual.nodoPadre Is Nothing) Then Set nodoActual = analizados.Item(nodoActual.nodoPadre.nodoGrafo.idEsri) Else Set nodoActual = Nothing End If contador = contador + 1 Loop Set poly line = segmentos poly line.Simplify Network Set pathFinderAEstrella = poly line MsgBox "la distancia es " & contador Else MsgBox "NO EXISTE UN CAMINO ENTRE EL NODO INICIAL Y EL NODO FINAL" Set pathFinderAEstrella = Nothing End If End Function Private Function extraerNodoConMenorF(disponibles, nodoFinal As Nodo) As NodoAStar Dim arregloKey s Dim i As Long Dim idActual As Long Dim poly As IPoly line Dim x1 As Double Dim y 1 As Double Dim x2 As Double Dim y 2 As Double Dim h As Double Dim fpoint As IPoint Dim nodoActual As NodoAStar Dim fMinimo As Double Dim nodoMinimo As NodoAStar fMinimo = 50000 'infinito arregloKey s = disponibles.key s For i = 0 To disponibles.Count - 1 idActual = arregloKey s(i) Set nodoActual = disponibles.Item(idActual) x1 = nodoActual.nodoGrafo.posX y 1 = nodoActual.nodoGrafo.posY x2 = nodoFinal.posX y 2 = nodoFinal.posY h = (x1 - x2) * (x1 - x2) + (y 1 - y 2) * (y 1 - y 2) h = Math.Sqr(h) If fMinimo > (nodoActual.costoG + h) Then Set nodoMinimo = nodoActual fMinimo = (nodoActual.costoG + h) End If Next Set extraerNodoConMenorF = nodoMinimo End Function ANEXO 6. Codigo fuente del algoritmo de Dijkstra usando el modelo multigrafo. Precondición: idInitNode, idEndNode deben tener un valor entero positivo, menor a 132.538 grafoSimple es una variable global, la cual debe hacer referencia al grafo simple generado por el algoritmo del Anexo 1. Postcondición: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Descripción: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Para soluciuonarlo utiliza el algoritmo de Dijkstra. Se utiliza el modelo multigrafo. Public Function pathFinderDijkstraRestricciones(idInitNode As Long, idEndNode As Long) As IPoly line Dim geometry As IGeometry Dim segmentos As ISegmentCollection Dim poly line As IPoly line Dim disponibles Dim analizados Dim nodoActual As NodoDijkstraMultiple Dim sucesorActual As NodoDijkstraMultiple Dim nodoFinal As Nodo Dim i As Integer Dim numSucesores As Integer Dim arcoFinal As String Dim contador As Long Dim startTime As Single Dim endTime As Single startTime = Timer If grafoSimple Is Nothing Then MsgBox "ERROR GRAFO SIMPLE ES NULL" Set pathFinderDijkstraRestricciones = Nothing Exit Function End If Set nodoFinal = grafoSimple.Item(idEndNode) Set disponibles = CreateObject("Scripting.Dictionary ") Set analizados = CreateObject("Scripting.Dictionary ") Set nodoActual = New NodoDijkstraMultiple With nodoActual .costoG = 0 Set .nodoGrafo = grafoSimple.Item(idInitNode) Set .nodoPadre = Nothing Set .shapePadre = Nothing .idMultigrafo = nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.idEsri .idMultiPadre = "" End With disponibles.Add nodoActual.idMultigrafo, nodoActual Do Until disponibles.Count = 0 Set nodoActual = extraerNodoConMenorWMultiple(disponibles) If nodoActual Is Nothing Then MsgBox "que putas paso? Dijkstra" End If disponibles.Remove (nodoActual.idMultigrafo) analizados.Add nodoActual.idMultigrafo, nodoActual If nodoActual.nodoGrafo.idEsri = idEndNode Then disponibles.RemoveAll Else numSucesores = nodoActual.nodoGrafo.númeroSucesores For i = 0 To numSucesores - 1 If Not analizados.Exists(nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Then Set sucesorActual = New NodoDijkstraMultiple With sucesorActual Set .nodoGrafo = grafoSimple.Item(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Set .nodoPadre = nodoActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesor(i).shape .costoG = nodoActual.costoG + nodoActual.nodoGrafo.obtenerSucesor(i).costo .idMultigrafo = nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.obtenerSucesor(i).idEsri .idMultiPadre = nodoActual.idMultigrafo End With If Not disponibles.Exists(sucesorActual.idMultigrafo) Then disponibles.Add sucesorActual.idMultigrafo, sucesorActual Else If disponibles.Item(sucesorActual.idMultigrafo).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.idMultigrafo) = sucesorActual End If End If End If Next End If Loop If nodoActual.nodoGrafo.idEsri = idEndNode Then endTime = Timer MsgBox "tiempo de proceso " & endTime - startTime & " segundos" MsgBox "el número de nodos analizados fue " & analizados.Count Set segmentos = New poly line Set sucesorActual = analizados.Item(nodoActual.idMultigrafo) Set nodoActual = analizados.Item(nodoActual.idMultiPadre) MsgBox "El costo total del camino es " & sucesorActual.costoG contador = 1 Do Until nodoActual Is Nothing Set geometry = sucesorActual.shapePadre segmentos.AddSegmentCollection geometry Set sucesorActual = nodoActual If Not (nodoActual.nodoPadre Is Nothing) Then Set nodoActual = analizados.Item(nodoActual.idMultiPadre) Else Set nodoActual = Nothing End If contador = contador + 1 Loop Set poly line = segmentos poly line.Simplify Network Set pathFinderDijkstraRestricciones = poly line MsgBox "la distancia es " & contador Else MsgBox "NO EXISTE UN CAMINO ENTRE EL NODO INICIAL Y EL NODO FINAL" Set pathFinderDijkstraRestricciones = Nothing End If End Function ANEXO 7. Código fuente del algoritmo A-Estrella usando el modelo de multigrafo. Precondición: idInitNode, idEndNode deben tener un valor entero positivo, menor a 132.538 grafoSimple es una variable global, la cual debe hacer referencia al grafo simple generado por el algoritmo del Anexo 1. Postcondición: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Descripción: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Para soluciuonarlo utiliza el algoritmo de A-Estrella. Se utiliza el modelo simple del grafo. Public Function pathFinderAEstrellaRestricciones(idInitNode As Long, idEndNode As Long) As IPoly line Dim geometry As IGeometry Dim segmentos As ISegmentCollection Dim poly line As IPoly line Dim disponibles Dim analizados Dim nodoActual As NodoAStarMultiple Dim sucesorActual As NodoAStarMultiple Dim nodoFinal As Nodo Dim i As Integer Dim numSucesores As Integer Dim arcoFinal As String Dim contador As Long Dim startTime As Single Dim endTime As Single startTime = Timer If grafoSimple Is Nothing Then MsgBox "ERROR GRAFO SIMPLE ES NULL" Set pathFinderAEstrellaRestricciones = Nothing Exit Function End If Set nodoFinal = grafoSimple.Item(idEndNode) Set disponibles = CreateObject("Scripting.Dictionary ") Set analizados = CreateObject("Scripting.Dictionary ") Set nodoActual = New NodoAStarMultiple With nodoActual .costoG = 0 Set .nodoGrafo = grafoSimple.Item(idInitNode) Set .nodoPadre = Nothing Set .shapePadre = Nothing .idMultigrafo = nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.idEsri .idMultiPadre = "" End With disponibles.Add nodoActual.idMultigrafo, nodoActual Do Until disponibles.Count = 0 Set nodoActual = extraerNodoConMenorFMultiple(disponibles, nodoFinal) If nodoActual Is Nothing Then MsgBox "que putas paso?" End If disponibles.Remove (nodoActual.idMultigrafo) analizados.Add nodoActual.idMultigrafo, nodoActual If nodoActual.nodoGrafo.idEsri = idEndNode Then disponibles.RemoveAll Else numSucesores = nodoActual.nodoGrafo.númeroSucesores For i = 0 To numSucesores - 1 If Not analizados.Exists(nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Then Set sucesorActual = New NodoAStarMultiple With sucesorActual Set .nodoGrafo = grafoSimple.Item(nodoActual.nodoGrafo.obtenerSucesor(i).idEsri) Set .nodoPadre = nodoActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesor(i).shape .costoG = nodoActual.costoG + nodoActual.nodoGrafo.obtenerSucesor(i).costo .idMultigrafo = nodoActual.nodoGrafo.idEsri & "," & nodoActual.nodoGrafo.obtenerSucesor(i).idEsri .idMultiPadre = nodoActual.idMultigrafo End With If Not disponibles.Exists(sucesorActual.idMultigrafo) Then disponibles.Add sucesorActual.idMultigrafo, sucesorActual Else If disponibles.Item(sucesorActual.idMultigrafo).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.idMultigrafo) = sucesorActual End If End If End If Next End If Loop If nodoActual.nodoGrafo.idEsri = idEndNode Then endTime = Timer MsgBox "tiempo de proceso " & endTime - startTime & " segundos" MsgBox "el número de nodos analizados fue " & analizados.Count Set segmentos = New poly line Set sucesorActual = analizados.Item(nodoActual.idMultigrafo) Set nodoActual = analizados.Item(nodoActual.idMultiPadre) MsgBox "El costo total del camino es " & sucesorActual.costoG contador = 1 Do Until nodoActual Is Nothing Set geometry = sucesorActual.shapePadre segmentos.AddSegmentCollection geometry Set sucesorActual = nodoActual If Not (nodoActual.nodoPadre Is Nothing) Then Set nodoActual = analizados.Item(nodoActual.idMultiPadre) Else Set nodoActual = Nothing End If contador = contador + 1 Loop Set poly line = segmentos poly line.Simplify Network Set pathFinderAEstrellaRestricciones = poly line MsgBox "la distancia es " & contador Else MsgBox "NO EXISTE UN CAMINO ENTRE EL NODO INICIAL Y EL NODO FINAL" Set pathFinderAEstrellaRestricciones = Nothing End If End Function ANEXO 8. Código fuente del algoritmo A-Estrella por Sectores. Precondición: idInitNode, idEndNode deben tener un valor entero positivo, menor a 132.538 grafoSimple es una variable global, la cual debe hacer referencia al grafo simple generado por el algoritmo del Anexo 1. Postcondición: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Descripción: Soluciona el problema de encontrar la ruta más corta entre el nodo identificado con idInitNode y el nodo idEndNode. Para soluciuonarlo utiliza el algoritmo de A-Estrella por sectores. Se utiliza el modelo simple del grafo. Public Function pathFinderAEstrella_Zonas(idInitNode As Long, idEndNode As Long) As IPoly line Dim disponibles Dim analizados Dim sucesores Dim sucesoresKey s Dim i As Integer Dim sucesorKey As Long Dim nodoActual As NodoAStar Dim sucesorActual As NodoAStar Dim nodoFinal As Nodo Dim nodoInicial As Nodo Dim nodoZonaActual As NodoZona Dim sucesoresTotales Dim nodoAdj As Adjacente Dim camino As PathResultado Dim geometry As IGeometry Dim segmentos As ISegmentCollection Dim poly line As IPoly line If grafoSimple Is Nothing Then MsgBox "ERROR GRAFO SIMPLE ES NULL" Set pathFinderAEstrella_Zonas = Nothing Exit Function End If Set nodoFinal = grafoSimple.Item(idEndNode) Set nodoInicial = grafoSimple.Item(idInitNode) Set disponibles = CreateObject("Scripting.Dictionary ") Set analizados = CreateObject("Scripting.Dictionary ") Set nodoActual = New NodoAStar With nodoActual .costoG = 0 Set .nodoGrafo = grafoSimple.Item(idInitNode) Set .nodoPadre = Nothing Set .shapePadre = Nothing End With disponibles.Add nodoActual.nodoGrafo.idEsri, nodoActual Do Until disponibles.Count = 0 Set nodoActual = extraerNodoConMenorF(disponibles, nodoFinal) disponibles.Remove (nodoActual.nodoGrafo.idEsri) analizados.Add nodoActual.nodoGrafo.idEsri, nodoActual If nodoActual.nodoGrafo.idEsri = idEndNode Then disponibles.RemoveAll Else If nodoActual.nodoGrafo.idZona = nodoFinal.idZona And Not nodoActual.nodoGrafo.esNodoFrontera Then ' ES LA PARTE FINAL ' LOS DOS NODOS ESTAN DENTRO DE LA ZONA FINAL ' CASO 1 ' sucesores es del tipo (idEsri, idEsri) Set sucesores = generarSucesoresFrontera(nodoActual.nodoGrafo) ' esto es para verificar que no esta y a dentro de los sucesores If Not nodoFinal.esNodoFrontera Then sucesores.Add nodoFinal.idEsri, nodoFinal.idEsri End If sucesoresKey s = sucesores.key s For i = 0 To sucesores.Count - 1 sucesorKey = sucesoresKey s(i) If Not analizados.Exists(sucesorKey ) Then ' calculamos el costo del camino usando sólo nodos de la misma zona camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorKey ) Set sucesorActual = New NodoAStar With sucesorActual Set .nodoGrafo = grafoSimple.Item(sucesorKey ) Set .nodoPadre = nodoActual .costoG = nodoActual.costoG + camino.costo Set .shapePadre = camino.path End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next Else If nodoActual.nodoGrafo.idZona = nodoFinal.idZona And nodoActual.nodoGrafo.esNodoFrontera Then ' ES LA PARTE FINAL ' ESTAN EN LA MISMA ZONA, PERO EL INICIAL ES FRONTERA ' CASO 2 Set nodoZonaActual = grafoZonas.Item(nodoActual.nodoGrafo.idEsri) ' sucesoresTotales es del tipo (idEsri, Adjacente) Set sucesoresTotales = nodoZonaActual.sucesores camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, idEndNode) Set nodoAdj = New Adjacente With nodoAdj .idEsri = nodoFinal.idEsri .idZona = nodoFinal.idZona .costo = camino.costo End With ' esto es para verificar que no esta y a dentro de los sucesores If Not nodoFinal.esNodoFrontera Then sucesoresTotales.Add nodoAdj.idEsri, nodoAdj End If sucesoresKey s = sucesoresTotales.key s For i = 0 To sucesores.Count - 1 sucesorKey = sucesoresKey s(i) If Not analizados.Exists(sucesorKey ) Then Set nodoAdj = sucesoresTotales.Item(sucesorKey ) Set sucesorActual = New NodoAStar With sucesorActual Set .nodoGrafo = grafoSimple.Item(sucesorKey ) Set .nodoPadre = nodoActual .costoG = nodoActual.costoG + nodoAdj.costo End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then If sucesorActual.nodoGrafo.idZona = nodoFinal.idZona Then camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorActual.nodoGrafo.idEsri) With sucesorActual Set .shapePadre = camino.path End With Else With sucesorActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesorBy ID(sucesorActual.nodoGrafo.idEsri).shape End With End If disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then If sucesorActual.nodoGrafo.idZona = nodoFinal.idZona Then camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorActual.nodoGrafo.idEsri) With sucesorActual Set .shapePadre = camino.path End With Else With sucesorActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesorBy ID(sucesorActual.nodoGrafo.idEsri).shape End With End If Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next Else If (nodoActual.nodoGrafo.idZona <> nodoFinal.idZona) And nodoActual.nodoGrafo.esNodoFrontera Then ' ES LA PARTE INTERMEDIA ' ALGORITMO ENTRE ZONAS ' CASO 3 Set nodoZonaActual = grafoZonas.Item(nodoActual.nodoGrafo.idEsri) ' sucesoresTotales es del tipo (idEsri, Adjacente) Set sucesoresTotales = nodoZonaActual.sucesores sucesoresKey s = sucesoresTotales.key s For i = 0 To sucesores.Count - 1 sucesorKey = sucesoresKey s(i) If Not analizados.Exists(sucesorKey ) Then Set nodoAdj = sucesoresTotales.Item(sucesorKey ) Set sucesorActual = New NodoAStar With sucesorActual Set .nodoGrafo = grafoSimple.Item(sucesorKey ) Set .nodoPadre = nodoActual .costoG = nodoActual.costoG + nodoAdj.costo End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then If sucesorActual.nodoGrafo.idZona = nodoFinal.idZona Then camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorActual.nodoGrafo.idEsri) With sucesorActual Set .shapePadre = camino.path End With Else With sucesorActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesorBy ID(sucesorActual.nodoGrafo.idEsri).shape End With End If disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then If sucesorActual.nodoGrafo.idZona = nodoFinal.idZona Then camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorActual.nodoGrafo.idEsri) With sucesorActual Set .shapePadre = camino.path End With Else With sucesorActual Set .shapePadre = nodoActual.nodoGrafo.obtenerSucesorBy ID(sucesorActual.nodoGrafo.idEsri).shape End With End If Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next Else If (nodoActual.nodoGrafo.idZona <> nodoFinal.idZona) And Not nodoActual.nodoGrafo.esNodoFrontera Then ' ES LA PARTE INICIAL ' NODO INICIAL - NODOS FRONTERA ' CASO 4 Set sucesores = generarSucesoresFrontera(nodoActual.nodoGrafo) sucesoresKey s = sucesores.key s For i = 0 To sucesores.Count - 1 sucesorKey = sucesoresKey s(i) If Not analizados.Exists(sucesorKey ) Then ' calculamos el costo del camino usando sólo nodos de la misma zona camino = pathFinder_G_Path_AEstrellaZona(nodoActual.nodoGrafo.idEsri, sucesorKey ) Set sucesorActual = New NodoAStar With sucesorActual Set .nodoGrafo = grafoSimple.Item(sucesorKey ) Set .nodoPadre = nodoActual .costoG = nodoActual.costoG + camino.costo Set .shapePadre = camino.path End With If Not disponibles.Exists(sucesorActual.nodoGrafo.idEsri) Then disponibles.Add sucesorActual.nodoGrafo.idEsri, sucesorActual Else If disponibles.Item(sucesorActual.nodoGrafo.idEsri).costoG > sucesorActual.costoG Then Set disponibles.Item(sucesorActual.nodoGrafo.idEsri) = sucesorActual End If End If End If Next End If ' END CASO 4 End If ' END CASO 3 End If ' END CASO 2 End If ' END CASO 1 End If Loop If nodoActual.nodoGrafo.idEsri = idEndNode Then MsgBox "SE ENCONTRO UN CAMINO" MsgBox "el número de nodos analizados fue " & analizados.Count Set segmentos = New poly line Set sucesorActual = analizados.Item(idEndNode) Set nodoActual = analizados.Item(sucesorActual.nodoPadre.nodoGrafo.idEsri) MsgBox "El costo total del camino es " & sucesorActual.costoG Do Until nodoActual Is Nothing Set geometry = sucesorActual.shapePadre segmentos.AddSegmentCollection geometry Set sucesorActual = nodoActual If Not (nodoActual.nodoPadre Is Nothing) Then Set nodoActual = analizados.Item(nodoActual.nodoPadre.nodoGrafo.idEsri) Else Set nodoActual = Nothing End If Loop Set poly line = segmentos poly line.Simplify Network Set pathFinderAEstrella_Zonas = poly line Else MsgBox "NO SE ENCONRO CAMINO" Set pathFinderAEstrella_Zonas = Nothing End If End Function REFERENCIAS [1] Kolahdouzan, Mohammad R. Sharifzadeh, Mehdi. Shahabi, Cyrus. A Road Network Embedding Technique for K-Nearest Neighbor Search in Moving Object Databases. Disponible en http://infolab.usc.edu/DocsDemos/acmgis02.pdf. Consultado en Febrero de 2005. [2] Brandes, Ulrik. Schulz, Frank. Wagner, Dorothea. Willhalm, Thomas. Generating Node Coordinates for Shortest-Path Computations in Transportation Networks. Disponible en http://www.inf.uni-konstanz.de/algo/publications/bsww-gncsp-04.pdf. Consultado en Febrero de 2005. [3] Chapter 1. Digital Library and Archives, University Libraries. Virginia Tech. Disponible en http://scholar.lib.vt.edu/theses/available/etd-042499-225537/unrestricted/chap1.pdf. Consultado en Febrero de 2005. [4] Wagner, D. Willhalm, T. Drawing Graphs to Speed Up Shortest-Path Computations. Disponible en http://delis.upb.de/paper/DELIS-TR-034.pdf . Consultado en Febrero de 2005. [5] [6] Faramroze Engineer. Fast Shortest Path Algorithms for Large Road Networks. Department of Engineering Science. University of Auckland. Disponible en http://www.esc.auckland.ac.nz/Organisations/ORSNZ/conf36/papers/Engineer.pdf. Consultado en Febrero de 2005. Claramunt, C. Jiang , B. Topological Analysis of Urban Street Networks. Disponible en http://www.ecole-navale.fr/fr/irenav/cv/claramunt/EPB2003.pdf. Consultado en Febrero de 2005. [7] Holzer, Martin. Schulz, Frank. Willhalm, Thomas. Combining Speed-Up Techniques for Shortest-Path Computations. Disponible en http://www.ira.uka.de/members/willhalm /publications/hsw-cstsp-04.pdf. Consultado en Febrero de 2005. [8] Mikola, T. Route Resolving Tools In Urban Análisis. Karttakeskus Ltd. Disponible en www.geomatics.kth.se/~hans/C4_Final_Conf/COST_Final_PDF/Mikola_Fin.pdf. Consultado en Febrero de 2005. [9] Wagner, Dorothea. Willhalm, Thomas. Geometric Speed-Up Techniques for Finding Shortest Paths in Large Sparse Grapas. Disponible en www.inf.uni-konstanz.de/Preprints/ papers/2003/preprint-183.pdf. Consultado en Febrero de 2005. [10] Wagner, Dorothea. Willhalm, Thomas. Geometric Speed-Up Techniques for Finding Shortest Paths in Large Sparse Graphs. Disponible en http://www.ilkd.uni- karlsruhe.de/algo/people/dwagner/papers/ww-gsutf-03.pdf. Consultado en Febrero de 2005. [11] Algoritmo de Dijkstra. Disponible en http://www.alumnos.unican.es/uc900 /Algoritmo.htm. Consultado eb Febrero de 2005. [12] Tsai, Benny. Introduction to the A-Star Algorithm. Disponible en http://upe.acm.jhu.edu/websites/Benny_Tsai/Introduction%20to%20AStar.htm. Consultado en Febrero de 2005. [13] Heyes-Jones, Justin. A* algorithm tutorial. Disponible en http://www.geocities.com/jheyes jones/astar.html. Consultado en Febrero de 2005. [14] Path Finding A* Algorithm. Disponible en http://www.edenwaith.com/products /pige/tutorials/a-star.php. Consultado en Febrero de 2005. [15] Eranki, Rajiv. Pathfinding using A* (A-Star). Disponible en http://rajiv.macnn.com/ tut/search.html. Consultado en Febrero de 2005. [16] Batten, Christopher. Algorithms for Optimal Assembly. Disponible en www.mit.edu/~cbatten/work/ssbc04/optassembly-ssbc04.pdf. Consultado en Febrero de 2005. [17] Seet, Boon-Chong. Genping Liu, Bu-Sung Lee, Chuan-Heng Foh, Kai-Juan Wong, Keok-Kee Lee. A-STAR: A Mobile Ad Hoc Routing Strategy for Metropolis Vehicular Communications. Disponible en www.cemnet.ntu.edu.sg/Permocom/papers/2004/ astar%20networking2004.pdf. Consultado en Febrero de 2005.