1. introducciÓn 4 1.1. presentaciÓn 4 1.2. a 5 1.3 ...controlbyte.net/fib/dijkstra.pdf ·...

107
Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos. 1 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. ANÁLISIS DE ANTECEDENTES 5 1.3. OBJETIVOS 6 1.4. APLICACIÓN 6 1.5. PLANIFICACIÓN DEL TRABAJO 7 2. ANÁLISIS DE REQUERIMIENTOS 9 2.1. REQUERIMIENTOS 9 2.1.1. QUÉ OFRECE LA LIBRERÍA DE CAMINOS MÍNIMOS. 9 2.1.2. NECESIDAD DE UNA LIBRERÍA DE ESTE TIPO. 10 2.1.3. USO DE LA LIBRERÍA. 12 2.1.4. USUARIOS 12 3. EL PROBLEMA DEL CAMINO MÍNIMO 13 3.1. DEFINICIONES 13 3.2. MÉTODO GENERAL DEL PROBLEMA DEL CAMINO MÍNIMO (UN ORIGEN/MUCHOS DESTINOS) 15 3.2.1. ALGORITMO GENÉRICO DEL CAMINO MÍNIMO 16 3.3. IMPLEMENTACIONES DEL ALGORITMO GENÉRICO 17 3.3.1. MÉTODOS ETIQUETADORES (LABEL SETTING METHODS). 17 3.3.2. MÉTODOS CORRECTORES DE ETIQUETA (LABEL CORRECTING METHODS). 17 3.4. MÉTODOS ETIQUETADORES (DIJKSTRA) 18 3.4.1. TIEMPO DE EJECUCIÓN DEL ALGORITMO DE DIJKSTRA 19 3.4.2. EJEMPLO GRÁFICO DE EJECUCIÓN DEL ALGORITMO 20 3.5. OTRAS VERSIONES DEL ALGORITMO 21 3.5.1. ALGORITMO DE DIJKSTRA HACIA ATRÁS 21 3.5.2. ALGORITMO DE DIJKSTRA BIDIRECCIONAL 21 3.6. IMPLEMENTACIONES DEL ALGORITMO DE DIJKSTRA 22 3.6.1. IMPLEMENTACIÓN DE DIAL 22 3.6.2. IMPLEMENTACIONES CON COLAS DE PRIORIDAD 24 3.6.3. IMPLEMENTACIÓN CON HEAP BINARIO 25 3.6.4. IMPLEMENTACIÓN CON D-HEAP 25 3.6.5. IMPLEMENTACIÓN CON HEAP DE FIBONACCI 25 3.6.6. IMPLEMENTACIÓN CON RADIX HEAP 25 3.7. MÉTODOS CORRECTORES DE ETIQUETA 27 3.7.1. IMPLEMENTACIÓN CON DEQUEUES27 3.8. RESUMEN Y RECAPITULACIÓN DE LOS ALGORITMOS 28 3.8.1. GRAFOS COMPLETOS. AUMENTANDO EL NÚMERO DE NODOS. 29 3.8.2. GRAFO COMPLETO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 29 3.8.3. GRAFO COMPLETO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 30 3.8.4. GRAFO PEQUEÑO. DISMINUYENDO DENSIDAD DE ARISTAS. 30 3.8.5. GRAFO GRANDE. DISMINUYENDO DENSIDAD DE ARISTAS. 30 3.8.6. GRAFO DISPERSO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 31 3.8.7. GRAFO DISPERSO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 31 3.8.8. RESUMEN 32 4. IMPLEMENTACIÓN 33

Upload: others

Post on 18-Jun-2020

10 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

1

1. INTRODUCCIÓN 4

1.1. PRESENTACIÓN 4 1.2. ANÁLISIS DE ANTECEDENTES 5 1.3. OBJETIVOS 6 1.4. APLICACIÓN 6 1.5. PLANIFICACIÓN DEL TRABAJO 7

2. ANÁLISIS DE REQUERIMIENTOS 9

2.1. REQUERIMIENTOS 9 2.1.1. QUÉ OFRECE LA LIBRERÍA DE CAMINOS MÍNIMOS. 9 2.1.2. NECESIDAD DE UNA LIBRERÍA DE ESTE TIPO. 10 2.1.3. USO DE LA LIBRERÍA. 12 2.1.4. USUARIOS 12

3. EL PROBLEMA DEL CAMINO MÍNIMO 13

3.1. DEFINICIONES 13 3.2. MÉTODO GENERAL DEL PROBLEMA DEL CAMINO MÍNIMO (UN ORIGEN/MUCHOS DESTINOS) 15 3.2.1. ALGORITMO GENÉRICO DEL CAMINO MÍNIMO 16 3.3. IMPLEMENTACIONES DEL ALGORITMO GENÉRICO 17 3.3.1. MÉTODOS ETIQUETADORES (LABEL SETTING METHODS). 17 3.3.2. MÉTODOS CORRECTORES DE ETIQUETA (LABEL CORRECTING METHODS). 17 3.4. MÉTODOS ETIQUETADORES (DIJKSTRA) 18 3.4.1. TIEMPO DE EJECUCIÓN DEL ALGORITMO DE DIJKSTRA 19 3.4.2. EJEMPLO GRÁFICO DE EJECUCIÓN DEL ALGORITMO 20 3.5. OTRAS VERSIONES DEL ALGORITMO 21 3.5.1. ALGORITMO DE DIJKSTRA HACIA ATRÁS 21 3.5.2. ALGORITMO DE DIJKSTRA BIDIRECCIONAL 21 3.6. IMPLEMENTACIONES DEL ALGORITMO DE DIJKSTRA 22 3.6.1. IMPLEMENTACIÓN DE DIAL 22 3.6.2. IMPLEMENTACIONES CON COLAS DE PRIORIDAD 24 3.6.3. IMPLEMENTACIÓN CON HEAP BINARIO 25 3.6.4. IMPLEMENTACIÓN CON D-HEAP 25 3.6.5. IMPLEMENTACIÓN CON HEAP DE FIBONACCI 25 3.6.6. IMPLEMENTACIÓN CON RADIX HEAP 25 3.7. MÉTODOS CORRECTORES DE ETIQUETA 27 3.7.1. IMPLEMENTACIÓN CON “DEQUEUES” 27 3.8. RESUMEN Y RECAPITULACIÓN DE LOS ALGORITMOS 28 3.8.1. GRAFOS COMPLETOS. AUMENTANDO EL NÚMERO DE NODOS. 29 3.8.2. GRAFO COMPLETO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 29 3.8.3. GRAFO COMPLETO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 30 3.8.4. GRAFO PEQUEÑO. DISMINUYENDO DENSIDAD DE ARISTAS. 30 3.8.5. GRAFO GRANDE. DISMINUYENDO DENSIDAD DE ARISTAS. 30 3.8.6. GRAFO DISPERSO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 31 3.8.7. GRAFO DISPERSO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 31 3.8.8. RESUMEN 32

4. IMPLEMENTACIÓN 33

Page 2: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

2

4.1. REPRESENTACIONES DE UN GRAFO 35 4.1.1. MATRIZ DE INCIDENCIAS NODO-ARISTA 35 4.1.2. MATRIZ DE ADYACENCIAS NODO-NODO 36 4.1.3. LISTAS DE ADYACENCIA 37 4.1.4. REPRESENTACIONES EN ESTRELLA 38 4.2. DISEÑO DE LA REPRESENTACIÓN DEL GRAFO 39 4.2.1. ELEMENTOS DEL GRAFO 39 EJEMPLO: 40 4.3. IMPLEMENTACIÓN DE LAS ESTRUCTURAS DE DATOS. 42

5. ESPECIFICACIÓN DEL SOFTWARE. 43

5.1. MODELO CONCEPTUAL. 43 5.1.1. INTRODUCCIÓN. 43 5.1.2 APARICIÓN DE NUEVOS ALGORITMOS 45 5.1.3. HERRAMIENTA DE ESPECIFICACIÓN Y DISEÑO 45 5.1.4. DIAGRAMA DE CLASES 46 5.1.5. CLASES DEL DOMINIO 48 5.1.6. CASOS DE USO 49 5.1.7. DIAGRAMA DE SECUENCIA 49 5.1.4. PSEUDO CÓDIGO DE LAS OPERACIONES 51 GRAFO 51 CAMMIN 53 DIJKSTRA 54 5.2. HERRAMIENTAS Y BASE TEÓRICA 57 5.2.1 PROGRAMACIÓN ORIENTADA A OBJETOS 57 5.2.2. C++ 58

6. MANUAL DE USO 59

6.1 INTRODUCCIÓN 59 6.2. PROCEDIMIENTO PARA EJECUTAR UN ALGORITMO DE LA LIBRERÍA 60 6. 2. 1. UNIDIRECCIONAL: 62 6. 2. 2. BIDIRECCIONAL: 62 6. 3. CREACIÓN DEL GRAFO 63 6.4. CLASES Y MÉTODOS PÚBLICOS 64 6.4.1. GRAFO 64 6.4.2. CAMMIN 66

7. RESULTADOS 67

7.1. PREPARATIVOS 67 7.2. RESULTADOS: 69 7.3 ANÁLISIS DE LOS RESULTADOS 72 7.3.1. GRAFOS COMPLETOS. 72 CONCLUSIONES 73 7.3.2. GRAFO COMPLETO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 73 7.3.3. GRAFO COMPLETO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 74 7.3.4. GRAFO PEQUEÑO. DISMINUYENDO DENSIDAD DE ARISTAS. 74 7.3.5. GRAFO GRANDE. DISMINUYENDO DENSIDAD DE ARISTAS. 75 7.3.6. GRAFO DISPERSO PEQUEÑO. AUMENTANDO COSTE MÁXIMO DE ARISTA. 76 7.3.7. GRAFO DISPERSO GRANDE. AUMENTANDO COSTE MÁXIMO DE ARISTA. 77

Page 3: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

3

7.4. PRIMERAS CONCLUSIONES 78 7.5. TABLA CONSEJO SOBRE UTILIZACIÓN DE LOS ALGORITMOS SEGÚN PARÁMETROS DE GRAFO DE ENTRADA. 79

7. APLICACIÓN. 82

7.1. INTRODUCCIÓN 82 7.2 DESCRIPCIÓN DEL PROBLEMA. 82 7.3 APLICACIÓN REAL 82 7.4 ANÁLISIS DE LA APLICACIÓN 83 7.5. CÓMO SUSTITUIR EL CÓDIGO 86 7.6 SUSTITUCIÓN DEL CÓDIGO 87 7.6.1. CREACIÓN DEL GRAFO. 87 7.6.2. NODOS ORIGEN Y DESTINO DEL PROBLEMA 88 7.6.3. EJECUCIÓN DEL ALGORITMO 89 7.7. RESULTADOS 90

8. APÉNDICE 92

8.1. ESTRUCTURAS DE DATOS 92 8.2. D-HEAPS 92 8.2.1. DEFINICIÓN Y PROPIEDADES DE UN D-HEAP 92 8.2.2. ALMACENAMIENTO DE UN D-HEAP. 93 8.2.3. PROPIEDAD ORDEN DEL HEAP 94 8.2.4. INTERCAMBIO 94 8.2.5. RECUPERAR EL ORDEN DEL HEAP 94 8.2.6. OPERACIONES DEL HEAP 95 8.3. HEAPS DE FIBONACCI 96 8.3.1. PROPIEDADES 96 8.3.2. DEFINICIÓN Y ALMACENAMIENTO DE UN HEAP DE FIBONACCI 96 8.3.3. UNIENDO Y CORTANDO 97 8.3.4. INVARIANTES 98 8.3.5. RESTAURANDO EL INVARIANTE 2 98 8.3.6. RESTAURANDO EL INVARIANTE 3 99 8.3.7. OPERACIONES 100

9. BALANCES Y CONCLUSIONES 101

9.1. COMPARACIÓN TIEMPO ESTIMADO CON TIEMPO REAL 101 9.2. BALANCE ECONÓMICO 102 9.3. CONCLUSIONES 103 9.4. LÍNEAS ABIERTAS 104

10. BIBLIOGRAFÍA 105

10.1. LIBROS 105 10.2. ARTÍCULOS EN REVISTAS 105 10.3. DE INTERNET: 106 10.4. DE LA APLICACIÓN DE PROTECCIÓN DE DATOS 107

Page 4: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

4

1. Introducción

1.1. Presentación Este proyecto tiene como objetivo desarrollar una librería cuyo contenido serán diferentes

versiones de algoritmos que solucionan el problema de encontrar el camino mínimo entre nodos de

una red o grafo de forma eficiente.

Una vez implementada, se empleará en una aplicación real de protección de datos estadísticos en la

que se ejecutan numerosos subproblemas que no son más que búsquedas de caminos mínimos en

redes. Dicha aplicación dispone ya de un módulo básico que implementa una única versión del

algoritmo básico de Dijkstra. Una vez creada la librería, se sustituirá ésta por el módulo existente, lo

que permitirá evaluar diferentes variantes del algoritmo, minimizando así los tiempos de espera de

cada subproblema y, como resultado, el de toda la aplicación.

El proyecto se compone de varias partes:

Básicamente, la librería consistirá en diferentes implementaciones del conocido algoritmo de

Dijkstra más otros, llamados correctores de etiqueta, que pueden ser más idóneos y mejorar el tiempo

de cálculo para ciertos tipos de redes o topologías de éstas.

Además, cada implementación tendrá dos variantes: la original, que encuentra dado un nodo

origen todos los caminos mínimos al resto de nodos de la red y una versión que encuentra el camino

mínimo entre dos nodos dados origen y destino. Ésta última versión de buscar el camino mínimo entre

dos nodos es la que verdaderamente necesita la aplicación real y es por ello de su existencia en el

proyecto.

Para el algoritmo de Dijkstra, las versiones difieren entre sí en el uso de distintas estructuras de

datos internas durante su ejecución, que ayudan a reducir el tiempo de cálculo, aunque a priori, y para

un cierto tipo de red, no es fácil intuir qué versión será la más beneficiosa. Es decir, cada tipo de

implementación no es una mejora de otra, sino una forma diferente de intentar aumentar la eficiencia.

Page 5: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

5

1.2. Análisis de antecedentes

Por extraño que parezca en un principio, no existe ninguna biblioteca o aplicación que reúna las

características deseadas en este proyecto.

Normalmente, en las librerías existentes especializadas en problemas con grafos o redes, se puede

encontrar como mucho uno o dos algoritmos para la resolución de los caminos mínimos. En concreto,

el algoritmo básico de Dijkstra y una mejora eficiente usando d-heaps.

Estos algoritmos son más que suficientes para aplicaciones que necesiten aplicarlos unas pocas

veces o que la ejecución de los mismos no sea una parte esencial del total del programa. El problema

surge cuando en la aplicación la mayor parte del tiempo se dedica a buscar caminos mínimos y, por

tanto, es crucial la eficiencia de este tipo de algoritmos.

Posiblemente, y con muchos esfuerzo (temporal o económico) se podrán encontrar otras

versiones, pero con interficies distintas o incluso lenguajes de programación diferentes. De todas

formas, después de una búsqueda intensiva no se han encontrado más de tres algoritmos

implementados. El resto, únicamente aparece en literatura.

Afortunadamente, sí que existe bastante documentación con propuestas de algoritmos, ya sea en

forma de libros especializados en grafos y redes o en revistas científicas e informáticas.

Page 6: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

6

1.3. Objetivos

- Diseñar una biblioteca que contenga distintas versiones de algoritmos que resuelven el problema

del camino mínimo en redes.

- Sencillez de uso de los algoritmos. El usuario debería nada más que proporcionar los mínimos

datos en un tipo de formato predeterminado y elegir la versión del algoritmo a ejecutar.

- Capacidad de detectar anomalías y comunicarlas al agente externo que ejecute las funciones de la

biblioteca.

- Máxima eficiencia temporal y espacial, aunque si es necesario se sacrificará el segundo a favor del

primero.

- Implementar las estructuras de datos que emplearán las distintas versiones de los algoritmos, de

forma lo más eficiente posible (d-heaps, fibonnaci-heaps, colas circulares...).

- Una vez se tengan dichas estructuras, implementar las versiones de los algoritmos de caminos

mínimos, también de manera lo más eficientemente posible.

- Proporcionar una interficie sencilla y clara al usuario de los algoritmos que permita escoger la

versión de implementación pero que sea única independientemente de la elección.

1.4. Aplicación - Estudiar la topología de las redes que aparezcan en la aplicación y observar empíricamente cuál de

las versiones de los algoritmos de caminos mínimos ya implementadas es la ideal.

- Sustituir las llamadas al módulo inicial de Dijkstra de la aplicación por la librería implementada.

Page 7: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

7

1.5. Planificación del trabajo

Previamente a la planificación seria del trabajo se trabajó en un prototipo del software sin

dedicación temporal específica ni precisable, a la vez que se recogía información sobre la existencia de

distintos algoritmos de caminos mínimos, se estudiaban las herramientas de trabajo, etc.

Por eso, ya se disponía de un prototipo de software que, sin mucho refinamiento, cubría la mitad de

los objetivos referentes a la librería. Se tenían implementadas la mayoría de estructuras de datos y

alguno de los algoritmos.

Quedaba, entonces, acabar de implementar las estructuras de datos restantes y las versiones de los

algoritmos. Además, algunas de estas versiones no habían sido todavía estudiadas a fondo, aunque se

disponía de la documentación.

Por supuesto, todo lo referente a insertar la librería en el problema de protección de datos quedaba

por ser desarrollada, junto con un estudio previo del código de la aplicación, para detectar los puntos

donde se efectuarían las modificaciones necesarias.

1ª Quincena Julio 2003 Terminar las estructuras de datos y algoritmos (versiones de Dijkstra).

2ª Quincena Julio 2003 Estudio del resto de algoritmos (no de Dijkstra).

Implementación de los mismos.

1ª Quincena Agosto 2003 Revisión de las implementaciones de los algoritmos.

Juegos de prueba.

2ª Quincena Agosto 2003 Estudio del código de la aplicación.

Elección de la versión idónea de algoritmo para los tipos de redes que

aparecen en el problema (dependiendo del tiempo disponible, se podrá

hacer un estudio empírico más global, para todos las versiones

implementadas).

Aplicación de la librería en el problema.

Septiembre 2003 Revisiones de código.

Redacción últimos capítulos de la memoria.

Septiembre u Octubre 2003

(fecha no escogida todavía)

Revisión final de la memoria.

Preparación de la defensa del proyecto (diapositivas).

Defensa del proyecto.

Page 8: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

8

Para el periodo previo a la matriculación del proyecto (Julio 2003) se estiman 5 horas semanales de

dedicación media (desde Marzo 2003). Esto hace un total aproximado de 80 horas.

Para el periodo posterior se estiman 20 horas de dedicación por semana. Esto hace un total

aproximado de 320 horas.

Total: 400 horas.

Page 9: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

9

2. Análisis de requerimientos

2.1. Requerimientos

2.1.1. Qué ofrece la librería de caminos mínimos.

La biblioteca ofrece distintas implementaciones muy eficientes de algoritmos que resuelven el

problema del camino mínimo, para un único origen y todos los destinos y para parejas origen-destino.

Estas implementaciones están totalmente integradas en una interfaz que proporciona transparencia

y sencillez de cara al usuario, de modo que éste no nota diferencia a la hora de trabajar aunque use

implementaciones distintas del algoritmo.

No debe existir algoritmo mejor que otro (en términos de eficiencia). Para cierta combinación de

condiciones (tamaño de grafo, tipo de solución requerida, topología del grafo, etc.) existirá uno que

será más eficiente que el resto y cada uno de ellos será el más eficiente en como mínimo alguna de las

combinaciones. Es decir, no tiene sentido ofrecer un algoritmo que en todos los casos proporcione una

eficiencia peor que otro.

Sencillez a la hora de integrar nuevas implementaciones en la biblioteca. Debe resumirse en añadir

el código que difiera del resto de algoritmos aprovechando las partes comunes a todos. Por norma

general las diferencias entre implementaciones suelen ser el uso de distintas estructuras de datos para

almacenar datos temporales durante la ejecución del algoritmo, pero todos ellos comparten el

algoritmo básico.

Page 10: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

10

2.1.2. Necesidad de una librería de este tipo.

Existen multitud de implementaciones de algoritmos que resuelven el problema del camino mínimo

en grafos o redes. La mayoría de ellas, pero, están integradas en el propio código del software que las

emplean con lo que su función no va más allá de ser un simple módulo o subrutina de todo un sistema

más global.

En estos casos seguramente cumplen a la perfección su función y cumplen los requerimientos

propuestos. Normalmente a un algoritmo de este tipo se le pide que tenga una eficiencia temporal

aceptable y el tiempo de procesado de su código supone un pequeño porcentaje del código total de la

aplicación.

Se pueden encontrar, ya sea pagando o gratuitamente, algunas bibliotecas que ofrecen unas pocas

implementaciones del algoritmo de Dijkstra, pero están basadas más en la sencillez de su uso que en la

rapidez de su ejecución.

El problema surge cuando en una cierta aplicación, se necesita ejecutar estos algoritmos multitud

de veces, ocupando gran parte del tiempo total de ejecución. En este caso una mínima diferencia de

eficiencia temporal entre dos implementaciones puede suponer una demora considerable cuando es

ejecutada miles o millones de veces.

Page 11: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

11

Se necesita, por tanto, una biblioteca que proporcione implementaciones lo más eficientes posible,

