Subido por Angelo Wilfredo Quezada Cruz

ALGORITMO PARA CALCULAR LA RUTA MÁS CORTA

Anuncio
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.
Descargar