pensadas para ser ejecutadas multitud de veces en una misma aplicación. Deben tener, entonces, las

siguientes características:

• Eficiencia temporal máxima.

Empleo de complejas estructuras de datos temporales para minimizar el número total de

operaciones en el proceso de búsqueda de la solución al problema.

• Austeridad en funcionalidad extra.

Hay que limitarse a proporcionar la solución, evitando proporcionar funciones que

simplifiquen o hagan más cómodo su uso si eso conlleva demoras temporales.

• Consideración de la topología del grafo de entrada.

Una implementación puede ser la más eficiente para un determinado tipo de grafo o red pero

no serlo para otro. Por ello hay que incluir una gran variedad de alternativas que abarquen el

mayor de número de topologías.

Page 12: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

12

2.1.3. Uso de la librería.

Una vez desarrollada e implementada la librería, su uso se limitará en la construcción de

aplicaciones que necesiten resolver problemas de caminos mínimos en redes o grafos.

Se entiende que estas aplicaciones trabajarán o con grafos enormemente grandes o que gran parte

de su tiempo de ejecución lo dedicarán a resolver problemas de caminos mínimos. Es decir, la librería

es una buena ayuda a la hora de querer ganar eficiencia cuando la mayor parte de los cálculos se

destina a este tipo de problemas.

No obstante, no se limita el uso a este tipo de aplicaciones pues su sencilla interficie puede ser lo

suficientemente atractiva para que muchos usuarios decidan emplear la librería aún cuando no ganen

eficiencia temporal considerable o no sea su prioridad.

2.1.4. Usuarios

Vale la pena recordar que estamos hablando de una librería que contiene distintas

implementaciones de algoritmos que resuelven problemas de caminos mínimos, pero en

absoluto se ofrece una herramienta que los resuelva por ella misma.

Los usuarios, por tanto, no serán los comúnmente llamados “finales”, sino que serán la

mayoría de veces desarrolladores de software que construyen aplicaciones donde aparecen

subproblemas de caminos mínimos en grafos.

Page 13: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

13

3. El problema del camino mínimo

3.1. Definiciones

Sea G = (Ν, Α) un grafo dirigido con nodos numerados 1, ..., N. Cada arco (i,j) ∈ Α tiene un coste

o “longitud” aij asociado con él. La longitud de un camino (i1, i2, ..., ik) que consista exclusivamente en

arcos hacia delante, es igual a la suma de los arcos

∑−

=+

1

11

k

nii nn

a .

Este camino es mínimo si tiene la longitud menor entre todos los caminos posibles con los mismos

nodos origen y destino. El camino mínimo también puede llamarse distancia mínima. La distancia

mínima de un nodo a sí mismo es 0 por convención.

El problema del camino mínimo intenta buscar las distancias mínimas dentro de un grafo entre un

cierto número de nodos. Según los nodos escogidos inicialmente, aparecen distintas versiones del

problema aunque dos son las principales:

• Un origen / muchos destinos: Se indica un nodo inicial y se requiere todos los caminos

mínimos entre este nodo y el resto. La solución entonces aparece como un árbol de

recubrimiento mínimo con el nodo inicial como raíz.

Ejemplo:

0

1 4

5

2 3

4

2

1

1

11

1

4

3

Figura 1. Grafo donde se busca el camino mínimo. Nodo origen: 0.

Page 14: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

14

0

1 4

5

2 3

1

1

11

1

Figura 2. Solución del problema del camino mínimo: un origen / muchos

destinos.

• Un origen / un destino: Se indica una pareja de nodos y se requiere el camino mínimo entre

ellos. La solución será la sucesión de aristas que construyen el recorrido. Nótese que a partir

de la versión un origen / muchos destinos se puede encontrar también la solución al de esta

versión.

0

1 4

5

2 3

1

1

1

1

Figura 3. Solución del problema del camino mínimo: un origen / un destino.

Nodo origen: 0. Nodo destino: 5.

Page 15: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

15

3.2. Método general del problema del camino mínimo (un origen/muchos destinos)

En este método se empieza con un vector de distancias al origen para cada nodo (d1, d2, ..., dN),

inicializadas a un valor máximo o infinito. Se van seleccionando sucesivamente arcos (i, j) que violen

la condición dj > di + aij, y se obliga a

dj := di + aij.

Se continúa hasta que la condición dj ≤ di + aij se satisfaga para todos los arcos (i, j).

La idea es que, en el transcurso del algoritmo, di puede ser interpretado para todo i como la

longitud de algún camino Pi desde el origen hasta i. Si dj > di + aij para algún arco (i, j), el camino que

se obtiene extendiendo el camino Pi con el arco (i, j), de longitud di + aij, es más corto que el actual

camino Pj, de longitud dj. Por eso, el algoritmo encuentra sucesivamente caminos más cortos desde el

origen hasta todos los destinos.

Normalmente se implementa este método examinando los arcos que salen de un nodo dado i

consecutivamente. Se mantiene una lista de nodos V, llamada lista de candidatos, y un vector d = (d1,

d2, ..., dN), donde cada dj, llamada etiqueta del nodo j, es un número real o ∞. Inicialmente,

V = {1},

d1 = 0, di = ∞, ∀ i ≠ 1.

El algoritmo itera hasta que V esté vacía. La típica iteración (asumiendo V no vacía) es:

borrar un nodo i de la lista de candidatos V. para cada arco (i, j) ∈ A con j ≠ 1 hacer

si dj > di + aij entonces dj := di + aij; añadir j a V si todavía no lo estaba

fsi fpara

Nótese que, en el transcurso del algoritmo, las etiquetas nunca se incrementan. Por lo tanto,

di < ∞ ⇔ i ha entrado en la lista de candidatos V por lo menos una vez.

Page 16: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

16

3.2.1. Algoritmo genérico del camino mínimo

• Al final de cada iteración, se dan las siguientes condiciones: o d1 = 0. o Si dj < ∞ y j ≠ 1, entonces dj es la longitud de algún camino que empieza en 1, nunca

regresa a 1, y termina en j.

o Si i ∉ V, entonces di = ∞ o

dj ≤ di + aij, ∀j tal que (i, j) ∈ A.

• Si el algoritmo ha terminado, para todo j ≠ 1 tal que dj < ∞, dj es la distancia mínima entre 1 y

j y

dj = };{

),(iji

Ajiadmín +

dj = ∞ si y sólo si no existe ningún camino desde 1 hasta j.

• Si el algoritmo no termina, entonces es que existe algún camino de longitud negativa que

empieza en 1 y nunca regresa a 1.

Page 17: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

17

3.3. Implementaciones del algoritmo genérico

Existen muchas implementaciones del algoritmo genérico; difieren entre ellas en cómo se

selecciona el nodo a eliminar de la lista de candidatos V. Así, se pueden clasificar en dos categorías:

3.3.1. Métodos etiquetadores (Label setting methods).

En estos métodos, el nodo i eliminado de V es el nodo con la etiqueta más pequeña entre

todos los candidatos en V. Asumiendo que todos los arcos son de longitud no negativa, estos

métodos tienen una propiedad remarcable: cada nodo entrará en V como mucho una sola vez;

su etiqueta obtendrá su valor final en el momento que salga de V. La parte más costosa en

tiempo de estos métodos es calcular el nodo con etiqueta mínima entre todos los de V en cada

iteración; existen muchas implementaciones que emplean una variedad de métodos más o

menos creativos y estructuras de datos eficientes para calcular estos valores mínimos.

3.3.2. Métodos correctores de etiqueta (Label correcting methods).

En estos métodos la elección del nodo i que se borra de V es menos sofisticada que en los métodos

etiquetadores y requieren menos cálculo. Sin embargo, un nodo puede que entre en la lista de

candidatos V muchas veces.

En la práctica, cuando los arcos tienen longitud no negativa (cosa que supondremos siempre), los

mejores métodos de los etiquetadores y los mejores de los correctores de etiqueta son competitivos

entre sí. Hay también muchas cotas de complejidad en los peores casos para los dos métodos. Las

mejores cotas corresponden a los métodos etiquetadores pero, sin embargo, en la práctica no se puede

afirmar que los que tengan mejores cotas de complejidad temporal sean los mejores.

Page 18: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

18

3.4. Métodos etiquetadores (Dijkstra)

El algoritmo de Dijkstra es el más famoso de los algoritmos de caminos mínimos. Es idéntico al

método general excepto que siempre borra de la lista de candidatos V el nodo j que tiene la etiqueta

más pequeña. Es decir,

dj = i

Vidmín

∈.

El método queda así: Inicialmente,

V = {1},

d1 = 0, di = ∞, ∀ i ≠ 1.

El método itera hasta que V se queda vacía. Una típica iteración (asumiendo V no vacía) es:

borrar de V un nodo i tal que di = j

Vjdmín

∈.

para cada arco (i, j) ∈ A, con j ≠ 1 hacer si dj > di + aij entonces dj := di + aij añadir j a V si no lo estaba ya fsi fpara

Mirando con detalle el algoritmo, se puede considerar W como el conjunto de nodos que han estado

en V pero que ya no lo están,

W = {i | di < ∞, i ∉ V}.

Si de V sólo se borra el nodo con etiqueta mínima, entonces W contiene los nodos con las etiquetas

de valor más pequeño durante todo el algoritmo,

dj ≤ di, si j ∈ W e i ∉ W.

Como se asume aij ≥ 0, cuando un nodo i se borra de V y pasa a ser de W, se tiene que para cada j ∈

W tal que (i, j) es un arco de A,

dj ≤ di + aij.

Un nodo, al entrar en W, permanecerá siempre ahí ya y su etiqueta nunca variará. Por lo tanto, W

puede ser visto como el conjunto de nodos permanentemente etiquetados, es decir, los nodos que han

adquirido su etiqueta final, que debe ser igual a la distancia mínima desde el origen.

Page 19: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

19

Más formalmente:

Se asume que todos los arcos son no negativos y que existe por lo menos un camino mínimo desde

el nodo origen (nodo 1) hasta cada uno de los demás nodos.

• Para cualquier iteración del método etiquetador, se cumple lo siguiente:

W = {i | di < ∞, i ∉ V}.

• Un nodo que pertenezca a W al principio de la iteración no entrará en la lista de candidatos V

durante ésta.

• Al final de la iteración, di ≤ dj para todo i ∈ W y j ∉ W.

• Para cada nodo i, considerar caminos que empiezan en 1, acaban en i, y tienen todos sus nodos

en W al final de la iteración. Entonces la etiqueta di al final de la iteración es igual a la

longitud del mínimo de estos caminos (di = ∞ si no existe tal camino).

• Al ser un método etiquetador, todos los nodos se borrarán de la lista de candidatos

exactamente una vez en orden creciente de distancia al nodo 1; i será borrado antes que j si la

etiqueta final satisface di < dj.

3.4.1. Tiempo de ejecución del algoritmo de Dijkstra

• Selección de nodos. El algoritmo acaba seleccionando los N nodos del grafo quitándolos de V.

Como tiene que encontrar cada vez el mínimo entre todos los de la lista de candidatos, el

tiempo total de selección es N + (N – 1) + (N – 2) + ... + 1 = O(N2).

• Actualización de distancias. El algoritmo examina cada arco (i, j) ∈ A exactamente una vez,

para comprobar si j ≠ 1 o si se cumple la condición dj > di + aij y actualizar dj := di + aij si se

requiere. En total, se necesitarán O(A) operaciones, menor en comparación con O(N2).

El recorrido por todos los arcos en tiempo O(A) es inevitable y no es posible reducirlo. Sin

embargo, la búsqueda de las etiquetas mínimas en tiempo O(N2) puede mejorarse considerablemente

empleando diversas estructuras de datos que ayuden a hacer esta búsqueda más eficientemente. Las

mejores estimaciones en el peor caso que se han obtenido son de O(A + NlogN) y O(A + N Clog ),

siendo C el rango de las longitudes de los arcos: C = max(i,j)∈Aaij. De todas formas la experiencia dice

que los métodos que se comportan mejor en la práctica tienen cotas temporales en el caso peor

bastante malas.

Page 20: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

20

3.4.2. Ejemplo gráfico de ejecución del algoritmo

Aristas visitadas Aristas del grafo solución

0

1 4

5

2 3

4

2

1

1

11

1

4

3

0

∞ ∞

0

1 4

5

2 3

4

2

1

1

11

1

4

3

0

3 3

5

1

2

0

1 4

5

2 3

4

2

1

1

11

1

4

3

0

3 3

5

1

2

Page 21: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

21

3.5. Otras versiones del algoritmo

3.5.1. Algoritmo de Dijkstra hacia atrás

En el algoritmo original se determina un camino mínimo desde s al resto de nodos en N – {s}. Esta

versión lo que pretende resolver es la búsqueda de un camino mínimo desde cada uno de los nodos en

N – {t} a un nodo destino {t}. Para ello, se modifica ligeramente el algoritmo original de Dijkstra. Se

mantiene una distancia d’j para cada nodo j, que es una cota superior de la longitud del camino mínimo

desde el nodo j hasta el nodo t. Como antes, el algoritmo designa a un conjunto de nodos, W, como a

los permanentemente etiquetados y al resto, V, como a los temporalmente etiquetados. En cada

iteración, el algoritmo escogerá el nodo con etiqueta temporal mínima, d’j, y lo hará permanente.

Después examinará cada arco que llegue (i, j) y modificará la distancia del nodo i a mín{d’i, cij + d’j}.

El algoritmo termina cuando todos los nodos hayan sido etiquetados permanentemente.

3.5.2. Algoritmo de Dijkstra bidireccional

En algunas aplicaciones de caminos mínimos (el caso, por ejemplo, de la aplicación donde

usaremos la librería desarrollada para este proyecto) no se necesita encontrar el camino mínimo entre

un nodo s hasta el resto de nodos de la red o grafo. En vez de eso, se quiere determinar un camino

mínimo entre un nodo s y otro t especificado también. Para resolver esto y evitar otros cómputos, se

podría ejecutar el algoritmo original de Dijkstra y terminar en el instante en que se seleccione t de V

(aunque otros nodos sigan estando etiquetados temporalmente). De todas formas, el siguiente

algoritmo resuelve este problema de forma más eficiente en la práctica (aunque no en el peor caso).

En el algoritmo bidireccional, se aplica simultáneamente la versión original (hacia delante) desde el

nodo s y la versión hacia atrás desde el nodo t. El algoritmo alternativamente designa como

permanentes un nodo en V y un nodo en V’ hasta que las dos versiones hayan etiquetado

permanentemente el mismo nodo, por ejemplo el nodo k. En este punto, sea Pi el camino mínimo

desde el nodo i ∈ W encontrado por el algoritmo de Dijkstra original, y sea P’j el camino mínimo

desde el nodo j ∈ W’ al nodo t encontrado por la versión hacia atrás. Se puede demostrar que el

camino mínimo desde el nodo s hasta el nodo t es o el camino Pk ∪ P’k o el camino Pi ∪ {(i, j)} ∪ P’j

para algún arco (i, j), i ∈ W y j ∈ W’. Este algoritmo es muy eficiente porque tiende a etiquetar de

forma permanente pocos nodos y casi nunca examina arcos incidentes a un número elevado de nodos.

Page 22: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

22

3.6. Implementaciones del algoritmo de Dijkstra

La selección de los nodos para sacarlos de la lista de candidatos es el principal cuello de botella del

algoritmo de Dijkstra. La idea que tienen todas las distintas implementaciones del algoritmo es

intentar guardar de alguna forma estos nodos para que encontrar el de etiqueta mínima sea lo más

eficiente posible.

3.6.1. Implementación de Dial

El algoritmo de Dial se basa principalmente en la siguiente propiedad:

Las etiquetas de distancia que el algoritmo de Dijkstra designa como permanentes no van a

disminuir su valor.

Esta propiedad surge del hecho de que el algoritmo etiqueta permanentemente un nodo i con la

menor distancia temporal di y, mientras recorre los arcos (i, j), nunca decrementará el valor de ninguna

etiqueta más allá de di porque los arcos son no negativos.

El algoritmo de Dial almacena los nodos con etiqueta temporal de un forma peculiar. Mantiene NC

+ 1 conjuntos, llamados baldes (buckets), numerados 0, 1, 2, ..., NC: El balde k guarda todos los nodos

con distancia temporal igual a k. C es la longitud máxima de todos los arcos del grafo y, por tanto, NC

es una cota superior de la distancia del origen a cualquier nodo. Los nodos con etiqueta infinita no se

guardarán en ningún balde. El contenido de un balde será el conjunto contenido(k).

En la operación de selección de nodo, se recorren los baldes 0, 1, 2, ... hasta encontrar el primero

que no esté vacío. Supongamos que k es el primero no vacío. Entonces, cada nodo en contenido(k)

tendrá la mínima distancia. Uno por uno, se borrarán estos nodos del balde, designándolos como

permanentes (es decir, saldrán de V para pasar a W) y se recorrerán sus arcos para actualizar las

distancias de los nodos adyacentes. Siempre que se modifique la etiqueta de algún nodo i de d1 a d2,

deberá moverse i desde el contenido(d1) hasta el contenido(d2). En la siguiente operación de selección,

deberá reanudarse la búsqueda desde los baldes k + 1, k + 2, ... para encontrar el siguiente balde no

vacío. La propiedad en la que se basa el algoritmo de Dial asegura que los baldes 0, 1, 2, ..., k siempre

estarán vacíos en las siguientes iteraciones hasta el final y, por tanto, no deberán ser examinados.

La estructura de datos que guardará el contenido de un balde será una lista doblemente encadenada.

Esta estructura permite realizar cada operación en tiempo O(1): comprobar si un balde está o no vacío,

borrar un elemento del balde y añadir un elemento al balde. Con ésta, el algoritmo necesita un tiempo

Page 23: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

23

O(1) para actualizar cada distancia, y un total de O(A) para todas las actualizaciones. El cuello de

botella en esta implementación aparece al recorrer los NC + 1 baldes durante la selección de nodos.

Consecuentemente, el tiempo de ejecución del algoritmo de Dial es de O(A + NC).

Puesto que el algoritmo de Dial usa NC + 1 baldes, los requerimientos de memoria pueden llegar a

ser prohibitivos. La siguiente idea permite reducir el número de baldes a C + 1:

Si di es la etiqueta que el algoritmo designa como permanente al principio de una iteración,

entonces al final dj ≤ di + C para cada etiqueta j en V.

Esto proviene de que dl ≤ di para cada nodo l ∈ W y de que para cada nodo j ∈ V con dj ≠ ∞, dj = dl

+ cij para algún nodo l ∈ W. Por tanto, dj = dl + cij ≤ di + C. En otras palabras, todas las etiquetas no

infinitas están comprendidas entre di y di + C. En consecuencia, son suficientes C + 1 baldes para

guardar los nodos con etiquetas no infinitas.

El algoritmo de Dial usa C + 1 baldes numerados 0, 1, 2, ... , C, que puede verse como una lista

circular. Se guarda temporalmente un nodo j con etiqueta dj en el balde dj mod (C + 1). Durante la

ejecución del algoritmo, el balde k guarda los nodos con etiqueta k, k + (C + 1), k + 2(C + 1), etc. Sin

embargo, por la propiedad antes expuesta, un nodo siempre guardará nodos de la misma distancia.

Esta forma de almacenamiento también implica que, si un balde k contiene un nodo con la mínima

etiqueta, entonces los baldes k + 1, k + 2, ..., C, 0, 1, 2, ..., k – 1, guardan nodos con etiquetas mayores.

Este algoritmo examina los baldes secuencialmente para identificar el primero no vacío. En la

siguiente iteración sigue recorriéndolos empezando desde donde lo dejó. Una potencial desventaja de

esta implementación comparada con la implementación original de Dijkstra O(N2) es que requiere

mucho espacio de memoria cuando C es muy grande. Y, en ese caso, el tiempo de cálculo también se

incrementará al tener que recorrer una estructura tan grande. El algoritmo se ejecuta en O(A + NC),

que es pseudopolinómico. Por ejemplo, si C = N4, el algoritmo se ejecuta en O(N5) y, si C = 2n, se

necesita un tiempo exponencial en el peor de los casos. Sin embargo, el algoritmo normalmente no

llega a la cota de O(A + NC). Para la mayoría de aplicaciones, C es un número modesto, y los baldes

que se recorren suelen ser inferiores a N - 1, con lo que el tiempo de ejecución en la práctica es mucho

mejor que el indicado por su complejidad en el caso peor.

Page 24: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

24

3.6.2. Implementaciones con colas de prioridad

Una cola de prioridad o heap, es una estructura de datos que permite realizar las siguientes

operaciones sobre una colección de objetos H, cada uno asociado a un número real llamado su clave.

crear-heap(H). Crea un heap vacío.

busca-mínimo(i, H). Encuentra y devuelve el elemento i de clave mínima.

inserta(i, H). Inserta un nuevo elemento i con una clave predefinida.

decrementa-clave(valor, i, H). Reduce la clave del elemento i desde su valor actual a valor,

que debe ser inferior a la que reemplaza.

borra-mínimo(i, H). Borra el elemento i de clave mínima.

Si se implementa el algoritmo de Dijkstra usando una cola de prioridad, H sería la colección de

nodos con etiqueta de distancia finita y temporal (los que están en V).

El algoritmo queda así:

algoritmo Dijkstra-heap; crear-heap(H); dj := ∞ para todo j ∈ N; ds := 0 y pred(s) := 0; inserta(s, H); mientras H ≠ ∅ hacer busca-mínimo(i, H); borra-mínimo(i, H); para cada (i, j) ∈ A(i) hacer valor := d(i) + cij; si d(j) > valor entonces si d(j) = ∞ entonces d(j) := valor; pred(j) := i; inserta(j, H) si no d(j) := valor; pred(j) := i; decrementa-clave(valor, i, H) fsi fsi fpara fmientras

Del algoritmo se puede observar que las operaciones busca-mínimo, borra-mínimo e inserta se

realizan como mucho N veces y que la operación decrementa-clave como mucho A veces. Se

analizarán a continuación los tiempos de ejecución del algoritmo de Dijkstra implementado usando

diferentes tipos de heaps: heaps binarios, d-heaps, heaps de Fibonacci ...

Page 25: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

25

3.6.3. Implementación con heap binario

Un heap binario necesita un tiempo O(logN) para efectuar las operaciones inserta, decrementa-

clave y borra-mínimo, y un tiempo O(1) para las demás. En consecuencia, esta versión ejecuta el

algoritmo de Dijkstra en tiempo O(AlogN). Es más lenta que la original cuando se tratan grafos muy

densos (por ejemplo de orden cuadrático respecto el número de nodos), pero es más rápida cuando A =

O(N2 / logN).

3.6.4. Implementación con d-heap

Dado un parámetro d ≥ 2, el d-heap requiere un tiempo O(dlogdN) para las operaciones borra-

mínimo y un tiempo constante O(1) para el resto. Por eso, el tiempo de ejecución de esta versión es de

O(AlogdN + NdlogdN). Para obtener el valor óptimo de d, se igualan los dos términos, dando d =

máx{2, ⎡A/N⎤}. El tiempo resultante es O(AlogdN). Hay que observar que para redes muy dispersas

(por ejemplo con A = O(N)) el tiempo de ejecución es O(NlogN). Para grafos no dispersos (A = f(N1 +

x) para algún x > 0), el tiempo de cálculo de la implementación con d-heap es O(AlogdN) =

O((AlogN)/(logd)) = O((AlogN)/(logNx)) = O((AlogN)/(xlogN)) = O(A/x) = O(N). El último paso es

cierto porque x es una constante. Por tanto, el tiempo de cálculo total es O(A), que es óptimo.

3.6.5. Implementación con heap de Fibonacci

El heap de Fibonacci permite hacer cada operación en tiempo O(1) excepto borra-mínimo, que

requiere un tiempo O(logN). Por lo tanto, el tiempo de cálculo total es O(A + NlogN). Esta cota

temporal es bastante mejor que la de la implementación con heap binario y con d-heap para todos las

densidades de grafo. La implementación, además, es actualmente la mejor polinómicamente hablando

para resolver el problema del camino mínimo.

3.6.6. Implementación con Radix Heap

Esta implementación es una mezcla de la versión original y la de Dial, que usa nC + 1 baldes.

La implementación original de Dijkstra designa una misma prioridad a todas las etiquetas

temporales y busca la mínima entre ellas, como si se encontrasen en un mismo y gran balde. Dial

emplea un número considerable de baldes para separar las etiquetas y, así, las búsquedas se efectúan

entre menos elementos. La implementación con radix heap mejora estos dos métodos adoptando una

aproximación intermedia: guarda un cierto número de etiquetas en un balde, pero no todas. En vez de

guardar en cada balde las etiquetas con misma distancia, guarda las etiquetas con valor comprendido

en un mismo rango. La cardinalidad de este rango se denomina anchura. Si se emplean baldes de

Page 26: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

26

anchura k, se reduce el número necesario en un factor de k. Pero para encontrar la etiqueta mínima,

habrá que buscar entre todas las etiquetas del balde no vacío con rango más pequeño. Nótese que si k

es suficientemente grande, sólo existiría un único balde y se tendría el algoritmo original de Dijkstra.

Para evitar tener que buscar la etiqueta mínima, se modifica esta implementación de forma que

siempre el balde con rango inferior tenga anchura 1. El algoritmo de radix heap usa anchuras variables

para los baldes y los cambia dinámicamente.

1. Las anchuras de los baldes son 1, 1, 2, 4, 8, 16, ..., con lo que el número necesario de ellos es

de sólo O(log(NC)).

2. Los rangos se modifican dinámicamente y se recolocan las etiquetas temporales de forma que

siempre se tenga la mínima en el balde de anchura 1.

La ventaja sobre la versión de Dial es que ahora sólo se tienen O(logNC)) baldes, pero se mantiene

la propiedad de que no hay que hacer búsquedas de etiquetas mínimas. Esta implementación tiene un

coste temporal de O(A + Nlog(NC)).

Page 27: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

27

3.7. Métodos Correctores de Etiqueta

El método corrector de etiqueta es prácticamente idéntico a su hermano etiquetador. La diferencia

radica en que no realiza ningún cómputo complejo para seleccionar un elemento de V, empleando

siempre un tiempo O(1) para ello.

En el método etiquetador un elemento extraído de V, pasaba a W y se quedaba ahí hasta el final del

algoritmo. Ahora, normalmente esto no ocurrirá así y un elemento podrá entrar en V más de una vez.

El algoritmo queda así:

algoritmo corrector_de_etiqueta d(j) := ∞ para cada nodo j ∈ N; d(s) := 0 y pred(s) := 0; V := {s}; mientras L ≠ ∅ hacer borrar un elemento i de V; para cada arco (i, j) ∈ A(i) hacer si d(j) > d(i) + cij entonces d(j) := d(i) + cij; pred(j) := i; si j ∉ V entonces añade j a V; fsi fpara fmientras

El coste de ejecución de este algoritmo es de O(NA).

3.7.1. Implementación con “dequeues”

Esta modificación proporciona muy buenos tiempos en la práctica. De hecho, es de los mejores

métodos para redes dispersas. La única contrapartida es que su caso peor tiene un coste

pseudopolinómico.

Esta implementación guarda V en una “dequeue”. Una “dequeue” es una estructura de datos que

permite guardar una lista a la que se pueden añadir o borrar elementos tanto por delante como por

detrás.

Este algoritmo siempre selecciona los nodos que están delante de V, pero los añade según sea el

caso. Si el nodo ya había estado en la lista, lo añade al frente. Si no, detrás. Este heurístico ha sido

comprobado empíricamente y ha dado como resultado que el algoritmo examine menos nodos que la

mayoría de otros algoritmos de caminos mínimos.

Page 28: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

28

3.8. Resumen y recapitulación de los algoritmos Algoritmo Coste temporal Características Dijkstra original O(N2) Muy fácil de implementar.

Ofrece el mejor tiempo de ejecución para grafos densos.

Dial O(A + NC) Fácil de implementar y excelente comportamiento empírico. Coste temporal pseudopolinómico y no atractivo en teoría.

d-Heap O(AlogdN), con d = A/N Tiempos de ejecución lineales si el número de aristas es función del número de nodos elevado a alguna potencia.

Fibonacci O(A + NlogN) Ofrece el mejor tiempo polinómico teórico. Difícil y complicado de implementar.

Radix Heap O(A + Nlog(NC)) En principio es una mejora del algoritmo de Dial. Excelentes tiempos de ejecución.

Corrector de etiqueta O(AN) Ofrece el mejor tiempo polinómico con pesos de aristas arbritarios. Bastante eficiente en la práctica.

Corrector de etiqueta con “dequeue”

O(mín({NAC, A2N}) Muy eficiente en la práctica (con tiempo posiblemente lineal). El peor caso es intratable.

Page 29: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

29

A continuación se expone una tabla que trata de estudiar la eficiencia de estos algoritmos en la

teoría. Como se ha expuesto anteriormente parece que en la práctica la mayoría de los algoritmos se

comportan de forma diferente a lo que se podría esperar. Es por ello que no se podrá desechar ninguna

de las versiones como tampoco adivinar cuál de ellos es el más rápido pero seguramente se podrá tener

una primera aproximación de sus comportamientos según las tres variables principales: número de

nodos del grafo, número de aristas (densidad) y peso máximo de las aristas (aunque no todos dependen

de ésta).

3.8.1. Grafos completos. Aumentando el número de nodos. Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

10 100 1 100 110 100 110 110 1000 1000

20 400 1 400 420 400 426 426 8000 8000

40 1600 1 1600 1640 1600 1664 1664 64000 64000

80 6400 1 6400 6480 6400 6552 6552 512000 512000

Para grafos densos los métodos etiquetadores tienen claramente los mejores costes

temporales, aunque entre ellos no hay grandes diferencias. El método de dijkstra, tal como se

expuso, es el que consigue tiempos peores de ejecución más pequeños.

3.8.2. Grafo completo pequeño. Aumentando coste máximo de arista. Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

10 100 1 100 110 100 110 110 1000 1000

10 100 10 100 200 100 110 120 1000 10000

10 100 100 100 1100 100 110 130 1000 100000

10 100 1000 100 10100 100 110 140 1000 102400

10 100 1000000 100 10000100 100 110 170 1000 102400

Como en el caso anterior, Dijkstra sigue siendo el más eficiente en grafos completos. Nótese que

Dial no será nada recomendable para grafos completos con pesos máximos de arista elevados.

Page 30: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

30

3.8.3. Grafo completo grande. Aumentando coste máximo de arista.

Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

103 106 1 106 106 106 106 106 109 109 103 106 10 106 106 106 106 106 109 1010

103 106 100 106 106 106 106 106 109 1011

103 106 103 106 2*106 106 106 106 109 1012

103 106 106 106 109 106 106 106 109 1015

En grafos completos grandes ya no existe tanta diferencia entre los métodos etiquetadores si

exceptuamos el de Dial que, como anteriormente, se comporta bastante mal cuando los pesos máximos

de arista crecen considerablemente.

3.8.4. Grafo pequeño. Disminuyendo densidad de aristas.

Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

10 100 1 100 110 100 110 110 1000 1000

10 75 1 100 85 83 85 85 750 750

10 50 1 100 60 72 60 60 500 500

10 25 1 100 35 52 35 35 250 250

10 11 1 100 21 37 21 21 110 110

Aquí se encuentra la primera dificultad a la hora de asignar un algoritmo ideal para un cierto tipo

de grafo. Es cierto que a partir de una densidad media Dial, Fibonacci y RadixHeap sobresalen del

resto pero con densidades medio-altas será difícil precisar cuál de ellos se comporta mejor

temporalmente.

3.8.5. Grafo grande. Disminuyendo densidad de aristas.

Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

103 106 1 106 106 106 106 106 109 109

103 5*105 1 106 5*105 6*105 5*105 5*105 5*108 5*108

103 3*105 1 106 3*105 3*105 3*105 3*105 3*108 3*108

103 105 1 106 105 3*105 105 105 108 108

103 2*103 1 106 3*103 2*104 5*103 5*103 2*106 2*106

Dial se sitúa en el algoritmo más eficiente cuando la densidad baja considerablemente (aunque en

este ejemplo los costes máximos de arista son mínimos). Cabe notar que d-Heap tiene un extraño

comportamiento en el rango de densidades medio-altas, donde en algunos casos es comparable al resto

de métodos etiquetadores, aunque para densidades pequeñas es de los menos eficientes.

Page 31: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

31

3.8.6. Grafo disperso pequeño. Aumentando coste máximo de arista.

Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

10 11 1 100 21 37 21 21 110 110

10 11 2 100 31 37 21 24 110 220

10 11 4 100 51 37 21 27 110 440

10 11 10 100 111 37 21 31 110 1100

10 11 100 100 1011 37 21 41 110 11000

Al estudiar anteriormente el comportamiento en grafos pequeños disminuyendo la densidad, se vio

que habían tres versiones “ganadoras”. Aquí se puede afirmar que para un coste máximo de arista

arbitrario, Fibonacci es el método más adecuado y, en cualquier caso, Dial no es nada aconsejable.

3.8.7. Grafo disperso grande. Aumentando coste máximo de arista.

Nodos Aristas Peso máx Dijkstra Dial d-Heap Fibonacci RadixHeap Corrector Dequeue

103 103 1 106 2*103 104 4*103 4*103 106 106

103 103 2 106 3*103 104 4*103 4*103 106 2*106 103 103 4 106 5*103 104 4*103 5*103 106 4*106 103 103 10 106 104 104 4*103 5*103 106 107 103 103 100 106 105 104 4*103 6*103 106 108

Como se había observado anteriormente, Dial ofrece los mejores tiempos en grafos

dispersos con costes máximos de arista pequeños pero, a mayores pesos de arista Fibonacci

destaca algo sobre el resto.

Page 32: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

32

3.8.8. Resumen Este primer estudio teórico de los algoritmos ha proporcionado una primera idea del

comportamiento de los mismos aunque habrá que esperar a un posterior estudio empírico, una

vez implementada la librería y mediante juegos de pruebas, para conocer sus

comportamientos reales.

Es de destacar el horroroso papel de los métodos correctores, con tiempos de ejecución

teóricos nada atractivos que hacen pensar en descartarlos totalmente. El motivo de que se

sigan manteniendo en la librería de caminos mínimos es el hecho de que parece que en la

práctica se pueden comportar incluso mejor que los métodos etiquetadores, según la

bibliografía.

Densidad Tamaño Peso máximo Algoritmo más eficiente

Alto Etiquetadores excepto Dial Grande Bajo Etiquetadores Alto Dijkstra, d-Heap

Alta Pequeño

Bajo Dijkstra, d-Heap Alto Fibonacci Grande Bajo Dial Alto Fibonacci Baja

Pequeño Bajo

Dial, Fibonaccci, RadixHeap

Page 33: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

33

4. Implementación

Uno de los objetivos del presente proyecto es la implementación de algoritmos lo más

eficientemente posible.

Si se resume al máximo el comportamiento de los algoritmos se puede observar que los mismos

no son más que recorridos a través de un grafo. El orden en que se recorre es lo que los diferencia

entre ellos, además de la forma en que guardan valores temporales.

Por tanto, donde realmente se tiene que tener cuidado a la hora de implementar para lograr la

mejor eficiencia es en ese recorrido sobre el grafo.

El grafo debería ser implementado mediante una estructura que permitiera acceder a los elementos

necesarios para “viajar” por él en tiempo de ejecución mínimos.

Analizando con más detalle el algoritmo de Dijkstra se podrán detectar los elementos del grafo

que son más cruciales respecto a la eficiencia temporal.

borrar de V un nodo i tal que di = jVj

dmín∈

.

para cada arco (i, j) ∈ A, con j ≠ 1 hacer si dj > di + aij entonces dj := di + aij añadir j a V si no lo estaba ya fsi fpara De V, se borrarán como máximo el número de nodos del grafo. Además, primero deben añadirse.

Para cada uno de estos nodos borrados se examinarán todas sus aristas salientes. De las aristas se

necesita saber a qué nodo van a parar y el peso de las mismas.

El resto de cálculos deberían ser simples operaciones aritméticas o comparaciones, que no

dependerán de las implementaciones.

Resumiendo, se realizarán |N| inserciones en una estructura de datos que representará a V, |N|

extracciones de V y |A| observaciones de aristas.

Page 34: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

34

Como se ha comentado anteriormente, la investigación de las aristas del grafo es inevitable a la

hora de encontrar los caminos mínimos. Lo que diferenciará a cada uno de los algoritmos es la

representación del conjunto V y las operaciones de inserción y extracción sobre él.

Esto no evita, sin embargo, que el recorrido por las aristas del grafo no tenga que ser lo más rápido

posible, pues normalmente siempre será mucho mayor |A| que 2*|N|.

Sobre la estructura V, en los métodos etiquetadores el problema radica en que cada vez que se

extrae un elemento, éste tiene que ser el mínimo. En los métodos correctores este problema no existe,

pero, se realizan muchas más inserciones y extracciones.

Este proyecto no pretende buscar métodos eficientes de búsqueda de valores mínimos, pues éstos

ya los proporciona la distinta bibliografía y fuentes de documentación, pero sí que tiene que

proporcionar la implementación y, por tanto, encontrar la forma más idónea para traducir éstos

métodos a código ejecutable, intentando que los tiempos de ejecución se minimicen.

Concluyendo, habrá que decidir sobre la implementación de:

• Grafos: encontrar la estructura idónea para que las operaciones de creación y recorrido tengan

los mejores tiempos teóricos y prácticos.

• TADs: encontrar las estructuras idóneas para minimizar los tiempos de ejecución de sus

métodos u operaciones.

Page 35: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

35

4.1. Representaciones de un grafo

A continuación se exponen distintas posibles representaciones de un grafo. Al final se decidirá

cuál de ellas es la más idónea para este proyecto.

4.1.1. Matriz de incidencias nodo-arista

Esta representación consiste en una matriz M de tamaño N x A (número de vértices x aristas del

grafo).

La columna correspondiente a la arista (i, j) únicamente tiene dos elementos distintos de 0. Tiene

+1 en la fila correspondiente al nodo i y -1 a la fila correspondiente al nodo j.

Las características de esta implementación son:

• La matriz M sólo tiene 2A de sus NA entradas con valores diferentes a 0.

• Los valores distintos a 0 son +1 o -1.

• Cada columna tiene exactamente un +1 y un -1.

• El número de +1s en una fila equivale al grado saliente del correspondiente nodo y el número

de -1s en la fila equivale al grado entrante del nodo.

Ventajas:

• Sencillo de implementar.

Desventajas:

• Uso ineficiente del espacio.

• No guarda los pesos de las aristas.

Page 36: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

36

4.1.2. Matriz de adyacencias nodo-nodo

Esta representación consiste en una matriz M de tamaño N x N. La matriz contiene una fila y una

columna para cada nodo y una entrada ij es igual a 1 si (i, j) ∈ A y 0 en caso contrario.

Se puede emplear otra matriz C del mismo tamaño para almacenar los costes de las aristas.

Las características de esta implementación son:

• La matriz tiene N2 elementos.

• Sólo A elementos de la matriz son distintos de 0.

• El coste de una arista (i, j) se obtiene mirando el elemento ij en la matriz C. O(1).

• Los arcos que salen del nodo i se obtienen escaneando la fila i. O(N).

• Los arcos que entran al nodo j se obtienen escaneando la columna j. O(N).

Ventajas:

• Sencillo de implementar.

• Eficiente en espacio si el grafo es lo suficientemente denso.

• Eficiencia temporal a la hora de escanear arcos en grafos lo suficientemente densos.

Desventajas:

• Uso ineficiente de espacio en grafos poco densos.

• Ineficiencia a la hora de escanear arcos en grafos poco densos.

• Uso de una matriz adicional para guardar los costes.

Page 37: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

37

4.1.3. Listas de adyacencia

Esta representación guarda la lista de adyacencia de cada nodo i (el conjunto de nodos j tal

que (i, j) ∈ A) en una lista encadenada.

La lista de adyacencia para el nodo i será una lista encadenada que tendrá |A(i)| celdas y

cada celda corresponderá a la arista (i, j) ∈ A. La celda correspondiente a la arista (i, j) tendrá

tantos campos como información se quiera guardar. Uno de ellos guardará al nodo j. También

se pueden añadir campos para el peso o la capacidad de la arista. El último campo guardará un

enlace a la siguiente celda en la lista de adyacencias. Si la celda es la última de la lista se

guardará un 0 en este campo.

Además, se necesita de un vector de punteros que apunten a la primera celda para cada

lista de adyacencias (hay una por nodo). Se define por lo tanto un vector de tamaño N llamado

primero cuyo elemento primero(i) guarda un puntero dirigido la primera celda de la lista de

adyacencias del nodo i. Si la lista de adyacencias está vacía el puntero tomará el valor 0.

Ventajas:

• Uso eficiente del espacio, independientemente de la densidad del grafo.

• Eficiencia temporal a la hora de escanear las aristas salientes de un nodo.

Inconvenientes:

• No tan sencillo de implementar como las matrices.

Page 38: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

38

4.1.4. Representaciones en estrella

La representación en estrella hacia delante es similar a las listas de adyacencias pero, en vez de

usar listas encadenadas, se usan vectores.

Primeramente se asocia secuencialmente un número a cada arista del grafo, en orden ascendente

según del nodo de donde salgan y, si salen del mismo nodo, de forma arbitraria.

Para cada arista se guarda el nodo origen, nodo destino, coste y capacidad. A cada nodo se le

añade un puntero que se dirige al primero de estos arcos que emanan de él.

Con esta representación se tiene de forma eficiente el conjunto de aristas que salen de un nodo

determinado. Si se desea también conocer los conjuntos de aristas que llegan a los nodos se necesita

una estructura adicional llamada de estrella hacia atrás.

Partiendo de una representación en estrella hacia delante es muy fácil conseguir la versión hacia

atrás. Se van examinando los nodos en orden y, para cada uno, se guarda el destino, origen, coste y

capacidad de los arcos que llegan al nodo i. Como antes, se guarda un puntero que indica para un nodo

dado la primera de las aristas que llegan a él.

Si se juntan las dos representaciones es fácil ver que hay información duplicada. Para evitar esto,

en la representación estrella hacia atrás únicamente se guarda el número de arista pues la información

ya se tiene en la representación estrella hacia delante.

Ventajas:

• Uso eficiente del espacio.

• Eficiencia temporal a la hora de escanear aristas.

• Se tiene información sobre aristas entrantes y salientes para cada nodo.

Inconvenientes:

• Es la versión más difícil de implementar.

• Más difícil de actualizar.

Page 39: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

39

4.2. Diseño de la representación del grafo

Para la representación del grafo en la librería se empleará la representación estrella hacia delante y

atrás, con algunas modificaciones.

La principal razón es la eficiencia temporal que ofrece, pues trabaja únicamente con vectores.

Posiblemente una versión con punteros ofrezca un uso más racional del espacio pero en este proyecto

el factor crítico es la velocidad y, en todo caso, no se desperdicia demasiado el espacio de memoria.

4.2.1. Elementos del grafo

N (entero): Número máximo de nodos. M (entero): Número máximo de aristas. C (entero): Peso máximo de arista. ini_salen (vector de enteros [1..M]): ini_salen[i] guarda el número de la primera arista que sale del

nodo i. ini_llegan (vector de enteros [1..M]): ini_llegan[j] guarda el número de la primera arista que llega

al nodo j. Estructura de datos AristaTAD:

d (entero): nodo destino de la arista. o (entero): nodo origen de la arista. p (entero): peso de la arista. sig_d (entero): número de la siguiente arista incidente al origen. sig_o (entero): número de la siguiente arista incidente al destino.

aristas (vector de AristaTAD): aristas[a] guarda la información de la arista con número a.

Page 40: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

40

Ejemplo:

Nótese que es un grafo con aristas bidireccionales. Por tanto, en la estructura se guardará la

información relativa al grafo como si las aristas se desglosaran en dos, una por cada sentido. Se

numerarán con números impares las que vayan en un sentido y con número pares en el otro.

En la figura 4 los números en las aristas indican los pesos de las mismas.

En la figura 5 los números en las aristas indican una posible numeración de las mismas al

guardarlas en la estructura.

Figura 4 y 5. Ejemplo de representación de grafo.

1 2 3

4 5

11

20 12

30 22

40

1 2 3

4 5

1/2

3/4 7/8

5/6 9/10

11/12

Page 41: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

41

N: 5 M: 6 C: 0 Ini_salen Ini_llegan -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

aristas Arista 1 2 3 4 5 6 7 8 9 10 11 12 d 4 o 1 p 11 sig_d -1 sig_o -1 C = 11 Ini_salen Ini_llegan

1 -1 -1 -1 -1 -1 -1 -1 1 -1 aristas Arista 1 2 3 4 5 6 7 8 9 10 11 12 d 4 1 o 1 4 p 11 11 sig_d -1 -1 sig_o -1 -1 Ini_salen Ini_llegan

1 -1 -1 2 -1 2 -1 -1 1 -1 aristas Arista 1 2 3 4 5 6 7 8 9 10 11 12 d 4 1 2 o 1 4 1 p 11 11 20 sig_d -1 -1 -1 sig_o -1 -1 1 C = 20 Ini_salen Ini_llegan

3 -1 -1 2 -1 2 3 -1 1 -1 aristas Arista 1 2 3 4 5 6 7 8 9 10 11 12 d 4 1 2 1 o 1 4 1 2 p 11 11 20 20 sig_d -1 -1 -1 2 sig_o -1 -1 1 -1 C = 20 Ini_salen Ini_llegan

3 4 -1 2 -1 4 3 -1 1 -1 etc…

Page 42: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

42

4.3. Implementación de las estructuras de datos.

Las estructuras de datos eficientes son muy distintas entre sí. Pretenden la mayoría minimizar los

costes temporales a la hora de encontrar el valor mínimo de entre todos los que guarda.

Cada una de ellas hace hincapié en algún aspecto. Con matices, unas dedican sus esfuerzos en

tener bien localizado siempre el valor mínimo con lo que los mayores tiempos de ejecución se dan a la

hora de insertar los valores y otras los insertar sin preocuparse, teniendo que realizar las búsquedas

posteriormente.

Las hay desde simples listas hasta estructuras complejas como los heaps de Fibonacci. Todas ellas

tienen en común su objetivo: almacenar un conjunto de valores y permitir la extracción del valor

mínimo, así como de insertar nuevos elementos.

Mayoritariamente estas estructuras tienen forma de árbol, o parecidas: elementos que guardan los

valores enlazados con otros elementos. Por ello, es fácil pensar en implementaciones basadas en

punteros, minimizando el espacio en memoria.

Pero, como se hizo con la estructura del grafo, se sacrificará el uso de la memoria para obtener un

código más veloz que navegue por los elementos de las estructuras.

Por tanto, se implementarán casi siempre mediante vectores, implicando por ello tener que

renunciar a diseños más intuitivos como podría ser en el caso de usar punteros de memoria. De todas

formas, una vez implementados los métodos básicos y subiendo al siguiente nivel de abstracción, se

podrá realizar casi una copia idéntica del algoritmo proporcionado por la documentación al código

implementador.

Page 43: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

43

5. Especificación del software.

5.1. Modelo conceptual.

5.1.1. Introducción. ¿Qué elementos deben aparecer en el modelo conceptual?

Básicamente dos: el grafo donde se quiere encontrar el/los camino/s mínimo/s y la variante del

algoritmo que se empleará para lograrlo.

Debería existir una clase representativa (por ejemplo CamMin) de las variantes que, además,

incluya una parte común a todas ellas. Esto hace pensar en aplicar un patrón controlador: la clase

CamMin se encarga de recibir los parámetros de entrada (grafo, nodo origen y destino del grafo,

variante de algoritmo deseada) y de guardar los de salida (solución del problema).

Esto proporciona una total transparencia de cara al usuario. La interacción es únicamente con esta

clase y no se conoce absolutamente nada de lo que hay detrás. Las distintas implementaciones de los

algoritmos se podrán así modificar en un futuro sin que cambie el método de trabajo. Además, se

facilita el poder añadir nuevas versiones de algoritmos o, en caso extremo, eliminar alguna.

El siguiente paso es ver qué clases representarán a las implementaciones de los algoritmos. Una

primera aproximación (la más simple) es crear una clase por versión que se encargue precisamente de

ejecutar el algoritmo correspondiente pero observando las siete versiones propuestas es fácil ver que

varios de ellos tienen mucho en común y que sería conveniente seguir con este diseño de detectar

partes comunes y asignarlas a clases más genéricas.

Page 44: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

44

Los grupos serían:

• Dijkstra.

o Dijkstra original. o d-Heap. o Fibonacci.

• Dial • RadixHeap • Corrector de etiqueta

o Corrector o Dequeue

La única diferencia que existe entre Dijkstra original, d-Heap y Fibonacci es la estructura de datos

que guarda las etiquetas temporales de los nodos que se van visitando. Por tanto, la solución ideal es

diseñar una clase Dijkstra general que resuelva el problema indicándole siempre qué estructura

temporal se quiere usar.

Esto permitirá asimismo poder en un futuro añadir nuevas estructuras de datos que resulten más

eficientes que las actuales. La única tarea sería diseñar e implementar esta nueva estructura de datos y

añadirla a la librería, sin modificar absolutamente nada la clase que implementa el algoritmo de

Dijkstra.

Algo parecido similar resulta en los algoritmos correctores de etiqueta, donde la única diferencia

sigue estando en las estructuras de datos empleadas.

Dial y RadixHeap sí que son algoritmos distintos y por lo tanto tendrán una clase cada uno que los

implemente completamente.

Finalmente, y como se podría imaginar, existirá una clase por cada estructura de datos a

implementar. Concretamente: lista doblemente encadenada (para dijkstra, corrector, el TAD balde y el

TAD radixheap), balde (para Dial), d-heap (para dijkstra con d-heap), fibonacci heap (para dijkstra

con fibonacci heap), radix heap (para radix heap) y dequeue (para el método corrector con dequeue).

Page 45: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

45

5.1.2 Aparición de nuevos algoritmos Una revisión detallada del algoritmo de RadixHeap muestra que en cada balde se tiene una lista

doblemente encadenada donde se tienen etiquetas dentro de un rango. De todas estas etiquetas en un

momento dado se busca la de menor valor. Es decir, se busca en una lista doblemente encadenada el

elemento de valor mínimo.

Una posible modificación que podría mejorar la eficiencia de este algoritmo sería guardar estos

valores en d-heaps o en fibonacci heaps. Aparecerían entonces tres versiones del RadixHeap: con

listas doblemente encadenadas, con d-heaps y con fibonacci heaps. A priori se desconoce si esta

modificación será efectivamente una mejora pero un diseño que permita utilizar cualquier estructura

de datos temporal para guardar estas etiquetas siempre será beneficiosa. Tal vez ninguna de estas

versiones sea mejor pero en un futuro se puede encontrar una estructura de datos que sea realmente

efectiva y su inserción en el algoritmo debería ser lo más simple y transparente posible.

Por tanto, se añaden dos nuevos algoritmos más, haciendo un total de nueve:

Dijkstra con listas doblemente encadenadas.

Dijkstra con d-heaps.

Dijkstra con fibonacci heaps.

Dial.

RadixHeap con listas doblemente encadenadas.

RadixHeap con d-heaps.

RadixHeap con fibonacci heaps.

Corrector de etiqueta.

Corrector de etiqueta con dequeues.

5.1.3. Herramienta de especificación y diseño

La especificación y el diseño teórico del sistema ha sido desarrollado mediante la metodología del

Lenguaje Unificado de Modelado (UML), generando los diagramas, clases y casos de uso que se

presentan en los siguientes apartados.

Page 46: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

46

5.1.4. Diagrama de clases

1 *

Dijkstra ejecuta() damePadres() dameDistancias() dameDestino()

Dial ejecuta() damePadres() dameDistancias() dameDestino()

RadixHeap ejecuta() damePadres() dameDistancias() dameDestino()

Corrector ejecuta() damePadres() dameDistancias() dameDestino()

Corrector2 ejecuta() damePadres() dameDistancias() dameDestino()

Dlista inserta(ent, ent) inserta_delante(ent, ent) borra(entero) dameMinimo() : ent printaLista() damePrimero() : ent dameValor(ent) : ent modifica(ent, ent) esMiembro?(ent) : bool esVacio?() : bool dameTam() : ent borraMinimo() : ent

DHeap inserta(elem, clave) borra(elem) dameMinimo() : elem dameClave(elem) : clave esMiembro?(elem) : bool esVacio?() : bool borraMinimo() : elem modifica(elem, clave) imprime()

FibHeap dameClave(elem) : clave inserta(elem) inserta(elem, clave) modifica(elem, clave) borra (elem) borraMinimo() : elem dameMinimo() : elem imprime() esMiembro?(elem) : bool esVacio?() : bool

Radix pertRango?(valor, balde) : bool contenido(balde) : {DLista, DHeap, FibHeap} insNodo(nodo, valor, balde) insNodo(nodo, valor) borraNodo(nodo, balde) borraNodo2(nodo, valor) imprimeBaldes() dameMin() : nodo

Balde insNodo(nodo, ent, ent) contenido() : DLista borraNodo(nodo, ent) imprimeBaldes()

Grafo n : entero m : entero C : entero

damePrimSale(vertice) : arista damePrimLlega(vertice) : arista dameSigSale(arista) : arista dameSigLlega(arista) : arista dameDestino(arista) : vertice dameOrigen(arista): vertice damePeso(arista) : peso creaArista(vertice, vertice, peso) leeGrafo(fichero) : bool grabaGrafo(fichero) creaGrafoAzar(ent, doble, ent, ent) dimeArista(vertice, vertice) : arista ponPeso(arista, peso)

CamMín origen : ent destino : ent padres : vector de ent dist : vector de ent ejecuta() damePadres() dameDistancias() dameDestino()

TAD 1

1

1 1

*

*

*

*

Page 47: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

47

Con este diseño conceptual se pretende sobretodo dos cosas:

• Ofrecer tres partes bien diferenciadas de la que partir: Grafo, Algoritmo de camino mínimo y

Estructura eficiente de datos. Con esto se logra que el usuario pueda ver de un simple vistazo

el funcionamiento del sistema: El algoritmo de camino mínimo trabaja sobre un grafo

empleando, quizá, estructuras eficientes de datos.

A partir de ahí, se podrá profundizar más en los detalles, pero no habrán más partes

involucradas en el proceso general.

Este modelo permitirá a un futuro desarrollador implementar nuevos algoritmos o estructuras

de datos (o incluso otras implementaciones de grafos) basándose en él. Simplemente deberá

respetar el formato de los métodos de las clases CamMin y Grafo.

• Aislar cada una de las partes que forman el sistema de forma que facilite futuras

modificaciones.

Una modificación en la implementación de una estructura eficiente de datos o de un algoritmo

en concreto nunca significará tener que modificar absolutamente nada en otra estructura o

algoritmo. La separación entre ellos es máxima. Incluso se pueden crear otras estructuras e

insertarlas en el modelo para que las usen los algoritmos. El proceso no implicará cambiar

ninguna línea de código en el resto de clases, excepto quizá en el nombre de la estructura

cuando sea llamada pero, si por ejemplo se quiere sustituir una de las implementaciones de un

TAD por otra que parezca ser más eficiente, no se modificará nada del código de los

algoritmos.

Page 48: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

48

5.1.5. Clases del dominio

Clase Grafo: Implementa el grafo donde se buscarán los caminos mínimos. Proporciona métodos

para su creación mediante funciones básicas, lectura de ficheros con formato específico o

generadores aleatorios, así como métodos para la navegación.

Clase CamMin: Clase de partida para las distintas implementaciones de los algoritmos de

búsqueda de caminos mínimos. Su única función es proporcionar una interficie al usuario con los

métodos para inicializar el algoritmo, ejecutarlo y recuperar los datos de la solución.

Clase TAD: Clase de partida para las distintas implementaciones de la estructuras eficientes que

emplearán los algoritmos de búsqueda de caminos mínimos. Su función es la de proporcionar una

interficie común a todas las estructuras de datos de cara a los algoritmos.

Clases Dijkstra, Dial, RadixHeap, Corrector y Corrector2: Son las implementaciones de los

distintos algoritmos. Nunca se interactuará directamente con ellas, sino a través de la clase más

general CamMin. Algunas de estas clases necesitan obligatoriamente usar estructuras de datos

específicas. Otras, pueden ir asociadas a alguna de las estructuras de datos eficientes y, por tanto,

implementan las versiones de los algoritmos de este proyecto.

Clase Balde: Implementa la estructura Balde, utilizada por el algoritmo de Dial.

Clase Radix: Implementa la parte del algoritmo de RadixHeap dedicada a la interacción con las

estructuras de datos eficientes.

Clases DLista, DHeap y FibHeap: Son implementaciones de las estructuras de datos eficientes que

pueden emplear algunos de los algoritmos.

Page 49: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

49

5.1.6. Casos de Uso

Figura 5

El usuario de la librería únicamente puede interactuar con el grafo y con los algoritmos, a través

de la superclase CamMin.

Es cierto que los métodos de las demás clases son públicos, pero no tiene sentido llamarlos

directamente en un uso normal del sistema, esto es, crear u obtener un grafo y buscar los caminos

mínimos en él.

5.1.7. Diagrama de secuencia

A continuación se mostrará el diagrama de secuencia para el caso de uso más general y frecuente:

el de la creación de un grafo y ejecución de uno de los algoritmos para encontrar caminos mínimos.

Page 50: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

50

G: Grafo

crear

leeGrafo(fichero)

: Dijkstra

Crear(G, origen, destino, padres, distancias)

n

m

: FibHeap

esVacio?()

borraMínimo()

damePrimSale(i)

dameSigSale(a)

i

a

damePeso(a)

Inserta(j)

dameDestino(a)j

Ejecuta()

dameDistancias()

damePadres()

Figura 6. Diagrama de secuencia general del sistema.

Page 51: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

51

5.1.4. Pseudo código de las operaciones A continuación se muestra a modo de ejemplo el pseudocódigo de algunas de las clases más

representativas del sistema.

Grafo grafo (entero NN, entero MN) { N = NN; M = MM + 1; ini_llegan = vector vacío de aristas[N+1]; ini_salen = vector vacío de aristas[N+1]; aristas = vector vacío de aristaTAD[M+1]; n = N; m = 0; C = 0; para todos los vértices u del grafo hacer ini_salen[u] = ini_llegan[u] = nulo; } arista damePrimSale(vertice v) devuelve ini_salen[v]; arista damePrimLlega(vertice v) devuelve ini_llegan[v]; arista dameSigSale(arista a) devuelve aristas[a].sig_o; arista dameSigLlega(arista a) devuelve aristas[a].sig_d; vertice dameDestino(arista a) devuelve aristas[a].d; vertice dameOrigen(arista a) devuelve aristas[a].o; vertice damePeso(arista a) devuelve aristas[a].p; entero creaArista(vertice u, vertice v, peso p) { crea un aristaTAD con número asociado m +1 insertando la información proporcionada por los parámetros. m = m +1; si p > C entonces C = p; se actualiza la información de los vectores ini_salen e ini_llegan } booleano leeGrafo(fichero) { comprueba que el formato del grafo sea el correcto. inicializa las estructuras del grafo. mientras no se llegue a final de fichero hacer { leer arista e introducirla en el vector aristas

Page 52: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

52

actualizar C si hace falta } actualizar los vectores ini_salen e ini_llegan y la información relativa a las listas de adyacencias devolver cierto si no ha habido error. } grabaGrafo(fichero) { para cada arista del grafo hacer insertar el origen, destino y peso de la arista en el fichero con el formato determinado } creaGrafoAzar(entero NN, doble p, entero costmax, entero separacion) { genera un grafo dirigido al azar de NN vértices con probabilidad de arista p. Esto se consigue creando aristas sólo desde vértices con número inferior hasta vértices con número superior. Las aristas se restringen para que sólo puedan unir vértices cuyos índices están como mucho a la separación indicada. Genera los pesos de las aristas uniformemente en el intérvalo [1, costmax]. } arista dimeArista(vertice o, vertice d) { arista sale = damePrimSale(o); mientras dameDestino(sale) diferente a d hacer sale = dameSigSale(sale); si sale igual a nulo devuelve 0 (no hay arista entre los vértices). si no devuelve sale (devuelve la arista que hay entre los dos vértices). } ponPeso(arista a, peso p) aristas[a].p = p;

Page 53: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

53

CamMin CamMin(grafo graf, vertice o, vertice d, vertice pad[], entero dst[]) { G = graf; orig = 0; dest = d; padres = pad; dist = dst; } CamMin(grafo graf, vertice o, vertice pad[], entero dst[]) { G = graf; orig = 0; dest = -1; padres = pad; dist = dst; } ejecuta() { si dest igual a 1 entonces ejecuta1(); si no entonces ejecuta2(); } vertice* damePadres() devuelve padres; entero* dameDistancias() devuelve dist; vertice dameDestino() devuelve dest;

Page 54: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

54

Dijkstra ejecuta1() { vertice i,j; arista a; entero d = G.n/G.m si d < 2 entonces = 2; TAD V(G.n, d); para cada nodo i del grafo G hacer { padres[i] = nulo; dist[i] = ∞; } padres[orig] = orig; dist[orig] = 0; V.inserta(orig, 0); mientras V esté lleno hacer { i = V.borraMinimo(); si i igual a dest acabar; para toda arista saliente de a hacer { entero peso_a = G.damePeso(a); si peso_a igual a ∞ continuar; j = G.dameDestino(a); si dist[j] > (dist[i] + peso_a) hacer { dist[j] = dist[j] + peso_a; si (dist[j] < 0) entonces dist[j] = ∞; padres[j] = i; si V.esMiembro(j) entonces V.modifica(j, dist[j]); si no entonces V.inserta(j, dist[j]); } } } padres[orig] = nulo; }

Page 55: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

55

ejecuta2() { vertice i, j, k; vertice h[G.n + 1]; int d2[G.n + 1]; arista a; int D = G.n/G.m; si (D < 2) entonces D = 2; TAD V(G.n, D); TAD VV(G.n, D); int permanentes[G.n + 1]; para cada nodo i del grafo hacer { h[i] = padres[i] = nulo; dist[i] = d2[i] = ∞; permanentes[i] = 0; } padres[orig] = orig; h[dest] = dest; dist[orig] = 0; d2[dest] = 0; V.inserta(orig, 0); VV.inserta(dest, 0); booleano fin = falso; int mini = 0; int minj = 0; int dist_min_dir = ∞; int dist_min_rev = ∞; int compara; mientras !fin hacer { si (mini igual a 0) entonces mini = V.dameMinimo(); si (minj igual a 0) entonces minj = VV.dameMinimo(); si (dist[mini] <= d2[minj]) entonces { i = mini; si (dist[mini] >= dist_min_dir) entonces { fin = cierto; k = dest; continua; } si (permanentes[i]) entonces { fin = cierto; k = i; continua; } si no entonces permanentes[i] = 1; si (fin igual a cierto) entonces continua; V.borra(i); para cada arista que sale de i hacer { entero peso_a = G.damePeso(a); si (peso_a igual a ∞) entonces continua; j = G.dameDestino(a); compara = dist[i] + peso_a; si (dist[j] > compara y compara >= 0) entonces

Page 56: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

56

{ dist[j] = compara; si (dist[j] < 0) entonces dist[j] = ∞; padres[j] = i; si (j == dest) entonces dist_min_dir = dist[j]; si V.esMiembro(j) entonces V.modifica(j, dist[j]); si no entonces V.inserta(j, dist[j]); } } mini = 0; } si no entonces { j = minj; si (d2[j] > dist_min_rev) entonces { fin = cierto; k = orig; continua; } si (permanentes[j] > 0) entonces { fin = cierto; k = j; continua; } si no entonces permanentes[j] = 2; si (fin == cierto) continua; VV.borra(j); para cada arista que llega a j hacer { entero peso_a = G.damePeso(a); si (peso_a igual a ∞) entonces continua; i = G.dameOrigen(a); compara = d2[j] + peso_a; si (d2[i] > compara y compara > 0) entonces { d2[i] = compara; si (d2[i] < 0) entonces d2[i] = ∞; h[i] = j; si (i igual a orig) entonces dist_min_rev = d2[i]; si VV.esMiembro(i) entonces VV.modifica(i, d2[i]); si no entonces VV.inserta(i, d2[i]); } } minj = 0; } construir la solución a partir de las soluciones parciales hacia delante y hacia atrás. }

Page 57: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

57

5.2. Herramientas y base teórica

5.2.1 Programación orientada a objetos En el desarrollo de la aplicación que implementará la librería de caminos mínimos, se utiliza el

método de programación orientada a objetos. Ésta se basa en los conceptos de objeto y clase. Una

definición para un objeto puede ser “unidad atómica formada por la unión de un estado y de un

comportamiento”. El objeto permite encapsulación, proporcionando una cohesión interna muy fuerte y

un débil acoplamiento con el exterior. En este contexto, un objeto informático define una

representación de las entidades de un mundo real o virtual, con el objetivo de controlarlos o

simularlos. Todo objeto presenta las tres características siguientes: un estado, un comportamiento y

una identidad.

• Estado: Es la agrupación de los valores instantáneos de todos los atributos de un objeto,

sabiendo que un atributo es una información que cualifica al objeto que la contiene.

• Comportamiento: El comportamiento agrupa todas las competencias de un objeto y describe

las acciones y reacciones de ese objeto. Cada átomo de comportamiento se llama operación.

• Identidad: La identidad permite distinguir los objetos de forma no ambigua,

independientemente de su estado. Esto permite distinguir dos objetos en los que todos los

valores de atributos son idénticos.

Una vez definido el concepto de objeto, se define el concepto de clase. Una clase es a un objeto

como un tipo de datos es a una variable. En terminología de programación orientada a objetos se dice

que un objeto es una instancia de una clase.

Este tipo de programación ofrece una serie de ventajas respecto a la tradicional:

• Es la máxima expresión de la abstracción de los lenguajes imperativos.

• Permite una mayor modularidad del código, debido a que está claramente diferenciada la

visión externa de un objeto, de la interna. La visión externa informa de los mensajes que es

capaz de mandar y de recibir y de los cambios de estado que ese producen en el objeto en

cuestión. Debido a que para la utilización de un objeto no hay que tener conocimientos de la

visión interna ésta se puede cambiar sin afectar al sistema, con lo que la modularidad se hace

patente. Esta característica se hace posible con la encapsulación de los diferentes objetos, de

modo que todo acceso a dicho objeto sólo se pueda hacer a través de los mensajes y métodos

definidos como públicos.

Page 58: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

58

• La herencia permite el acceso automático a la información contenida por otras clases. Con la

herencia todas las clases están clasificadas en una jerarquía estricta. Cada clase tiene una

superclase, la clases superior en la jerarquía, y cada clases puede tener una o más subclases,

las clases inferiores en la jerarquía.

• El polimorfismo es la característica que permite implementar múltiples formas de un mismo

método, dependiendo cada una de ellas de la clase sobre la que se realice la implementación.

Esto hace que se pueda acceder a una variedad de métodos distintos utilizando exactamente el

mismo medio de acceso.

5.2.2. C++

Por sus características y su adecuación a los objetivos, C++ ha sido el lenguaje de programación

seleccionado para el desarrollo de la librería de caminos mínimos.

C++ es un derivado del lenguaje C. Este lenguaje apareció en los años 70 de la mano de Dennos

Ritchie para la programación en sistemas Unix, el cual surgió como un lenguaje generalista

recomendado sobre todo para programadores ya expertos, pues no llevaba implementadas muchas

funciones que hacen a un lenguaje más comprensible. Esto, sin embargo, permite un mayor control

sobre lo que se está haciendo.

C++ se creó unos años más tarde de la mano de Bjarne Stroustrup. Necesitaba de ciertas

facilidades de programación, incluidas en otros lenguajes pero que C no soportaba, al menos

directamente, como llamadas a clases y objetos, muy de moda por aquel entonces.

Rediseñó el C, ampliando sus posibilidades pero manteniendo su mayor cualidad, la de permitir al

programador en todo momento tener controlado lo que está haciendo, consiguiendo así una mayor

rapidez que no se conseguiría con otros lenguajes.

El sistema se desarrollará en este lenguaje. Las razones más importantes son:

• Velocidad. C y C++ son dos de los lenguajes que generan código más veloz y eficiente.

• Orientación a objetos. C++ permite programar con objetos, con todas las ventajas que ello

conlleva anteriormente expuestas.

• Integración. C++ resulta muy sencillo de integrar en tras aplicaciones aún cuando éstas no

hayan sido desarrolladas en este lenguaje. Además, puede hacerse en cualquier plataforma.

Page 59: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

59

6. Manual de Uso

6.1 Introducción

La librería se compone del fichero libdijkstra.a y un conjunto de ficheros cabecera .h.

Todas las implementaciones vienen representadas por una clase que, a su vez, deriva de una

genérica CamMin. Así, el uso de ellas es idéntico para cada una.

En la siguiente tabla se incluyen los nueve algoritmos, la clase que los implementa y las cabeceras

necesarias a incluir en el programa que los use:

Dijkstra básico. Dijkstra<DLista> #include “dijkstra.h”

#include “dlista.h”

Dijkstra con d-heaps. Dijkstra<DHeap> #include “dijkstra.h”

#include “dheap.h”

Dijkstra con fibonacci heaps. Dijkstra<FibHeap> #include “dijkstra.h”

#include “fibo.h”

Dial. Dial #include “dial.h”

Radix básico. RadixHeap<Radix> #include “radixheap.h”

#include “radix.h”

Radix implementado con d-heaps. RadixHeap<RadixDHeap> #include “radixheap.h”

#include “radixdheap.h”

Radix implementado con fibonacci

heaps.

RadixFib<RadixFib> #include “radixheap.h”

#include “radixfib.h”

Corrector de etiqueta básico. Corrector #include “corrector.h”

Corrector de etiqueta con “dequeue”. Corrector2 #include “corrector2.h”

Las clases Dijkstra y RadixHeap usan una plantilla para ir acompañadas de la clase que implementa

las estructuras de datos que utiliza.

Page 60: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

60

6.2. Procedimiento para ejecutar un algoritmo de la librería

Escoger la versión o algoritmo que se desee emplear. Anotar, según la tabla anterior, qué clase o

clases son necesarias así como los ficheros de cabecera a incluir.

Ejemplo: Queremos usar la versión Dijkstra con Heap de Fibonacci. Mirando en la tabla vemos que

emplearemos la clase Dijkstra<FibHeap> y deberemos incluir los ficheros cabecera “dijkstra.h” y

“fibo.h”.

En el programa que vaya a utilizar la librería, incluir los ficheros de cabecera necesarios.

Ejemplo: “include dijkstra.h” e “include fibo.h”.

Crear el grafo donde se necesita buscar el/los camino/s mínimo/s. Para ello se pueden usar las

funciones y métodos de la clase Grafo (será necesaria la inclusión también de la cabecera

correspondiente). Uno de los métodos de esta clase permite leer un grafo completamente desde un

fichero de texto con un formato predeterminado. Para más información véase creación del grafo.

Grafo nombre_de_grafo(int número_de_nodos, int número_de_aristas);

Ejemplo:

Grafo migrafo(100, 300);

Crear las variables solución:

int* padres = new int[número_de_nodos + 1];

int* distancias = new int[número_de_aristas + 1];

Ejemplo:

int* padres = new int[101];

int* distancias = new int[301];

Page 61: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

61

Al acabar la ejecución del algoritmo, padres será el árbol de recubrimiento del grafo con raíz el

nodo origen indicado o, para la versión bidireccional del algoritmo, será el camino desde origen hasta

destino.

El índice del vector representa el nodo del grafo y el valor que contiene, el nodo predecesor, ya sea

en el árbol de recubrimiento o en el camino directo.

Ejemplo: Reconstrucción del camino desde 1 hasta 7.

Similar a padres, distancias contendrá, para un nodo dado (índice del vector), la distancia al origen

en el árbol de recubrimiento o camino directo según modo.

1

2

3

4

5 6 7

0 2 1 3 3 5 6

1 6 3 4 5 2 7 nodo

padre

2

1

4

2

2 3

0 1 2 6 4 6 9

1 6 3 4 5 2 7 nodo

distancia

Figura 7. Reconstrucción de la solución del problema.

Figura 8. Vector solución de distancias.

Page 62: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

62

Crear un nuevo objeto de la clase que implementa el algoritmo escogido. En este punto hay que

tener decidido, si cabe, si se requiere el modo unidireccional o el bidireccional.

En el modo unidireccional será necesario conocer el vértice origen desde donde se quieren

encontrar los caminos mínimos. En el modo bidireccional habrá que conocerse la pareja de nodos

origen-destino.

6. 2. 1. Unidireccional:

Clase<TAD> nombre_objeto(*nombre_de_grafo, int nodo_origen, padres, distancias);

Ejemplo:

Dijkstra<FibHeap> mialgoritmo(*migrafo, 3, padres, distancias);

6. 2. 2. Bidireccional:

Clase<TAD> nombre_objeto(*nombre_de_grafo, int nodo_origen, int nodo_destino, padres,

distancias);

Ejemplo:

Corrector mialgoritmo2(migrafo, 3, 6, padres, distancias);

Finalmente:

nombre_objeto.ejecutar();

Ejemplo:

mialgoritmo.ejecutar(); mialgoritmo.damePadres(); mialgoritmo.dameDistancias();

Las variables padres y distancias contendrán la solución al problema.

Page 63: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

63

6. 3. Creación del grafo

Existen dos formas para crear el grafo donde se buscará el camino mínimo.

La más sencilla es usar la función leeGrafo(FILE*) que lee el grafo desde un fichero de texto

simple. El formato del grafo es el siguiente:

nº nodos nº aristas arista1 arista2 ... aristaM El formato de arista es: (nodo_origen,nodo destino,peso) Ejemplo: 6 9 (1,3,4) (1,2,6) (2,3,2) (2,4,2) (3,5,2) (3,4,1) (4,6,7) (5,4,1) (5,6,3)

Si la aplicación donde vamos a usar los algoritmos de caminos mínimos genera el grafo de forma

dinámica, habrá que crearlo paso a paso con las funciones de la clase Grafo.

Page 64: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

64

6.4. Clases y métodos públicos

6.4.1. Grafo • grafo(num_nodos, num_aristas);

Crea un grafo con num_nodos nodos y num_aristas aristas.

• int n; Devuelve el número de nodos del grafo.

• int m; Devuelve el número de aristas del grafo.

• int C; Devuelve el peso de la arista con peso máximo del grafo.

• arista damePrimSale(vertice v); Devuelve la primera arista que sale del vértice v.

• arista damePrimLlega(vertice v); Devuelve la primera arista que llega al vértice v.

• arista dameSigSale(arista a); Devuelve la siguiente arista que sale del origen de la arista a.

• arista dameSigLlega(arista a); Devuelve la siguiente arista que llega al destino de la arista a.

• vertice dameDestino(arista a); Devuelve el destino de la arista a.

• vertice dameOrigen(arista a); Devuelve el origen de la arista a.

• peso damePeso(arista a); Devuelve el peso de la arista a.

• int creaArista(vertice o, vertice d, peso p); Crea una arista (o, d) de peso p. Devuelve el número de aristas del grafo.

Page 65: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

65

• bit leeGrafo(FILE* f);

Lee un grafo del fichero indicado.

• void grabaGrafo(FILE* f); Graba un grafo en el fichero indicado.

• void creaGrafoAzar(int NN, double p, int costmax, int separacion); Genera un grafo dirigido al azar de NN vértices con probabilidad de arista p.

Esto se consigue creando aristas sólo desde vértices con número inferior hasta vértices con número

superior. Las aristas se restringen para que sólo puedan unir vértices cuyos índices están como mucho

a la separación indicada. Genera los pesos de las aristas uniformemente en el intérvalo [1, costmax].

• void creaGrafoCompleto(int NN, int costmax, int prob);

Similar a creaGrafoAzar pero el grafo resultante es conexo.

• arista dimeArista(vertice o, vertice d); Devuelve la arista que une los dos vértices. Esta función no tiene necesariamente coste de ejecución

constante.

• void ponPeso(arista a, peso p);

Pone el peso p a la arista a.

• void ponPeso2(arista a, peso p); Pone peso a la arista a y a su siguiente (para mejorar eficiencia en grafos bidireccionales).

• void ponPesos(peso *pesos)

Pone los pesos indicado en el vector a los pesos de las aristas del grafo. El índice del vector equivale al

número de arista. Si no se quiere modificar el peso de alguna se indica poniendo el valor –1 en la

posición del vector correspondiente.

Page 66: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

66

6.4.2. CamMin

• CamMin(grafo& grafo, vertice o, vertice dt, vertice* p, int* d) Versión bidireccional del algoritmo. Crea e inicializa el problema del camino mínimo que se quiere

buscar en grafo, con vértice de origen o y vértice destino dt. En los vectores p y d se guardará la

solución del problema. p será el árbol de recubrimiento de la solución y d las distancias mínimas al

origen para cada vértice del grafo.

• CamMin(grafo& grafo, vertice o, vertice* p, int* d)

Versión normal del algoritmo. Crea e inicializa el problema del camino mínimo que se quiere buscar

en grafo, con vértice de origen o. En los vectores p y d se guardará la solución del problema. p será el

árbol de recubrimiento de la solución y d las distancias mínimas al origen para cada vértice del grafo.

• void ejecuta()

Ejecuta el algoritmo.

• vertice* damePadres() Devuelve el árbol de recubrimiento de la solución del problema.

• int* dameDistancias() Devuelve las distancias mínimas al nodo origen para cada vértice del grafo.

• vertice dameDestino()

Devuelve el nodo destino indicado en la inicialización del algoritmo. Si se está trabajando con la

versión normal y, por tanto, no hay nodo destino, devuelve -1.

Page 67: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

67

7. Resultados

7.1. Preparativos

Una vez desarrollada e implementada la librería, se procede a probar sus resultados.

El primer punto principal a comprobar es que las salidas que proporcionan los algoritmos sean

correctas. Este proceso puede implicar una consiguiente depuración del código para detectar posibles

fallos, en mayor parte de implementación.

Una vez se tenga el código depurado de errores, se realizan pruebas exhaustivas ejecutando los

algoritmos en multitud de grafos insertando todo tipo de variables. La primera observación de que

todo va por buen camino es que todos los algoritmos den la misma solución.

De todas formas, hay que garantizar de algún modo que la solución sea correcta. Debido a la

enorme dificultad de encontrar “a mano” el camino mínimo en un grafo de tamaño considerable, se

trabajó con varias implementaciones del algoritmo básico de Dijkstra encontradas en Internet. Estas

implementaciones, mayoritariamente applets en java, sirvieron para poder comparar soluciones reales

con las de la librería desarrollada.

Después de asegurarse de la correcta funcionalidad de los algoritmos implementados en la librería,

queda el punto de la eficiencia. Como se expuso anteriormente, cada algoritmo se comporta

teóricamente de forma distinta según el grafo de entrada pero hace falta conocer cómo es la eficiencia

de cada uno en casos reales.

Para estudiar la eficiencia del algoritmo se creó un pequeño programa cuyo código se muestra a

continuación:

int main(int argc, char* argv[])

{

int n = atoi(argv[1]);

int C = atoi(argv[2]);

int prob = atoi(argv[3]);

int version = atoi(argv[4]);

Page 68: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

68

Grafo G;

G.creaGrafoCompleto(n,C,prob);

vertice* p = new vertice[G.n+1];

int* d = new int[G.n+1];

int destino, origen;

origen = 1;

destino = -1;

int i;

printf(“GRAFO: %d %d %d\n”,G.n,G.m,G.C);

if (version == 1)

{

Dijkstra<DLista> D(G,origen,p,d);

for(i = 1;i<=1000;i++) D. ejecuta();

exit(0);

}

Esta aplicación recibe como parámetros de entrada un tamaño de grafo, una densidad de

aristas, un peso máximo de arista y la versión con la que se quiera que se resuelva. Crea un grafo con

las características indicadas y resuelve el problema del camino mínimo versión un origen/todos los

destinos con el algoritmo indicado.

Mediante un sencillo fichero script se automatiza la tarea de ejecutar este programa multitud

de veces con todas las combinaciones posibles de variables, analizando el tiempo necesario de

ejecución para cada una (en realidad una media de varias muestras).

Page 69: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

69

7.2. Resultados:

D T C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

100 3000 1000 100,00 100,00 110,00 200,00 110,00 110,00 130,00 220,00 230,00

100 3000 100 100,00 100,00 100,00 120,00 100,00 110,00 120,00 110,00 110,00

100 3000 10 90,00 100,00 100,00 110,00 100,00 100,00 110,00 80,00 80,00

100 3000 2 90,00 100,00 100,00 110,00 100,00 100,00 100,00 80,00 80,00

100 1000 1000 11,12 11,67 13,40 14,71 12,05 12,82 18,78 23,45 22,74

100 1000 100 10,60 11,51 12,75 13,02 11,13 12,11 15,39 15,55 15,54

100 1000 10 10,34 11,02 11,82 11,92 10,73 10,55 13,49 9,38 9,54

100 1000 2 10,52 10,89 11,15 12,47 9,60 9,80 11,23 8,93 9,19

100 500 1000 3,42 3,71 4,54 5,61 4,07 4,33 7,09 8,04 8,34

100 500 100 3,29 3,42 4,17 4,19 4,04 4,12 6,22 6,73 6,76

100 500 10 3,23 3,77 3,88 4,31 3,89 3,84 4,90 3,20 3,23

100 500 2 3,39 3,48 3,76 3,86 3,51 3,81 3,56 2,96 4,12

100 100 1000 0,11 0,13 0,19 0,21 0,23 0,25 0,47 0,21 0,24

100 100 100 0,12 0,15 0,19 0,05 0,16 0,19 0,44 0,15 0,17

100 100 10 0,12 0,14 0,16 0,19 0,15 0,20 0,27 0,13 0,12

100 100 2 0,10 0,12 0,16 0,10 0,13 0,18 0,30 0,08 0,10

100 10 1000 0,01 0,01 0,01 0,05 0,01 0,01 0,03 0,01 0,01

100 10 100 0,01 0,01 0,01 0,03 0,01 0,01 0,02 0,01 0,01

100 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

100 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

100 5 1000 0,01 0,01 0,01 0,11 0,02 0,01 0,01 0,01 0,01

100 5 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

100 5 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

100 5 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

75 3000 1000 80,00 70,00 80,00 170,00 80,00 90,00 100,00 150,00 160,00

75 3000 100 80,00 80,00 80,00 100,00 80,00 80,00 90,00 90,00 90,00

75 3000 10 80,00 70,00 80,00 90,00 80,00 80,00 90,00 70,00 70,00

75 3000 2 80,00 70,00 70,00 80,00 80,00 70,00 80,00 60,00 60,00

75 1000 1000 8,76 9,05 10,64 11,40 9,26 10,10 15,05 21,35 21,02

75 1000 100 8,69 8,67 10,21 9,90 8,56 9,24 13,40 11,63 11,34

75 1000 10 8,97 8,84 9,96 10,15 8,91 8,57 10,58 7,45 7,43

75 1000 2 8,62 8,83 8,83 9,66 7,39 6,07 9,77 7,65 7,42

75 500 1000 2,57 3,19 3,56 3,84 3,30 3,87 7,01 6,19 6,06

75 500 100 2,51 2,65 3,21 2,85 2,82 3,24 5,02 4,00 4,04

75 500 10 2,48 2,64 2,92 3,19 2,65 2,93 3,84 2,37 2,28

75 500 2 2,48 2,39 2,65 2,90 2,66 2,65 3,13 2,08 2,05

75 100 1000 0,10 0,14 0,22 0,12 0,16 0,29 0,47 0,14 0,18

75 100 100 0,09 0,13 0,17 0,06 0,17 0,26 0,40 0,20 0,15

75 100 10 0,08 0,13 0,17 0,12 0,12 0,16 0,27 0,09 0,08

75 100 2 0,09 0,07 0,09 0,11 0,10 0,13 0,19 0,08 0,04

75 10 1000 0,01 0,01 0,01 0,03 0,01 0,01 0,01 0,01 0,01

Page 70: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

70

75 10 100 0,01 0,01 0,01 0,02 0,01 0,01 0,03 0,01 0,01

75 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

75 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

75 5 1000 0,01 0,01 0,01 0,11 0,01 0,01 0,01 0,01 0,01

75 5 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

75 5 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

75 5 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

50 3000 1000 60,00 54,75 60,00 146,30 57,72 62,05 78,73 128,88 120,00

50 3000 100 60,75 50,87 54,88 70,54 55,23 56,79 69,31 62,39 65,48

50 3000 10 59,37 48,86 52,05 57,83 52,84 53,54 60,00 40,00 44,91

50 3000 2 60,00 49,15 49,87 56,29 50,80 50,67 54,73 41,05 41,05

50 1000 1000 6,55 6,41 7,78 8,67 6,14 7,75 12,45 14,63 14,84

50 1000 100 6,65 6,25 8,05 7,04 5,46 4,48 10,46 9,14 8,98

50 1000 10 6,52 6,23 6,76 5,95 5,50 5,51 9,22 5,53 5,95

50 1000 2 6,39 5,67 6,32 6,78 4,90 5,19 6,98 4,85 5,01

50 500 1000 1,98 2,15 3,12 3,25 2,88 2,87 4,50 4,43 4,56

50 500 100 1,86 1,81 2,36 2,13 2,23 2,69 4,27 3,06 3,16

50 500 10 1,92 1,85 2,16 2,12 1,91 2,10 3,20 1,66 1,73

50 500 2 1,88 1,67 1,94 2,00 1,87 1,96 2,15 1,45 1,47

50 100 1000 0,06 0,07 0,18 0,18 0,20 0,24 0,41 0,14 0,14

50 100 100 0,06 0,10 0,18 0,08 0,14 0,24 0,37 0,20 0,12

50 100 10 0,04 0,10 0,15 0,11 0,11 0,11 0,29 0,06 0,07

50 100 2 0,06 0,04 0,14 0,11 0,09 0,09 0,13 0,03 0,03

50 10 1000 0,01 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01

50 10 100 0,01 0,01 0,01 0,04 0,01 0,01 0,01 0,01 0,01

50 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

50 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

50 5 1000 0,01 0,01 0,01 0,07 0,01 0,01 0,01 0,01 0,01

50 5 100 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

50 5 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

50 5 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

25 3000 1000 44,33 29,29 33,89 113,55 34,35 37,51 54,38 72,13 71,10

25 3000 100 42,82 27,70 31,87 43,78 31,34 33,92 45,95 36,59 37,46

25 3000 10 43,04 26,17 29,06 30,43 28,58 28,79 36,46 24,28 24,65

25 3000 2 42,27 25,21 27,36 29,14 26,95 26,42 32,77 21,85 21,87

25 1000 1000 4,73 4,79 6,01 5,75 3,74 4,71 9,02 8,57 8,54

25 1000 100 4,56 4,11 5,07 5,37 4,02 5,11 9,27 6,46 6,56

25 1000 10 4,84 4,14 4,70 4,01 3,84 3,96 7,05 3,42 3,37

25 1000 2 4,55 3,49 4,04 4,20 3,91 3,61 5,77 3,18 3,10

25 500 1000 1,28 1,39 2,13 2,03 1,60 2,09 3,73 2,47 2,69

25 500 100 1,29 1,32 2,09 1,96 1,99 2,46 3,93 2,85 2,46

25 500 10 1,51 1,23 2,65 2,40 1,85 2,02 2,38 1,21 0,99

25 500 2 1,17 1,25 1,29 1,25 1,35 1,25 1,47 0,96 0,90

25 100 1000 0,05 0,05 0,11 0,13 0,15 0,24 0,35 0,09 0,10

25 100 100 0,04 0,05 0,13 0,01 0,12 0,19 0,26 0,07 0,04

Page 71: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

71

25 100 10 0,05 0,03 0,13 0,07 0,07 0,12 0,22 0,05 0,03

25 100 2 0,03 0,06 0,06 0,05 0,09 0,06 0,14 0,04 0,03

25 10 1000 0,01 0,01 0,01 0,03 0,01 0,01 0,01 0,01 0,01

25 10 100 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

25 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

25 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

25 5 1000 0,01 0,01 0,01 0,15 0,01 0,01 0,01 0,01 0,01

25 5 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

25 5 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

25 5 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 3000 1000 34,66 14,98 19,87 98,93 19,78 22,14 36,77 37,12 37,09

10 3000 100 34,65 14,09 18,16 26,79 18,20 19,18 32,14 20,74 20,72

10 3000 10 34,14 13,11 15,84 15,75 15,05 15,56 22,45 11,58 12,02

10 3000 2 35,04 12,48 13,90 14,47 14,01 13,68 18,70 11,06 11,08

10 1000 1000 3,13 2,45 3,90 4,59 3,02 4,55 8,85 4,36 4,49

10 1000 100 3,04 2,26 3,43 3,25 2,44 3,02 7,11 3,64 3,36

10 1000 10 3,78 2,10 3,15 1,92 1,77 1,92 4,71 1,86 1,95

10 1000 2 3,39 1,82 2,53 2,20 2,10 1,85 4,52 1,67 1,92

10 500 1000 0,66 0,82 1,34 1,38 1,16 1,45 3,38 0,89 0,96

10 500 100 0,76 0,68 1,18 0,91 0,92 1,36 2,98 0,99 1,09

10 500 10 0,73 0,63 1,01 0,70 0,86 0,82 1,30 0,51 0,51

10 500 2 0,71 0,49 0,74 0,49 0,64 0,75 1,22 0,38 0,37

10 100 1000 0,04 0,05 0,08 0,07 0,11 0,18 0,42 0,03 0,04

10 100 100 0,02 0,09 0,12 0,01 0,16 0,17 0,31 0,04 0,03

10 100 10 0,03 0,03 0,08 0,02 0,07 0,10 0,15 0,03 0,04

10 100 2 0,04 0,01 0,07 0,06 0,05 0,06 0,10 0,01 0,02

10 10 1000 0,01 0,01 0,01 0,04 0,01 0,01 0,03 0,01 0,01

10 10 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

10 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 5 1000 0,01 0,01 0,01 0,24 0,01 0,01 0,01 0,01 0,01

10 5 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

10 5 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 5 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

Los valores son en milisegundos.

Page 72: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

72

7.3 Análisis de los resultados Una vez con los resultados en mano, se procede a un estudio de los mismos agrupando los grafos de

entrada por valores comunes y modificando alguno de ellos de forma progresiva. La intención es

observar el comportamiento de los algoritmos según la topología de los grafos de entrada.

7.3.1. Grafos completos.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2 5 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,0110 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01100 100 2 0,10 0,12 0,16 0,10 0,13 0,18 0,30 0,08 0,10500 100 2 3,39 3,48 3,76 3,86 3,51 3,81 3,56 2,96 4,121000 100 2 10,52 10,89 11,15 12,47 9,60 9,80 11,23 8,93 9,193000 100 2 90,00 100,00 100,00 110,00 100,00 100,00 100,00 80,00 80,00

Grafos completos con coste máximo mínimo. El algoritmo Corrector es el más eficiente para todos

los tamaños de grafo de máxima densidad y coste máximo de arista mínimo.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2 5 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,0110 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01100 100 10 0,12 0,14 0,16 0,19 0,15 0,20 0,27 0,13 0,12500 100 10 3,23 3,77 3,88 4,31 3,89 3,84 4,90 3,20 3,231000 100 10 10,34 11,02 11,82 11,92 10,73 10,55 13,49 9,38 9,543000 100 10 90,00 100,00 100,00 110,00 100,00 100,00 110,00 80,00 80,00

Grafos completos con coste máximo de arista pequeño. Los dos algoritmos correctores son los que

mejor se comportan temporalmente. De los dos parece que el primero es aún más eficiente.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 100 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

10 100 100 0,01 0,01 0,01 0,03 0,01 0,01 0,02 0,01 0,01

100 100 100 0,12 0,15 0,19 0,05 0,16 0,19 0,44 0,15 0,17

500 100 100 3,29 3,42 4,17 4,19 4,04 4,12 6,22 6,73 6,76

1000 100 100 10,60 11,51 12,75 13,02 11,13 12,11 15,39 15,55 15,54

3000 100 100 100,00 100,00 100,00 120,00 100,00 110,00 120,00 110,00 110,00

Grafos completos con coste máximo de arista elevado. Los algoritmos correctores de etiqueta ya

no se muestran tan favorables cuando el coste máximo aumenta. Dijkstra parece ser el más idóneo,

aunque para un cierto tamaño de grafo Dial es, con diferencia, el más eficiente. Cuando el tamaño del

grafo empieza a ser bastante importante la diferencia entre los algoritmos no es demasiado apreciable.

Page 73: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

73

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 100 1000 0,01 0,01 0,01 0,11 0,02 0,01 0,01 0,01 0,01

10 100 1000 0,01 0,01 0,01 0,05 0,01 0,01 0,03 0,01 0,01

100 100 1000 0,11 0,13 0,19 0,21 0,23 0,25 0,47 0,21 0,24

500 100 1000 3,42 3,71 4,54 5,61 4,07 4,33 7,09 8,04 8,34

1000 100 1000 11,12 11,67 13,40 14,71 12,05 12,82 18,78 23,45 22,74

3000 100 1000 100,00 100,00 110,00 200,00 110,00 110,00 130,00 220,00 230,00

Grafos completos con coste máximo de arista muy grande. Cuando el coste máximo de la arista es

muy elevado, Dijkstra se convierte en el algoritmo más idóneo de todos. Dial es totalemente

ineficiente cuando el grafo es de tamaño muy pequeño, completo y de coste máximo de arista muy

elevado.

Conclusiones

Los algoritmos correctores de etiqueta son los más idóneos para grafos completos con costes

máximos de arista muy pequeños. A medida que estos costes máximos aumentan, los algoritmos

correctores se vuelven más ineficientes a favor del algoritmo original de Dijkstra, aunque Dial destaca

en algún momento de esta transición.

7.3.2. Grafo completo pequeño. Aumentando coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2 5 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,015 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,015 100 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,015 100 1000 0,01 0,01 0,01 0,11 0,02 0,01 0,01 0,01 0,01

Dial se comporta muy ineficientemente cuando el coste máximo de arista crece, para grafos

completos muy pequeños.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2 10 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,0110 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,0110 100 100 0,01 0,01 0,01 0,03 0,01 0,01 0,02 0,01 0,0110 100 1000 0,01 0,01 0,01 0,05 0,01 0,01 0,03 0,01 0,01

Igual que antes, Dial empeora a medida que el valor máximo de arista crece. Los métodos

RadixHeap tampoco parecen ser aconsejables.

Page 74: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

74

7.3.3. Grafo completo grande. Aumentando coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

3000 100 2 90,00 100,00 100,00 110,00 100,00 100,00 100,00 80,00 80,00

3000 100 10 90,00 100,00 100,00 110,00 100,00 100,00 110,00 80,00 80,00

3000 100 100 100,00 100,00 100,00 120,00 100,00 110,00 120,00 110,00 110,00

3000 100 1000 100,00 100,00 110,00 200,00 110,00 110,00 130,00 220,00 230,00

Los algoritmos correctores son los más idóneos cuando los costes máximos de arista son pequeños

pero, a medida que éstos crecen, se vuelven menos eficientes y, al contrario, se ejecutan más

velozmente Dijkstra y dHeap.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

1000 100 2 10,52 10,89 11,15 12,47 9,60 9,80 11,23 8,93 9,19

1000 100 10 10,34 11,02 11,82 11,92 10,73 10,55 13,49 9,38 9,54

1000 100 100 10,60 11,51 12,75 13,02 11,13 12,11 15,39 15,55 15,54

1000 100 1000 11,12 11,67 13,40 14,71 12,05 12,82 18,78 23,45 22,74

Resultado similar. Correctores para costes máximo de arista pequeños y Dijkstra o dHeap para

costes máximos de arista grandes.

7.3.4. Grafo pequeño. Disminuyendo densidad de aristas.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 25 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 50 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 75 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

Para grafos de tamaño mínimo los resultados empíricos no proporcionan ningún dato relevante.

Grafo de tamaño pequeño con coste máximo de arista mínimo.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

10 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 25 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 50 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 75 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 100 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

Tampoco los datos revelan mucha información. Únicamente que parece que el comportamiento de

RadixHeap con Fibonacci es algo más ineficiente que el resto.

Page 75: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

75

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 25 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 50 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 75 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

Igual que antes, no se puede apreciar ningún dato significativo.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

10 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 25 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 50 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 75 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 100 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

Ningún dato significativo. Se recomienda aumentar el número de ejecuciones de los algoritmos

para obtener resultados más reveladores.

7.3.5. Grafo grande. Disminuyendo densidad de aristas.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

3000 10 2 35,04 12,48 13,90 14,47 14,01 13,68 18,70 11,06 11,08

3000 25 2 42,27 25,21 27,36 29,14 26,95 26,42 32,77 21,85 21,87

3000 50 2 60,00 49,15 49,87 56,29 50,80 50,67 54,73 41,05 41,05

3000 75 2 80,00 70,00 70,00 80,00 80,00 70,00 80,00 60,00 60,00

3000 100 2 90,00 100,00 100,00 110,00 100,00 100,00 100,00 80,00 80,00

Claramente, los métodos correctores son los más eficientes para cualquier densidad del grafo. El

resto de algoritmos no modifican su eficiencia considerablemente al modificar la densidad,

exceptuando Dijkstra que empeora al disminuirla.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

1000 10 2 3,39 1,82 2,53 2,20 2,10 1,85 4,52 1,67 1,92

1000 25 2 4,55 3,49 4,04 4,20 3,91 3,61 5,77 3,18 3,10

1000 50 2 6,39 5,67 6,32 6,78 4,90 5,19 6,98 4,85 5,01

1000 75 2 8,62 8,83 8,83 9,66 7,39 6,07 9,77 7,65 7,42

1000 100 2 10,52 10,89 11,15 12,47 9,60 9,80 11,23 8,93 9,19

Los métodos correctores se comportan eficientemente en casi todas las densidades de grafo. Cabe

destacar, RadixHeap con dHeaps que, para densidades medias y altas es de los más eficientes. Dijkstra

y RadixHeap con Fibonacci empeoran considerablemente la eficiencia al disminuir la densidad de

aristas.

Page 76: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

76

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

3000 10 10 34,14 13,11 15,84 15,75 15,05 15,56 22,45 11,58 12,02

3000 25 10 43,04 26,17 29,06 30,43 28,58 28,79 36,46 24,28 24,65

3000 50 10 59,37 48,86 52,05 57,83 52,84 53,54 60,00 40,00 44,91

3000 75 10 80,00 70,00 80,00 90,00 80,00 80,00 90,00 70,00 70,00

3000 100 10 90,00 100,00 100,00 110,00 100,00 100,00 110,00 80,00 80,00

El comportamiento es idéntico al observado con grafos con coste máximo de arista mínimo.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

1000 10 10 3,78 2,10 3,15 1,92 1,77 1,92 4,71 1,86 1,95

1000 25 10 4,84 4,14 4,70 4,01 3,84 3,96 7,05 3,42 3,37

1000 50 10 6,52 6,23 6,76 5,95 5,50 5,51 9,22 5,53 5,95

1000 75 10 8,97 8,84 9,96 10,15 8,91 8,57 10,58 7,45 7,43

1000 100 10 10,34 11,02 11,82 11,92 10,73 10,55 13,49 9,38 9,54

Exactamente igual que con costes máximos de aristas mínimos, aunque ahora RadixHeap original

es el más idóneo para densidades medio bajas.

7.3.6. Grafo disperso pequeño. Aumentando coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 10 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

5 10 1000 0,01 0,01 0,01 0,24 0,01 0,01 0,01 0,01 0,01

Dial y los RadixHeap empeoran la eficiencia al aumentar el coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

5 25 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 25 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

5 25 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

5 25 1000 0,01 0,01 0,01 0,15 0,01 0,01 0,01 0,01 0,01

Dial y RadixHeap empeoran la eficiencia al aumentar el coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

10 10 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 10 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 10 100 0,01 0,01 0,01 0,02 0,01 0,01 0,01 0,01 0,01

10 10 1000 0,01 0,01 0,01 0,04 0,01 0,01 0,03 0,01 0,01

Page 77: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

77

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

10 25 2 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 25 10 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 25 100 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01 0,01

10 25 1000 0,01 0,01 0,01 0,03 0,01 0,01 0,01 0,01 0,01

Al aumentar los costes máximos de arista Dial y RadixHeap proporcionan peores tiempos de

ejecución.

7.3.7. Grafo disperso grande. Aumentando coste máximo de arista.

T D C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR CORR2

3000 10 2 35,04 12,48 13,90 14,47 14,01 13,68 18,70 11,06 11,08

3000 10 10 34,14 13,11 15,84 15,75 15,05 15,56 22,45 11,58 12,02

3000 10 100 34,65 14,09 18,16 26,79 18,20 19,18 32,14 20,74 20,72

3000 10 1000 34,66 14,98 19,87 98,93 19,78 22,14 36,77 37,12 37,09

3000 25 2 42,27 25,21 27,36 29,14 26,95 26,42 32,77 21,85 21,87

3000 25 10 43,04 26,17 29,06 30,43 28,58 28,79 36,46 24,28 24,65

3000 25 100 42,82 27,70 31,87 43,78 31,34 33,92 45,95 36,59 37,46

3000 25 1000 44,33 29,29 33,89 113,55 34,35 37,51 54,38 72,13 71,10

1000 10 2 3,39 1,82 2,53 2,20 2,10 1,85 4,52 1,67 1,92

1000 10 10 3,78 2,10 3,15 1,92 1,77 1,92 4,71 1,86 1,95

1000 10 100 3,04 2,26 3,43 3,25 2,44 3,02 7,11 3,64 3,36

1000 10 1000 3,13 2,45 3,90 4,59 3,02 4,55 8,85 4,36 4,49

1000 25 2 4,55 3,49 4,04 4,20 3,91 3,61 5,77 3,18 3,10

1000 25 10 4,84 4,14 4,70 4,01 3,84 3,96 7,05 3,42 3,37

1000 25 100 4,56 4,11 5,07 5,37 4,02 5,11 9,27 6,46 6,56

1000 25 1000 4,73 4,79 6,01 5,75 3,74 4,71 9,02 8,57 8,54

Para costes máximos de arista mínimos, los Correctores son los más eficientes. Cuando estos

costes aumentan, dHeap y RadixHeap son los más rápidos y, los Correctores, todo lo contrario. Se

confirma que Dijkstra es poco eficiente cuando la densidad del grafo es pequeña.

Page 78: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

78

7.4. Primeras conclusiones

Se pueden sacar varias conclusiones después de este estudio empírico:

• Los algoritmos no se comportan la mayoría de las veces tal como se esperaba en el estudio

teórico. Algunos sí que siguen un comportamiento previsto pero la mayoría no.

• Gran sorpresa con los algoritmos correctores de etiqueta. Poco atractivos eran sus costes

temporales teóricos pero en la práctica, tal como anunciaba la bibliografía, se comportan

realmente bien, incluso superando a sus hermanos etiquetadores.

• Dificultad para distinguir una pauta de comportamiento general. Por ello, se muestra la

siguiente tabla, que intenta ofrecer un consejo sobre qué algoritmos usar en cada caso, según

el tamaño, densidad y coste máximo de arista del grafo. De todas formas, se recomienda

siempre hacer estudios empíricos cuando se utilice la librería con los grafos que se emplearán,

siempre que la topología de ellos sea similar. Por ejemplo, puede desconocerse el tamaño que

tendrán los grafos donde se buscarán los caminos mínimos pero, si se conoce que los pesos de

las aristas obtendrán valores muy elevados, puede descartarse el usar algún algoritmo (Dial en

este caso).

Page 79: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

79

7.5. Tabla consejo sobre utilización de los algoritmos según parámetros de grafo de entrada.

D T C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR DEQUE

100 3000 1000

100 3000 100

100 3000 10

100 3000 2

100 1000 1000

100 1000 100

100 1000 10

100 1000 2

100 500 1000

100 500 100

100 500 10

100 500 2

100 100 1000

100 100 100

100 100 10

100 100 2

100 10 1000

100 10 100

100 10 10

100 10 2

100 5 1000

100 5 100

100 5 10

100 5 2

75 3000 1000

75 3000 100

75 3000 10

75 3000 2

75 1000 1000

75 1000 100

75 1000 10

75 1000 2

75 500 1000

75 500 100

75 500 10

75 500 2

75 100 1000

75 100 100

75 100 10

75 100 2

Page 80: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

80

D T C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR DEQUE

75 10 1000

75 10 100

75 10 10

75 10 2

75 5 1000

75 5 100

75 5 10

75 5 2

50 3000 1000

50 3000 100

50 3000 10

50 3000 2

50 1000 1000

50 1000 100

50 1000 10

50 1000 2

50 500 1000

50 500 100

50 500 10

50 500 2

50 100 1000

50 100 100

50 100 10

50 100 2

50 10 1000

50 10 100

50 10 10

50 10 2

50 5 1000

50 5 100

50 5 10

50 5 2

25 3000 1000

25 3000 100

25 3000 10

25 3000 2

25 1000 1000

25 1000 100

25 1000 10

25 1000 2

25 500 1000

25 500 100

25 500 10

25 500 2

Page 81: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

81

D T C DIJK DHEAP FIBO DIAL RHEAP RDHEA RDFIB CORR DEQUE

25 100 1000

25 100 100

25 100 10

25 100 2

25 10 1000

25 10 100

25 10 10

25 10 2

25 5 1000

25 5 100

25 5 10

25 5 2

10 3000 1000

10 3000 100

10 3000 10

10 3000 2

10 1000 1000

10 1000 100

10 1000 10

10 1000 2

10 500 1000

10 500 100

10 500 10

10 500 2

10 100 1000

10 100 100

10 100 10

10 100 2

10 10 1000

10 10 100

10 10 10

10 10 2

10 5 1000

10 5 100

10 5 10

10 5 2

Page 82: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

82

7. Aplicación.

7.1. Introducción

A continuación se expondrá la aplicación de la librería desarrollada en un problema real, un

problema que, lógicamente, usa de algoritmos de búsqueda de caminos mínimos y en el que se desea

minimizar los tiempos de espera.

7.2 Descripción del problema.

El problema trata sobre la protección de datos. Este problema surge cuando se quiere proteger

información de individuos que puede ser obtenida de tablas estadísticas.

Un método que soluciona este problema es el llamado de “eliminación de celda complementaria”.

Existen varias técnicas para resolverlo y una de las más ventajosas en la teoría y en la práctica se basa

en optimización linear sobre redes o grafos.

Aunque aquí no se detallará el procedimiento de este método sí que hay que señalar que en la

ejecución del mismo se resuelven gran cantidad de subproblemas de caminos mínimos en grafos,

previa creación de los mismos.

7.3 Aplicación real

Se dispone de una aplicación real (y de su código fuente en C) que resuelve este problema.

El programa dispone de tres métodos para resolver los subproblemas de optimización lineal sobre

redes siendo uno de ellos una implementación básica del algoritmo de Dijkstra basado en d-heaps.

Se pretende sustituir este módulo por la librería desarrollada. Como los grafos que aparecen en los

subproblemas tienen todos una misma topología es fácil pensar a priori que se podrá detectar cuál de

todas las versiones de la librería es la más idónea para la aplicación.

Page 83: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

83

7.4 Análisis de la aplicación

El código de la aplicación no es demasiado intuitivo pues en su mayoría está desarrollado a muy

bajo nivel y, además, hay que conocer la teoría sobre este tipo de problemas. De todas formas, con la

ayuda de los comentarios, los nombres de las funciones y la organización en diversos ficheros se

podrán encontrar las partes que se dedican a la implementación del algoritmo de Dijkstra.

El algoritmo de Dijkstra lo implementan los ficheros dijkstra.h y dijkstra.c.

En dijkstra.h está la declaración de la estructura del problema:

/* Dijkstra data structure */ typedef EXPDLL_DIJKSTRA struct{ int nar; /* number of arcs */ int nnu; /* number of nodes */ int *mnk; /* origin nodes of arcs */ int *mnl; /* destination nodes of arcs */ int *mad; /* arc numbers comming out of each node */ int *ipa; /* position of 1st element of "mad" for each node */ int *mda; /* arc numbers arriving in each node */ int *iap; /* position of 1st element of "mda" for each node */ double *car; /* costs of arcs */ int mor; /* origin node in shortest path */ int mds; /* destination node in shortest path */ int lsp; /* length of shortest path (number of arcs in it) */ int *ksp; /* arcs in shortest path */ double spc; /* shortest path cost */ double infinity; /* infinity value: any cost greater or equal than this value is considered infinity */ } DSP;

En ella se puede ver que se guarda tanto los parámetros de entrada (grafo de entrada, nodo origen,

nodo destino…) como los parámetros de salida o solución (arcos del camino mínimo, longitud del

camino mínimo).

El resto de funciones implementan los subprocedimientos a bajo nivel del algoritmo basado en d-

heaps, a excepción de la función dijdmd que es la que implementa el algoritmo propiamente dicho.

En este punto está ya más claro los pasos que hará el programa y que se deberán sustituir por los

propios de la librería aquí desarrollada:

1. Creación del grafo. 2. Rellenar los parámetros de entrada de la estructura DSP. 3. Ejecutar el algoritmo de Dijkstra. 4. Extraer los parámetros solución de la estructura DSP.

Page 84: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

84

El único sitio donde se llama a la función dijdmd es en la otra función llamada

csp_SolveNF_Dijkstra(DSP *Dsp):

int csp_SolveNF_Dijkstra(DSP *Dsp) /****************************************************************************/ // Solves the network flow (shorthest path) problem // // Input parameters: // // DSP *Dsp : Dijkstra structure, already updated // // Returns: // 0: if everything was fine // -5: if error in solving shortest path problem /****************************************************************************/ { /* Declarations */ /* Calling Dijkstra */ if (dijmd(Dsp)) return (-5); return(0); }

En el mismo fichero donde está la función csp_SolveNF_Dijkstra se encuentra otra función más

llamada csp_IniDijkstra(TABLE2D *tab, NFPro *Ncp, DSP *Dsp), cuya documentación dice lo

siguiente:

/****************************************************************************/ // Loads Dsp->mnk, mnl, car of permanent cells and computes the adjacencies // for the Dijkstra algorithm // // Input parameters: // // TABLE2D *tab : 2D table // NFPro *Ncp : NF problem // DSP *Dsp : Dijkstra structure /****************************************************************************/

Esta función rellena los siguientes campos de la estructura DSP:

nar, nnu, infinity, mnk, mnl, car, ipa, iap, mad, mda. Es decir, crea el grafo donde buscar el

camino mínimo. Los datos los obtiene a partir del parámetro de entrada *Ncp, que es el problema de

protección de datos, y *tab, la tabla a proteger. De todas formas, no debería ser necesario conocer qué

representan estos dos parámetros o estructuras, únicamente se deberá crear un equivalente a la

estructura DSP con la librería de caminos mínimos.

Page 85: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

85

Esta función de inicialización de la estructura DSP es llamada en otra llamada

csp_heur2D_Cox(TABLE2D *tab), sin que tenga más código referente al algoritmo de Dijkstra.

Hasta este punto se tienen detectados los siguientes puntos:

• Creación del grafo. • Ejecutar el algoritmo de Dijkstra.

Para conocer el punto dónde se introduce el nodo origen y el nodo destino del grafo origen donde

se quiere buscar el camino mínimo simplemente habrá que encontrar qué código rellena los valores

mor y mds de la estructura DSP.

Esto se hace en una función llamada csp_set_targetNF(TABLE2D *tab,NFPro *Ncp, DSP *Dsp,

int target_i,int_target_j), a partir de valores del parámetro de entrada *Ncp.

Finalmente, habrá que detectar la parte de código que recupera la solución después de haber

ejecutado el algoritmo de Dijkstra.

Los atributos de la estructura DSP referentes a la solución son lsp y *ksp.

Éstos se recuperan en la función denominada csp_GetCycleNF.

Por tanto, se ha detectado la parte de código que hace referencia a la inicialización, ejecución y

recuperación de resultados del algoritmo de Dijkstra. El siguiente paso será estudiar cómo realizar la

sustitución de este código por el proporcionado en la librería de caminos mínimos.

Page 86: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

86

7.5. Cómo sustituir el código

Una primera aproximación a la hora de proceder a la sustitución sería pensar en reemplazar la

estructura DSP por otra propia de la librería.

Todos los atributos relacionados con el grafo de entrada podrían ser fácilmente eliminados y

sustituidos por la clase grafo. Los atributos de nodo origen y destino serían los orig y dest de la clase

CamMin y la solución del problema, padres, hijos y dist.

Pero esto significaría el tener que modificar muchas partes de código, con lo que el cambio no

sería tan transparente como uno desearía. Lo ideal sería sólo modificar la función que ejecuta el

algoritmo y no tocar la estructura DSP. Así, las inicializaciones y consulta de resultados

permanecerían intocables.

A la función dijmd que ejecuta el algoritmo de Dijkstra se le pasa como parámetro la estructura

DSP. En esta estructura tenemos todos los datos necesarios para poder ejecutar el algoritmo de

caminos mínimos. A partir de estos datos se crearía el grafo y se buscaría el camino mínimo en él dado

un nodo origen y un destino. Finalmente, se rellenarían los campos de resultado en DSP.

Esta alternativa tiene la ventaja expuesta de concentrar los cambios en un único punto pero la

desventaja de la ineficiencia: en este caso, por ejemplo, el grafo se crearía dos veces.

La mejor alternativa, por tanto, es una mezcla de las dos. Se intentará que los cambios se realicen

en el mínimo número de ficheros pero sin sacrificar eficiencia.

Page 87: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

87

7.6 Sustitución del código

7.6.1. Creación del grafo.

Originalmente, el grafo se crea en la función csp_IniDijkstra, rellenando los atributos referentes al

mismo en la estructura DSP.

Como en la librería de caminos mínimos el grafo es en sí mismo una clase propia, se introducirá

en la estructura DSP un campo que contenga el grafo y, en vez de introducir valores a los campos que

hacían referencia al grafo, se llamarán a las funciones o métodos de la clase.

Código inicial:

void csp_IniDijkstra(TABLE2D *tab, NFPro *Ncp, DSP *Dsp) { int i, j, M, N; M = get_nrows2D(tab); N = get_ncolumns2D(tab); Dsp->nar = Ncp->n; Dsp->nnu = Ncp->m; Dsp->infinity= DBL_MAX/((double)Dsp->nnu); for (i=0; i< Ncp->n; i++){ Dsp->mnk[ i] = Ncp->mnk[ i]; Dsp->mnl[ i] = Ncp->mnl[ i]; if (Ncp->max_flx[ i] == 0.0 ) Dsp->car[ i] = Dsp->infinity; } int imad= 0; int iarc= 0; for(i=0;i<M-1;i++) { Dsp->ipa[i]= imad; Dsp->iap[i]= imad; for(j=0;j<N-1;j++) { Dsp->mad[imad]= iarc; Dsp->mda[imad]= iarc+1; imad++; iarc += 2; } Dsp->mad[imad]= iarc+1; Dsp->mda[imad]= iarc; imad++; iarc += 2; } Dsp->ipa[i]= imad; Dsp->iap[i]= imad; for(j=0;j<N-1;j++) { Dsp->mad[imad]= iarc+1; Dsp->mda[imad]= iarc; imad++; iarc += 2; } Dsp->mad[imad]= iarc; Dsp->mda[imad]= iarc+1; imad++;

iarc= 0; int inc= 2*N; for(j=0;j<N-1;j++) { Dsp->ipa[M+j]= imad; Dsp->iap[M+j]= imad; for(i=0;i<M-1;i++) { Dsp->mad[imad]= i*inc+iarc+1; Dsp->mda[imad]= i*inc+iarc; imad++; } Dsp->mad[imad]= i*inc+iarc; Dsp->mda[imad]= i*inc+iarc+1; imad++; iarc += 2; } j= N-1; Dsp->ipa[M+j]= imad; Dsp->iap[M+j]= imad; for(i=0;i<M-1;i++) { Dsp->mad[imad]= i*inc+iarc; Dsp->mda[imad]= i*inc+iarc+1; imad++; } Dsp->mad[imad]= (M-1)*inc+iarc+1; Dsp->mda[imad]= (M-1)*inc+iarc; imad++; Dsp->ipa[M+N]= imad; Dsp->iap[M+N]= imad; }

Page 88: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

88

Código sustituido: void csp_IniDijkstra(TABLE2D *tab, NFPro *Ncp, DSP *Dsp) { int i, j, M, N; M = get_nrows2D(tab); N = get_ncolumns2D(tab); Dsp->nar = Ncp->n; Dsp->nnu = Ncp->m; Dsp->g = new grafo(Dsp->nnu, Dsp->nar); for (i = 0; i < M - 1; i++) { for (j = M; j <= M + N - 2; j++) { Dsp->g->creaArista(i+1, j+1, BIGINT); Dsp->g->creaArista(j+1, i+1, BIGINT); } Dsp->g->creaArista(j+1, i+1, BIGINT); Dsp->g->creaArista(i+1, j+1, BIGINT); } for (i = M; i <= M + N - 2; i++) { Dsp->g->creaArista(i+1, M, BIGINT); Dsp->g->creaArista(M, i+1, BIGINT); } Dsp->g->creaArista(M, M + N, BIGINT); Dsp->g->creaArista(M + N, M, BIGINT); }

En otra parte del código se insertan los pesos de las aristas. El cambio simplemente será hacerlo esta

vez con el método ponPeso de la clase grafo.

7.6.2. Nodos origen y destino del problema

Los nodos origen y destino se introducen aquí:

/* set origin and destination node of shortest path problem */ Dsp->mor = Ncp->mnl[ targetXp]; Dsp->mds = Ncp->mnk[ targetXp];

No se realizará ningún cambio. La función de ejecución del algoritmo ya se encargará de

tomar los valores. Nótese que entonces se estarán doblando esfuerzos para conseguir estos

parámetros pero, al ser simples asignaciones de valores enteros, la pérdida de eficiencia será

despreciable, sobretodo si se compara con la ventaja de no tener que hacer ningún cambio en

el código en esta función.

Page 89: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

89

7.6.3. Ejecución del algoritmo

Aquí es donde se realizará el cambio más importante. La función dedicada al algoritmo de

Dijkstra es dijmd, a partir de la estructura DSP rellenada. La función implementa el algoritmo

a muy bajo nivel. A continuación se muestra un pequeño fragmento de su código:

while (meq < nnu && mqe < nnu) { /* CAMI DIRECTE */ j12[mll] = j12[mll] - 2; if (j12[mll] == 0) { /* l'arbre directe i l'arbre invers ja s'han trobat */ aux = etq[mll] + eqt[mll]; /* "aux": fita superior de "spc" */ /* cerca del cami minim */ mar=-1; /* no.d'arc entre "imd" i "idm" que resulta en spc<aux */ /* "imd": es aqui un nus etiquetat de l'arbre directe */ /* "idm": es aqui un nus etiquetat de l'arbre invers */ for (i = 0; i < nnu; i++) { if (j12[i] == 1) { ini=ipa[i]; ifi=ipa[i+1]-1; if (ini <= ifi) { for (j = ini; j <= ifi; j++) { iax = mad[j]; ibx = mnl[iax]; if (j12[ibx] == 2 && etq[i]+car[iax]+eqt[ibx] < aux) { mar = iax; aux = etq[i] + car[mar] + eqt[ibx]; imd = i; idm = ibx; } } }

El código se sustituirá casi completamente por llamadas a las clases de la librería. En la

aplicación original el algoritmo escogido era Dijkstra con d-heaps. Aquí se permitirá al

usuario escoger qué versión de algoritmo de camino mínimo se desea utilizar. Por tanto, habrá

que modificar también el código principal para permitirlo.

Page 90: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

90

Un ejemplo del código sería:

int dijmd (DSP *ddsps) { extern int version; int ret_stat= 0; int* p = new int[ddsps->g->n+1]; int* d = new int[ddsps->g->n+1]; int k; int num_ar = 0; int hijo = ddsps->mds+1; ddsps->lsp = 0; ddsps->spc = d[ddsps->mds+1]; if (version == 1) { Dijkstra<DLista> Dsp(*ddsps->g,ddsps->mor+1,ddsps->mds+1,p,d); Dsp.ejecuta(); p = Dsp.damePadres(); d = Dsp.dameDistancias(); for (k = p[ddsps->mds+1]; k != ddsps->mor+1; k = p[k]) { ddsps->ksp[num_ar++] = ddsps->g->dimeArista(k, hijo)-1; ddsps->lsp++; hijo = k; } } …

Y así con las sucesivas versiones. Se incluye ya la inserción de las soluciones en la estructura DSP.

7.7. Resultados

Una vez compilado y depurado el programa con las modificaciones se comprobará empíricamente

si el cambio ha mejorado la eficiencia temporal.

Los grafos generados son de tamaño elevado y de una densidad de aristas medio-alta.

Observando la tabla resumen de la eficiencia de los algoritmos según la topología de los grafos, es

difícil decidir cuál de ellos será el más idóneo en esta aplicación.

De todas formas, al haber insertado la posibilidad de escoger el método en los parámetros de

entrada, no llevará mucho tiempo averiguar cuál de ellos emplear después de unas cuantas pruebas

empíricas.

Para ello se crearán varios juegos de prueba. Se ejecutarán con la aplicación original y luego con

la nueva, comparando los tiempos de ejecución. Para esto se contará con la ayuda de un generador de

Page 91: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

91

tablas para la aplicación y un script que genera diversas instancias de tablas y ejecuta el programa para

resolverlas.

El algoritmo que proporciona la mayor eficiencia temporal es el de Dijkstra con d-Heaps,

alrededor de un 20% más rápido que el original. Por ello, se modifica el código para que sea el

algoritmo empleado por defecto, aunque es posible escoger otro en la propia llamada a la aplicación

mediante un parámetro de entrada.

La razón más posible de que ninguno de los otros algoritmos con estructuras eficientes sea más

ventajoso en esta aplicación puede ser debido a los pesos máximos de las aristas, que toman valores

infinitos durante la ejecución del programa.

Intentar arreglar este problema seguramente conllevaría modificar el código de la aplicación

original en gran medida, algo que sale del objetivo de este proyecto, aunque también es cierto que los

algoritmos podrían tener en consideración los pesos con valores infinitos. Estas modificaciones

podrían sobrecargar el código y hacerlo más ineficiente. De todas formas es un caso puntual como

pueden aparecer más cuando se aplique la librería a otros códigos. El diseño de la misma permitirá al

futuro usuario poder realizar modificaciones sencillas para adecuarla a su propio gusto o necesidades.

Page 92: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

92

8. Apéndice

8.1. Estructuras de datos

A continuación se puede encontrar la información relativa a las llamadas estructuras de

datos eficientes empleadas en los algoritmos de caminos mínimos.

8.2. d-Heaps

Un heap (o cola con prioridades) es una estructura de datos para guardar y manipular

eficientemente una colección H de elementos (u objetos) cuando cada uno de estos elementos (e ∈ H)

tiene asociado un número real, denominado clave(i).

Las operaciones que se requieren poder hacer a los elementos en el heap H son:

• crear(H). Crea un heap H vacío.

• insertar(e, H). Inserta un elemento e en el heap.

• busca-mínimo(e, H). Busca un elemento e con la clave mínima en el heap.

• borra-mínimo(e, H). Borra el elemento e con la clave mínima del heap.

• borra(e, H). Borra un elemento arbritario e del heap.

• decrementa-clave(e, valor, H). Decrementa la clave del elemento e a un valor más

pequeño, indicado por valor.

• incrementa-clave(e, valor, H). Incrementa la clave del elemento e a un valor más

grande, indicado por valor.

8.2.1. Definición y propiedades de un d-Heap

En un d-Heap, se guardan los elementos del heap como un árbol enraizado cuyos arcos representan

una relación de padre-hijo. El árbol se almacena usando índices de predecesores y conjuntos de

sucesores:

pred(e): el padre del elemento e en el d-heap. La raíz no tiene predecesor, así que será igual a 0.

SUC(e): el conjunto de hijos del elemento e en el d-heap.

Page 93: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

93

En un d-heap se define la profundidad de un elemento e como el número de arcos en el camino

único que va desde la raíz hasta e.

Cada elemento en un d-heap tiene como mucho d sucesores, asumiendo que están ordenados de

izquierda a derecha. Estos sucesores se llaman hermanos entre sí. El d-heap siempre satisface la

siguiente propiedad que se mantiene invariable: Los elementos se han añadido al heap en orden

creciente de su profundidad y, para los de misma profundidad, de izquierda a derecha. Esta propiedad

se denomina propiedad de contigüidad, que implica los siguiente resultados:

(a) Como mucho dk elementos tienen profundidad k.

(b) Como mucho (dk + 1 – 1)/(d – 1) nodos tienen profundidad entre 0 y k.

(c) La profundidad de un d-heap que contiene n elementos es de como mucho,

⎣logdn⎦.

8.2.2. Almacenamiento de un d-Heap.

La estructura de un d-heap permite guardarlo en un vector y manipularlo de forma bastante

eficiente. Los elementos se ordenan por su profundidad de forma creciente, y de izquierda a derecha

en caso de que ésta sea la misma. Después, se insertan en orden en un vector. Además, se tiene un

vector que guarda las posición de cada elemento. Otro parámetro es último, que especifica el número

de nodos guardados en el vector.

Esta forma de almacenar el d-heap puede parecer a simple vista poco adecuada para manipularlo

posteriormente, pero tiene una curiosa propiedad que permitirá acceder a los predecesores y sucesores

de cualquier nodo de forma eficiente:

(a) El padre de un elemento en la posición i se encuentra en la posición ⎣(i – 1)/d⎦.

(b) Los hijos de un elemento en la posición i se encuentran entre el siguiente

rango de posiciones: [id – d + 2, ..., id + 1].

Así, esta propiedad implica que no se deberá mantener el índice del padre y el conjunto de hijos

para cada elemento. Se podrá calcular cuando sea necesario hacerlo durante la ejecución de un

Page 94: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

94

algoritmo. Por lo tanto, se ignorará el tiempo requerido para actualizar estas estructuras cuando el d-

heap cambie.

8.2.3. Propiedad Orden del Heap

Un heap siempre satisface el siguiente invariante, llamado propiedad orden del heap:

Invariante 1:

La clave de un elemento e en el heap es menor o igual que la clave de cada uno de sus sucesores.

Es decir que, para cada elemento e, clave(e) ≤ clave(j) para cada j ∈ SUC(e).

Nótese que el invariante se puede violar temporalmente cada vez que se efectúa una operación en el

heap, pero siempre se cumplirá al final de ésta.

Como resultado de esta propiedad, se puede afirmar lo siguiente:

La raíz del d-heap siempre tiene la clave más pequeña.

8.2.4. Intercambio En la estructura de datos d-heap, se puede reducir toda operación en una secuencia de operaciones

básicas, llamadas intercambiar(i, j). Esta operación intercambia los elementos i y j. En términos del

vector donde está guardada la estructura, el elemento i se guarda donde estaba el j, y viceversa.

8.2.5. Recuperar el orden del heap

Normalmente, durante la ejecución de un algoritmo, frecuentemente se modificará el valor de

alguna clave y, por tanto, durante un tiempo se estará violando el orden del heap.

Por ejemplo, se decrementa la clave de un elemento i. Sea j = pred(i). Si después del cambio en el

valor de clave(i), clave(j) ≤ clave(i), el heap sigue manteniendo el orden y no hay que hacer nada más.

Sin embargo, si clave(j) > clave(i), se necesita recuperar la ordenación. El siguiente proceso sube(i)

cumple esta tarea.

acción sube(i) mientras i no sea la raíz y clave(i) < clave(pred(i)) hacer intercambia(i, pred(i)); fmientras facción

Page 95: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

95

Argumentos inductivos muestran que al acabar dicho proceso, el heap satisface la propiedad de

orden. sube(i) requiere un tiempo O(logdn) porque cada ejecución del mientras decrementa la

profundidad del elemento i en una unidad y, como se ha dicho anteriormente, la profundidad máxima

de un elemento es O(logdn).

Si lo que se hace es incrementar la clave de algún elemento i, después puede ocurrir que clave(i) ≤

clave(j) para todo j ∈ SUCC(i) y, por tanto, el heap sigue manteniendo la propiedad de orden. De otra

manera, hay que modificar el heap para que la cumpla. El proceso baja(i) hace esta tarea.

acción baja(i) mientras i no sea hoja y clave(i) > clave(hijomin(i)) hacer intercambia(i, hijomin(i)); fmientras facción

Siendo hijomin(i) el elemento con clave más pequeña de los hijos de i.

De nuevo, por inducción, se demuestra que al final de la acción el heap satisface la propiedad de

orden. El proceso requiere un tiempo O(dlogdn) porque cada ejecución del mientras incrementa la

profundidad del elemento i en una unidad y además requiere un tiempo O(d) para calcular hijomin(i).

8.2.6. Operaciones del Heap

busca-mínimo(i, H). La raíz del heap es el elemento con clave más pequeña y se encuentra en la

primera posición del vector donde se guarda éste. Por lo tanto, es una operación que requiere tiempo

constante O(1).

inserta(i, H). Se incrementa último en 1 y se guarda el nuevo elemento i en la última posición del

vector. Después, se ejecuta la acción sube(i) para recuperar el orden del heap. Claramente, esta

operación requiere un tiempo equivalente al de sube(i): O(logdn).

decrementa-clave(i, valor, H). Se decrementa el valor de la clave del elemento i y se ejecuta el

proceso sube(i) para recuperar el orden del heap. Por tanto, el tiempo de ejecución es O(logdn).

incrementa-clave(i, valor, H). Se incrementa el valor de la clave del elemento i y se ejecuta el

proceso baja(i) para recuperar el orden del heap. Por tanto, el tiempo de ejecución es O(dlogdn).

borra-mínimo(i, H). La raíz del heap es exactamente el elemento con clave mínima. Sea j el

elemento guardado en la última posición del vector. Primero se efectúa intercambia(i, j) y luego se

Page 96: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

96

decrementa ultimo en 1. Después, se ejecuta baja(j) hasta recuperar el orden del heap. Esta operación

requiere un tiempo O(dlogdn).

borra(i, H). Se intercambia el elemento a borrar con el último del heap. Luego se ejecuta el proceso

baja o sube según sea el caso para recuperar el orden del heap. El tiempo necesario es O(dlogdn).

8.3. Heaps de Fibonacci

El heap de Fibonnaci es una estructura de datos que permite hacer operaciones de heaps de forma

más eficiente que los d-heaps. Esta estructura realiza las operaciones insertar, buscar-mínimo y

decrementa-clave en tiempo O(log n).

8.3.1. Propiedades

El nombre de Fibonacci es debido a que las demostraciones de sus tiempos de cálculo usan

propiedades de los conocidos números de Fibonnaci.

Los números de Fibonnaci se definen recursivamente como F(1) = 1, F(2) = 1, y F(k) = F(k – 1) +

F(k – 2), para todo k ≥ 3. Además, cumplen las siguientes propiedades:

(a) Para k ≥ 3, F(k) ≥ 2(k – 1)/2.

(b) F(k) = 1 + F(1) + F(2) + ... + F(k – 2).

(c) Dada una serie de números G(·) que satisface que G(1) = 1, G(2) = 1, y G(k) ≥

1 + G(1) + G(2) + ... + G(k – 2) para todo k ≥ 3, G(k) ≥ F(k).

8.3.2. Definición y almacenamiento de un Heap de Fibonacci

Como se dijo anteriormente, un heap guarda un conjunto de elementos, cada uno con una clave de

valor real. Un heap de Fibonacci es una colección de árboles dirigidos: cada nodo i del árbol

representa un elemento i y cada arco (i, j) representa una relación padre-hijo: el nodo j es el predecesor

del nodo i.

Para representar un heap de Fibonacci numéricamente y poder manipularlo cómodamente, se

necesitan las siguientes estructuras:

pred(i): el predecesor del nodo i en el heap de Fibonacci.

Page 97: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

97

Un nodo que no tenga padre es un nodo raíz y su predecesor será 0. Esto permite a simple vista ver

si un nodo es raíz o no, mirando al índice de predecesores.

SUC(i): el conjunto de hijos del nodo i. Este conjunto se guarda como una lista doblemente

encadenada.

rango(i): el número de sucesores del nodo i (o equivalentemente, |SUC(i)|).

clavemin: el nodo con clave mínima.

8.3.3. Uniendo y cortando

Como se vio en los d-heaps, aquí todas las operaciones también se reducen a otras más básicas, en

este caso dos: une(i, j), corta(i). La operación une(i, j) se aplica a dos nodos raíz distintos i y j de

igual rango; esto hace que se unan los dos árboles correspondientes para formar uno único. La

operación corta(i) separa el nodo i de su padre y lo convierte, por tanto, en la raíz de un árbol.

une(i, j). Si clave(j) ≤ clave(i), entonces se añade el arco (i, j) al heap de Fibonacci (haciendo que

el nodo i sea el predecesor del nodo j). Si clave(j) > clave(i), entonces se añade el arco(j, i) al heap.

corta(i). Se borra el arco (i, pred(i)) del heap (convirtiendo al nodo i en un nodo raíz).

Hay que notar que la operación une(i, j) incrementa el rango de uno de los dos nodos en 1 unidad.

Además, las dos cambian el predecesor, el conjunto de sucesores y la información de rango de, como

mucho, dos nodos; en consecuencia, se pueden ejecutar en tiempo O(1).

Mientras se manipula el heap de Fibonacci, se produce una secuencia de uniones y cortes. Hay una

estrecha relación entre el número de uniones y cortes. Para observarla, consideraremos una función f

definida como el número de árboles. Cada operación une decrementa f en 1 y cada operación corta la

incrementa en 1. Lo máximo que puede crecer f está acotado por su valor inicial (que es n) más el

incremento total de f.

Por tanto, el número de operaciones une es como mucho n más el número de cortes.

Page 98: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

98

8.3.4. Invariantes

El heap de Fibonacci mantiene un conjunto de árboles que cambia dinámicamente mientras se

efectúan las distintas operaciones de unir y cortar. Estos árboles satisfacen ciertos invariantes que son

esenciales para derivar las acotaciones de los costes temporales de las operaciones. Los nodos en el

heap siempre satisfacen la propiedad de orden, que dice que la clave de un nodo es menor o igual a las

claves de sus sucesores.

Además, se satisfacen los siguientes invariantes:

Cada nodo no raíz ha perdido como mucho un sucesor después de haberse convertido en un nodo

no raíz.

No hay dos nodos raíz que tengan el mismo rango.

Como siempre, aunque se puedan violar los invariantes en pasos intermedios en algunas

operaciones, el heap siempre los satisfacerá en la conclusión de éstas. Otra importante consecuencia de

los dos últimos invariantes es un lema que dice:

El rango máximo posible de cualquier nodo es 2logn + 1.

La siguiente propiedad surge directamente del Invariante 3 y del Lema X:

Un heap de Fibonacci tiene como mucho 1 + 2logn árboles.

8.3.5. Restaurando el Invariante 2

Para restaurar el invariante 2, se mantiene un índice adicional llamado perdidos(i) que representa,

para cada nodo i, el número de sucesores que el nodo ha perdido después de convertirse en un nodo no

raíz. Para un nodo raíz r, perdidos(r) = 0.

Supongamos que, mientras se manipula el heap de Fibonacci, se efectúa la operación corta(i). Sea j

= pred(i). En esta operación el nodo j pierde un sucesor (el nodo i). Si el nodo j no es raíz, se

incrementará en 1 unidad perdidos(j). Si perdidos(j) es 2, el invariante 2 requiere que j se convierta en

nodo raíz. En ese caso, se ejecuta corta(j) y j pasa a ser nodo raíz. Sea k = pred(j). Este corte

Page 99: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

99

incrementa perdidos(k) en 1. Si k es un nodo no raíz y perdidos(k) = 2 se debe convertir en raíz

también, y así sucesivamente. Por lo tanto, un corte puede ocasionar numerosos cortes en efecto

cascada: Se siguen efectuando estos cortes hasta que se llegue a un nodo que no haya perdido ningún

sucesor o sea raíz. A estos cortes adicionales se les llama cortes en cascada y a la secuencia completa

de pasos que siguen a un corte como multicascada. Se puede demostrar que el número total de cortes

en cascada es inferior o igual al número total de cortes efectuado hasta el momento en el heap.

8.3.6. Restaurando el Invariante 3

El invariante 3 dice que no hay dos nodos raíz con el mismo rango. Para mantener esta propiedad,

se necesita de un índice para cada posible rango k = 1, ..., K = 2 log n + 1.

balde(k). Si el heap no contienen ningún nodo raíz con rango igual a k, entonces balde(k) = 0; y si

algún nodo raíz i tiene rango igual a k, entonces balde(k) = i.

Supongamos que mientras se manipula un heap de Fibonacci, se crea un nodo raíz j de rango k pero

el heap ya tenía otro nodo raíz i con del mismo rango. Entonces se repite el siguiente proceso hasta

restaurar el tercer invariante:

Se ejecuta la operación enlaza(i, j), que junta los dos árboles en uno nuevo de rango k + 1.

Supongamos que ese nodo l es la raíz del nuevo árbol. Entonces mirando el valor de balde (k + 1), se

comprueba si el heap ya tiene un nodo raíz de rango k + 1. Si no, no hay que hacer nada más. En caso

contrario, se efectúa otra operación de enlace para crear otro árbol de rango k + 2 con la consiguiente

comprobación de si ya existía un nodo raíz de rango k + 2. Se repite el proceso hasta que el invariante

nº 3 quede restaurado. Esta secuencia de enlaces se denomina multienlaces. Se puede demostrar que el

número total de multienlaces es proporcional al número de enlaces efectuados hasta el momento en el

heap de Fibonacci.

Page 100: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

100

8.3.7. Operaciones

A continuación, se muestra cómo se ejecutan varias de las operaciones del heap y se indica el

tiempo que emplean.

busca-mínimo(i, H). Simplemente devuelve i = clavemín, pues ésta contiene el nodo con la clave más

pequeña. El tiempo de ejecución es de O(1).

inserta(i, H). Se crea un nuevo nodo raíz i y se añade a H. Después de esto, el heap tal viole el

invariante 3, por lo que posiblemente sean necesarios multienlaces posteriores para restaurarlo. El

tiempo de ejecución es de O(1).

decrementa-clave(i, valor, H). Primero se decrementa la clave del nodo i al nuevo valor. Después,

cada nodo del subárbol que cuelga del nodo i sigue satisfaciendo la propiedad de orden del heap; el

predecesor del nodo i puede, sin embargo, violarla. Sea j = pred(i). Si clave(j) ≤ valor, ya está. Si no,

se ejecuta un corte, corte(i), para convertir a i en nodo raíz, y se actualiza clavemín. Después de este

corte, el heap tal vez viole el segundo invariante, así que se necesitarán cortes en multicascada para

restaurarlo. Estos cortes en multicascada generan nuevos árboles cuyas raíces se guardan en una lista.

Entonces, uno por uno, se van borrando de la lista, añadiéndose al conjunto previo de raíces, y

efectuando multienlaces para satisfacer el tercer invariante. El proceso finaliza cuando la lista se quede

vacía. El tiempo de ejecución es de O(1).

borrar-mínimo(i, H). Primero se pone i = clavemín. Entonces, uno por uno, se recorre la lista de

sucesores de i, se cortan y se actualiza clavemín. Se aplica multienlace después de cada corte. Cuando

se han cortado todos, se busca en todos los nodos raíz (guardados en balde(k), para k = 0, 1, ..., 2 log n

+ 1), identificando el nodo raíz h con la clave mínima, y poniendo clavemín = h. Como | SUC(i) | ≤ 2

log n + 1, esta operación hace O(log n) cortes, seguido de un número de cortes en cascada y enlaces.

Después, se busca entre los O(log n) nodos raíz para encontrar el que tenga la clave mínima. El tiempo

de ejecución es de O(log n).

Page 101: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

101

9. Balances y conclusiones

9.1. Comparación tiempo estimado con tiempo real

El tiempo empleado para la ejecución de este proyecto no ha diferido demasiado del previsto en

un principio.

La diferencia más destacable es el estudio de los algoritmos a insertar en la librería. Al principio

se disponía de una bibliografía básica de donde se suponía que iban a salir todos los algoritmos pero

poco a poco se fue encontrando multitud de documentos, sobretodo en revistas científicas, con

propuestas nuevas de algoritmos y esto demoró en 1 mes aproximadamente el estudio de los mismos.

En lo que sí difieren la planificación del trabajo y el resultado final es en la dedicación de horas

por semana al proyecto. Debido a asuntos personales de el que escribe estas líneas hubo una

temporada en la que prácticamente no se dedicó ninguna hora a la realización del proyecto, con la

consecuencia de una demora importante en la presentación del mismo (aproximadamente 1 año).

Cabe decir que la librería y la aplicación de la misma en la aplicación de protección de datos ya

estaba lista a finales de 2003 y se entregó para su uso pero la redacción de los últimos capítulos de la

memoria así como la preparación de la defensa del proyecto no se retomaron hasta finales de

Noviembre de 2004.

Page 102: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

102

9.2. Balance económico 300 horas de programación 30 €/hora 9000 €

100 horas de redacción memoria,

estudios, etc.

20 €/hora 2000 €

Equipo completo Pentium IV 2,8

Ghz para la implementación del

código

800 € 800 €

Sistema Operativo Windows XP

para la redacción de la memoria,

presentación del proyecto y

documentación

150 € 150 €

Sistema Operativo Linux para la

implementación del software

gratuito gratuito

Conexión ADSL 29 € / mes 290 €

TOTAL APROXIMADO 12240 €

Todos estos gastos corrieron a cuenta del proyectista e incluyen el uso de la librería en la aplicación de

protección de datos.

Page 103: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

103

9.3. Conclusiones

Los objetivos propuestos al principio de este proyecto han sido cubiertos en gran medida.

Se ha diseñado e implementado una librería de algoritmos que resuelven el problema del camino

mínimo en redes o grafos de forma eficiente pensado para aplicaciones que usan exhaustivamente su

tiempo de ejecución en este tipo de problemas.

La librería ha sido suficientemente probada en todo tipo de situaciones y, salvo problemas derivados

de agentes externos, será una buena herramienta que proporcionará un gran conjunto de algoritmos a

escoger, algo hasta ahora inexistente.

La librería, además, es de uso muy sencillo sin sacrificar en ningún momento la eficiencia temporal.

Permite también a los desarrolladores implementar nuevas versiones teniendo que modificar un código

mínimo.

Además, se ha logrado incluir la librería en la aplicación de protección de datos, proporcionando una

considerable velocidad de ejecución, que es lo que se buscaba.

Page 104: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

104

9.4. Líneas abiertas

Nunca se podrá decir que la librería esté cerrada a futuras modificaciones.

Se ha diseñado pensando en posibles añadidos siempre que se encuentren nuevos algoritmos más

eficientes o estructuras de datos que ofrezcan mayores velocidades de ejecución.

En los dos casos la inserción de los mismos es muy sencilla, teniendo únicamente que modificar un

mínimo de código, pero siempre muy específico y localizado.

Obviamente es una librería pensada para ser usada en aplicaciones muy específicas que emplean el

mayor tiempo en buscar caminos mínimos en redes o grafos y, por tanto, puede parecer limitada al

querer usarse en otros tipos de aplicaciones que, aunque también necesitan resolver problemas de

caminos mínimos, no requieren tanta eficiencia ni complicados algoritmos.

Por tanto, cabría pensar en una librería más “estética” pero sacrificando enormemente la eficiencia

temporal, con métodos mucho más sencillos e intuitivos, con impresión por pantalla de datos de

ejecución, más opciones en la construcción de grafos (aceptación de formatos stándard, etc.) e incluso

una versión gráfica como proporcionan algunas librerías existentes.

Page 105: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

105

10. Bibliografía

10.1. Libros

R.K. Ahuja, T.L. Magnanti, J.B. Orlin, “Network flows: theory, algorithms, and applications”,

Prentice Hall (1993).

D.P. Bertsekas, “Linear network optimization: algorithms and codes”, The MIT Press (1990).

C. Gómez, E. Mayol, A. Olivé, E. Teniente, “Enginyeria del software Disseny I”, Edicions UPC

(2001).

10.2. Artículos en revistas

U. Pape, “Implementation and efficiency of Moore-algorithms for the shortest route problem”,

Mathematical Programming 7 (1974), North-Holland Publishing Company.

R.K. Ahuja, K. Mehlhorn, J.B. Orlin, R.E. Tarjan, “Faster algorithms for the shortest path problem”.

S. Pallotino, “Shortest path methods”, Networks 14 (1984).

M.S. Hung, J.J. Divoky, “A computational study of efficient shortest path algorithms”, Computational

Operations Research 15 (1988).

F.B. Zhan, C.E. Noon “Shortest path algorithms: an evaluation using real road networks”,

Transportation Science 32 (1988).

F. Glover, D. Klingman, N. Phillips, “A new polynomially bounded shortest path algorithm”,

Operations Research 33 1 (1985).

M.L. Fredman, R.E. Tarjan, “Fibonacci heaps and their uses in improved network optimization

algorithms”, Journal of the ACM 34 3 (1987).

Page 106: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

106

J. Mondou, T.G.Crainic, S. Nguyen, “Shortest path algorithms: a computational study with the C

programming language”, Operations Research 18 8 (1991).

L.H. Cox “Network models for complementary cell suppression”, Journal of the American Statistical

Association 90 432” (1995).

10.3. De internet:

F. B. Zhan, C. E. Noon, “A comparison between label-setting and label-correcting algorithms for

computing one-to-one shortest paths”

T.A.J. Nicholson, “Finding the shortest route between two points in a network”.

J. Larsen, I. Pedersen, “Experiments with the auction algorithm for the shortest path problem” (1997).

J. Turner, “Algorithms and programs”.

G. Righini, “Bidirectional Dijkstra’s algorithm” (1999).

B. Liu, “Intelligent route finding: combining knowledge, cases and an efficient…” (1996).

R. Jacob, M. Marathe, K. Nagel, “A computational study of routing algorithms for realistic

transportation” (1998).

E.M. Vieira, “The optimal path problem” (1998).

D.P. Bertsekas, “Parallel asynchronous label correcting methods for shortest paths”.

E.M. Vieira, “The k shortest paths problem”.

D. Abuaiadh, J.H. Kingston, “Are Fibonacci heaps optimal?”, (1994).

D. Abuaiadh, J.H. Kingston, “An efficient algorithm for the shortest path problem” (1993).

D.P. Bertsekas, “Auction algorithms for network flow problems: a tutorial introduction”.

Page 107: 1. INTRODUCCIÓN 4 1.1. PRESENTACIÓN 4 1.2. A 5 1.3 ...controlbyte.net/fib/dijkstra.pdf · Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección

Desarrollo de una librería para caminos mínimos. Aplicación a un problema de protección de datos.

107

D. Burton, “The inverse shortest paths problem with upper bounds on shortest paths” (1997).

D. Burton, “On an instance of the inverse shortest paths problem” (1992).

K. Mehlhorn, “Data structures and graph algorithms. Shortest paths”.

A.V. Goldberg, “A simple shortest path algorithm with linear average time”.

W. Pijls, “A general framework for shortest path algorithms”.

A.V. Goldberg, R.E. Tarjan, “Expected performance of Dijkstra’s shortest path algorithm” (1996).

S.G. Kolliopoulos, “Finding real-valued single-source shortest paths in o(n3) expected time”.

S.Rao, “Shortest path algorithms: Dijkstra, Bellmand-ford and applications”, (2001).

A.V. Goldberg, “Implementations of Dijkstra’s algorithm based on multi-level buckets” (1995).

B.V. Cherskassky, A.V. Goldberg, “Buckets, heaps, lists and monotone priority queues” (1999).

10.4. De la aplicación de protección de datos J. Castro, "A fast network flows heuristic for cell suppression in positive tables", Lecture Notes in

Computer Science, 3050 (2004), pp. 136-148. Volume Privacy in Statistical Databases, eds. Josep

Domingo-Ferrer and Vicenç Torra, Springer, ISBN 3-540-22118-2.

J. Castro, "A shortest paths heuristic for statistical disclosure control in positive tables ", Research

Report DR 2004/10, Dept. of Statistics and Operations Research, Universitat Politècnica de Catalunya,

2004. Revised version submitted to INFORMS Journal on Computing.