Está en la página 1de 20

GRAFOS

(extraído de apuntes del Dr. Thomas Hibbard y otros apuntes)

En una red de comunicación no es necesario que toda estación pueda comunicarse


directamente con otra, puesto que las estaciones pueden actuar de nexo ente otras
estaciones. Si una estación deja de funcionar, queremos saber si la red queda conexo, es
decir si todas las estaciones que siguen funcionando pueden comunicarse entre si.
Para este tipo de interrogantes, no nos interesa la ubicación física de las estaciones,
sino su conectividad, y así surge la noción de grafo, que es simplemente unos nodos
con algunas conexiones que se llaman aristas.
Una arista puede conectar dos nodos, ó un nodo consigo mismo.

Definición: Un grafo es (N, A, P) donde N es un conjunto de nodos, A es un


conjunto de aristas, y P es una función de las aristas tal que cada P(a) = {p, q}
donde p, q son nodos (puede ser que p = q).

Cuando G es un grafo GN denota sus nodos, GA sus aristas, y GP su función de


aristas.
Cuando pensamos en grafos nos representamos un dibujo, por ejemplo

Este dibujo corresponde al grafo G en donde GN = {1, 2, 3, 4}, GA = {a, b, c, d, e},


P(a) = {1, 2}, P(b) = {2, 3}, P(c) = {1, 4}, P(d) = {1, 3}, P(e) = {3}.

Caminos y Conectividad: Un camino es aquel que comienza en algún nodo y a


través de las aristas visita varios nodos.
Definición: Un camino en un grafo G = (N, A, P) es una sucesión (posiblemente
vacía) “a” de aristas tal que existe una sucesión “p” de nodos tal que para cada a i,
P(ai) = {pi, pi+1}. La sucesión “p” se llama un recorrido del camino “a”.
Una sucesión de aristas no siempre es un camino, porque puede ser que no exista un
recorrido.
Por ejemplo, en la figura anterior, la sucesión c,b no es un camino, la sucesión a,b si
es un camino y su recorrido es 1, 2, 3. La sucesión a,a tiene dos recorrido 1, 2, 1 y 2, 1,
2. La sucesión a sólo tiene dos recorridos 1, 2 y 2, 1.
Para cualquier nodo p la sucesión de solo p es recorrido del camino vacío.
A veces se prohíbe que un camino repita aristas (aunque su recorrido si puede
repetir nodos).
Definición: Los nodos p, q de un grafo son conexos (p es conexo con q) si hay
un camino que empieza en p y termina en q.
El camino vacío conecta cualquier nodo con si mismo.
Definición: Un grafo es conexo si todos sus pares de nodos son conexos.
Vamos a analizar un algoritmo que decide si dos nodos dados, son conexos.
Si queremos encontrar un camino que una los nodos p y q, empezamos por p y le
preguntamos si posee como vecino a q (cada nodo sólo conoce a sus vecinos, pues está

Página Nº 1 Grafos
conectado a ellos por una arista), si no lo conoce tomamos nota de todos los vecinos de
p y vamos preguntando a cada vecino si conoce a q, si no lo conoce, tomamos nota de
los vecinos de dicho nodo, y así sucesivamente. Vamos tomando nota de los nodos ya
visitados, para no preguntarles dos veces al mismo nodo. Por lo que al no tener mas
nodo a quien preguntar, indicará dicha situación que los nodos p,q no son conexos. Por
supuesto que si algún vecino posee como vecino a q, querrá decir que los nodos p,q son
conexos.
Los nodos visitados lo vamos registrando en un conjunto de nodos, (no es necesario
usar una lista, puesto que la lista ordena sus elemento y aquí no interesa el orden).
Elegimos cualquier vecino de uno de los nodos del conjunto que no está en el
conjunto. Si no hay entonces q no es conexo con p. Si hay nodos y es q, entonces q si es
conexo con p, y si no es q entonces lo agregamos al conjunto y repetimos.

conexos(G, p, q) = si (p = q) „si‟ sino extender (G, {p}, q)

donde
extender (G, C, q) = si hay s  C con vecino r  C
si ( r = q) „si‟ sino extender (G, C  {r}, q)
sino „no‟.

G es un grafo (G = (N, A, P)).


La expresión si hay s  C con vecino r  C significa que si existe un nodo s que
pertenezca al conjunto de nodos C y que además algún vecino de r no pertenezca a C.
Es decir si queda algún nodo por preguntar.
La expresión extender (G, C  {r}, q) indica la función extender que posee tres
argumentos, el grafo G, el conjunto de nodos C, al cual se le une el nodo r. El tercer
argumento es el nodo que queremos encontrar q.
El primer llamado a extender, inicializa C con p.
Todo nodo en C es conexo con p, y q  C.
Si la función extender contesta no es que no encontró el buscado vecino r. En este
caso no puede haber un camino de p a q porque su recorrido empezaría dentro de C (con
p) y terminaría fuera de C (con q).
Si la función extender contesta si, es porque es porque q es conexo con algún nodo
de C y por lo tanto de p. El algoritmo queda demostrado.
El algoritmo no es determinista en su ejecución; La expresión si hay s  C con
vecino r  C nos ofrece una elección. Si el algoritmo pusiera restricciones sobre esas
elecciones, no sería correcto. En la realización del algoritmo, tendremos que elegir
deterministamente por algún criterio, sea por orden numérico o por su ubicación
en una estructura de datos, pero eso no es la esencia del algoritmo.

Página Nº 2 Grafos
Ejemplo Dado el gráfico anterior ver si los nodos 3 y 4 son conexos.

conexos(G, 3, 4) = si (3 = 4) „si‟ sino extender (G, {3}, 4)

donde
extender (G, {3}, 4) = si hay s  C con vecino r  C (elijo r=2)
si ( 2 = 4) „si‟ sino extender (G, {3}  {2}, 4)

extender (G, {3,2}, 4) = si hay s  C con vecino r  C (elijo r=1)


si ( 1 = 4) „si‟ sino extender (G, {3,2}  {1}, 4)

extender (G, {3,2,1}, 4) = si hay s  C con vecino r  C (elijo r=4)


si ( 4 = 4)  contesto “si “
(No es necesario seguir el retorno de cada invocación)

Podemos mejorar el algoritmo para obtener el recorrido de los nodos p,q , en caso de
ser conexos. Para ello mantenemos una función llamada portador, que para cada nodo
C, salvo p, da la arista que la hizo entrar en C. En la primera invocación es vacía ya que
p no entra por ninguna arista, p no tiene portador.
Para poder usar la función portador usaremos un nuevo mecanismo notacional.
La función portador está definida en datos, no por algoritmo como lo veníamos
haciendo. Es decir que siempre que se invocaba f(x), se llamaba a un algoritmo, por
ejemplo la función extender (...). Ahora lo que haremos será definir a la función por
datos. Como en otros tipos de datos necesitamos notación para expresar las operaciones
básicas. Si deseamos asignar a la función un valor para un argumento dado, por ejemplo
f(5) = 4, entonces necesitamos la operación que cambia dicha función para un
argumento dado.
En el ejemplo f(5)=4, puede que f(5) no este definida, o que previamente tenía otro
valor. En realidad estamos queriendo definir una nueva función g que es casi igual que f
y difiere únicamente en la cuestión de g(5): queremos que para g(5) sea 4, pero que para
cualquier x  5 g(x) sea definido solo si f(x) esta definido, y en ese caso g(x) = f(x).
Llamaremos este g como f (5  4).
La definición formal es: “Para una función f con dominio D incluido es un tipo de
datos T, con valores en el tipo de datos U, un objeto x de T y un objeto y de U, f (x y)
es la función g de cuyo dominio es D  {x} con g(x) = y, y para todo z  x en D,
g(z)=f(z) .”

Algoritmo para obtener el camino del nodo p al nodo q en el grafo G:

camino(G, p, q) = si (p = q) el camino vacio


sino extender ({p}, función vacia, q)

donde
extender (C,portador, q) =
si hay arista a con P(a) = {s,r} donde s  C y r  C
si ( r = q) construir ( q, portador ( q a) )
sino extender ( C  {r},portador (r  a), q)

donde

Página Nº 3 Grafos
construir (q, portador) =
si portador(q) = a existe Nota: El a es genérico... ej f(3)=b entonces a=b
sea P(a) ={p, q}
contestar construir(p, portador),a
sino {}

Si la alternativa “si hay arista a ...” no se da, el algoritmo falla (se cuelga).
Este algoritmo es no determinista también. Su no determinismo afecta el resultado
en el caso de que haya más de un camino entre p y q. (según lo que se elija mostrará el
camino encontrado).
La eficiencia del algoritmo depende de cómo encontramos un nodo que
pertenezca a un conjunto y otro que no pertenezca al mismo conjunto “si hay arista a
con P(a) = {s,r} donde s  C y r  C”. La eficiencia para ello depende mucho de la
estructura de datos con que representemos el conjunto, así que no podemos saber mucho
de la eficiencia de estos algoritmos antes de ver algo sobre como están realizados.
Algo podemos saber. Sabemos por ejemplo que no va a haber más que n
invocaciones de extender, donde n es el número de nodos en G. Entonces la eficiencia
reside en la de extender.
En definitiva: el algoritmo abstracto puede indicar algo de la eficiencia con que
puede trabajar, pero puede depender mucho de cómo se lo realiza también (de la
estructura de datos empleada).

Ejemplo Dado el gráfico anterior ver si los nodos 3 y 4 son conexos.

camino(G, 3, 4) = si (3 = 4) el camino vacio


sino extender ({3}, función vacia, 4)
*1 retorna “b a c”
------------------------
*1 extender ({3}, función vacia, 4) =
(ELIJO a=”b” y r=2)
si hay arista a con P(a) = {s,r} donde s  C y r  C
si ( 2 = 4) ......
sino extender ({3}  {2},portador (2  “b”), 4)
retorna “b a c” a *1

extender ({3, 2}, (2b), 4) =


(ELIJO a=”a” y r=1)
si hay arista a con P(a) = {s,r} donde s  C y r  C
si ( 1 = 4) ......
sino extender ({3, 2}  {1},portador (2b, 1a), 4)
retorna “ b a c”

extender ({3, 2, 1}, (2b, 1a), 4) =


(ELIJO a=”c” y r=4)
si hay arista a con P(a) = {s,r} donde s  C y r  C
si ( 4 = 4) construir ( 4, portador (2b, 1a, 4c) )
*2 retorna “ b a c”
-----------------------
*2 construir (4, (2b, 1a, 4c)) =
si existe la relación portador(4) = c busco que valor tiene f(4) y es c
sea P(c) ={1, 4} Este dato ya lo poseo en G
contestar construir(1, portador), c

Página Nº 4 Grafos
I
retorna “b a c” al punto *2

construir (1, (2b, 1a, 4c)) =


si existe la relación portador(1) = a busco que valor tiene f(1) y es a
sea P(a) ={2, 1} Este dato ya lo poseo
contestar construir(2, portador), a

II retorna “b a” al punto I

construir (2, (2b, 1a, 4c)) =


si existe la relación portador(2) = b busco que valor tiene f(2) y es b
sea P(b) ={3, 2} Este dato ya lo poseo
contestar construir(3, portador), b
III retorna “ b” al punto II

construir (3, (2b, 1a, 4c)) =


si existe la relación portador(3) = ...... No existe
devuelvo {} vacio
Retorna {} al punto III

Los Puentes de Königsburg y Ciclos de Euler

Leonardo Euler, uno de los grandes matemáticos de la historia, vivía en Königsburg,


y los colegas de él se divertían discutiendo un problema: como hacer una caminata por
la ciudad cruzando cada uno de sus siete puentes una y sola una vez, y volver al punto
de partido.
Un plano de la ciudad se veía así:

Sólo importan los puentes, las islas y las dos bandas del río. Pero Euler quería
simplificarlo más. El decía que era un problema del siguiente grafo:

Página Nº 5 Grafos
Y que no importaba si los nodos eran islas u otra cosa. Y se puso a pensar en la
generalidad de esos curiosos objetos, que ahora llamamos grafos, y del problema de
encontrar un camino que pasaba por cada arista y volvía al nodo de partida.

Definición: Un camino no nulo cuyo recorrido empieza y termina en el mismo


nodo y que no repite ninguna arista se llama ciclo. Si cada arista del grafo aparece
en un ciclo se llama ahora ciclo de Euler.

Euler resolvió ese primer problema con que fundó la teoría de grafos.
Resultó que no existía un ciclo de Euler para los puentes de Königsburg. La razón
era que el grafo tiene nodos de grado impar. El grado de un nodo es el número de
extremos de aristas conectados al nodo.
Decimos extremos porque una arista conectado a un solo nodo cuanta pasa dos. La
idea es si imaginamos una puerta en cada extremo de cada arista, para entrar y salir del
nodo, el grado es el número de puertas.
Definición: El grado de un nodo p en un grafo G = (N, A, P) es el número de
aristas a tal que P(a) = {p, q} con q  p, más dos veces el número de aristas a tal
que P(a) = {p}.

Teorema: Un grafo sin nodos aislados (nodos sin aristas) tiene un ciclo de Euler
si y solo si es conexo y cada nodo tiene grado par.

Si hay un ciclo de Euler, salimos por una puerta del nodo inicial y entramos en otra
puerta al final de la última arista, y para cada nodo intermedio entramos por una puerta
y salimos por otra (incluimos que el nodo intermedio puede ser un nodo ya visitado,
pero usando otras puertas, tanto para entrar como para salir). Entonces vemos un
número par de puertas en cada nodo. No puede haber una puerta que no pasamos porque
eso significaría una arista que no pasamos.
Conclusión: un ciclo de Euler implica que cada nodo tiene grado par.

El algoritmo empieza con cualquier nodo p de G, y sigue eligiendo aristas hasta que
vuelve a p, pero recordando las aristas elegidas para no usarlas nuevamente.
Luego preguntamos si en el recorrido del ciclo así formado hay un nodo con una
arista que no está en el ciclo:
-Si no hay, no hay ninguna arista sin usar porque el grafo es conexo.
-Si hay, el nodo pertenece al ciclo pero no una arista que lo toca. Entonces el
algoritmo hace un ciclo sobre ese nodo y lo inserta en el lugar en donde está ese nodo,
obteniendo un nuevo ciclo, con que repite lo que estamos haciendo.
Por ejemplo insertamos el ciclo efg en el ciclo abcd.

Página Nº 6 Grafos
La única forma en que este algoritmo puede fallar es en no poder completar un ciclo
sobre un nodo dado. La única forma de que eso ocurra es que llegue a un nodo que no
posea aristas que no fue usada ya. Pero eso no se da con el nodo inicial, porque sería un
nodo sin aristas, un nodo aislado, que no hay. Tampoco se da al iniciar un ciclo para ser
insertado, porque ese nodo está elegido precisamente porque tiene una arista no usada.
Y, finalmente no puede ser un nodo recién entrado porque ha entrado un número impar
de veces, porque cada vez que entró previamente, salió. Pero el grado del nodo es par,
por lo tanto queda por lo menos una puerta sin usar, implicando de una arista sin usar.

cicloEuler (G= (N, A, P)) =


extender ({}, A)

donde
extender (c, B) = (c un ciclo, B las aristas no en c)
si (B es vacio) c
sino
sea r un recorrido del camino c
sea a  B tal que P(a) = { ri, q} para algún i y nodo q
sea (d, D) = ciclo ({a},ri , q, B –{a})
extender ( insertar (d, c, i), D)

y (los argumentos de ciclo son camino, nodo, nodo, conjunto de aristas)

donde
ciclo (c, p, q, B) =
si (p = q) (c, B)
sino
sea a  B con P(a) = {q, r}, algún nodo r
ciclo (ca, p, r, B -{a})

Insertar es tan útil que lo hacemos global.

insertar(d, c, i) =
si (d es vacia) c
sino insertar (resto(d), c (id1, cj  cj-1, todo j > i ), i+1)

EJEMPLO: Veremos el ejemplo del grafico anterior:

cicloEuler (G= (N, A, P)) =


extender ({}, A) vamos a **
Responder {a, d, c, g, f, e, b }

donde
** extender ({}, {a,b,c,d,e,f,g}) = (c un ciclo, B las aristas no en c)
c posee aristas
si ({a,b,c,d,e,f,g} es vacio) c
sino
sea r un recorrido del camino c
Nota: en este momento c no tiene camino (aristas), por lo tanto no
habría nodos que unen esas aristas (recorrido del camino son los nodos).

Página Nº 7 Grafos
Pero por definición “ El camino vacio conecta cualquier nodo con si
mismo”, por lo que podemos elegir cualquier nodo.

sea a  B tal que P(a) = { ri, q} para algún i y nodo q


Elegimos a=‟a‟ donde P(a)={1,2}
Nota: Si vemos el grafico, la arista „a‟ une el nodo 1 con el 2, elegimos
ri=1, por que i=1.

sea (d, D) = ciclo ({a},ri , q, B –{a}) *1


Nota: invocamos a ciclo({a}, 1, 2, {d,c,b,e,f,g}) vamos a *1
(d, D)= ({a, d, c, b}, {e, f, g})

extender ( insertar (d, c, i), D)


Invoca a insertar ({a, d, c, b}, { }, 1) vamos a *2
El llamado a *2 retorno { a, d, c ,b }
Invocamos a extender ({ a, d, c ,b }, {e, f, g}) vamos a *A
Responder {a, d, c, g, f, e, b } vamos a **
*A extender ({ a, d, c ,b }, {e, f, g}) extender (c, B)
B{}
sea r un recorrido del camino c
Actualmente r= {1, 2, 3, 4}

sea a  B tal que P(a) = { ri, q} para algún i y nodo q


Elijo a=‟f‟ tal que P(f) = { ¿?..
No encuentro un nodo en el recorrido r={1,2,3,4} que use la arista „f‟
Elijo a=‟g‟ tal que P(g) = {4, 6} la arista „g‟ une los nodos 4 y 6.
ri = 4, o sea I = 4.

(d, D) = ciclo ({g}, 4, 6, {e, f})


invocamos a ciclo ciclo ({g}, 4, 6, {e, f}) vamos a *3
(d, D) = {g, f, e}, { }

extender ( insertar (d, c, i), D)


Invoca a insertar ({g, f, e}, {a, d, c, b }, 4) vamos a *4
Insertar responde {a, d, c, g, f, e, b }
Invocamos a extender ({a, d, c, g, f, e, b }, { }) vamos a *B
Responder {a, d, c, g, f, e, b } regresamos a *A

*B extender ({a, d, c, g, f, e, b }, { }) extender (c, B)


B={}
Responder {a, d, c, g, f, e, b } regresamos a *B

---------------------------------------------------------

*1 ciclo ({a},1 , 2, {d, c, b, e, f, g}) ciclo (c, p, q, B)


pq

sea a  B con P(a) = {q, r}, algún nodo r


Tomo a= „d‟ con P(d) = {2, 3}

Página Nº 8 Grafos
Nota: Tome una arista que este en B (d) , pero que tenga un nodo igual
a q, en este caso q= 2 por lo que las únicas aristas que tienen al nodo 2
son „d‟ y „a‟. Pero „a‟  B.

ciclo (ca, p, r, B -{a})


Invocamos a ciclo({a, d}, 1, 3, {b, c, e, f, g}) *1-a
responder {a, d, c, b} {e, f, g} (vuelve a *1)

*1-a ciclo ({a, d}, 1, 3, {b, c, e, f, g}) ciclo (c, p, q, B)


pq

Tomo „c‟ con P(c) = {3, 4}


Invocamos a ciclo({a, d, c}, 1, 4, {b, e, f, g}) *1-b
responder {a, d, c, b} {e, f, g} (vuelve a *1)

*1-b ciclo ({a, d, c}, 1, 4, {b, e, f, g}) ciclo (c, p, q, B)


pq

Tomo „b‟ con P(b) = {4, 1}


Invocamos a ciclo({a, d, c, b}, 1, 1, { e, f, g}) *1-c
responder {a, d, c, b} {e, f, g} (vuelve a *1-a)

*1-c ciclo ({a, d, c, b}, 1, 1, { e, f, g}) ciclo (c, p, q, B)


p=q
responder {a, d, c, b} {e, f, g} (vuelve a *1-b)

--------------------------------------------

*2 insertar ({a, d, c, b}, { }, 1) insertar(d, c, i)


si (d es vacia) c
sino insertar (resto(d), c (id1, cj  cj-1, todo j > i ), i+1)
invocamos a insertar ({d, c, b}, {a}, 2) vamos *2-a

El objetivo de c (id1, cj  cj-1, todo j > i ) es insertar en la posición i al


primer elemento de d (c ( id1), pero previamente se hace un
corrimiento, colocando en la posición N+1 el contenido de c N, (cj cj-1),
coloco cj = cj-1. Luego vamos realizando el corrimiento hasta llegar a I,
(todo j > i ). (N es el número de elementos de c).

Hacemos C1=‟a‟

*2-a insertar ({ d, c, b}, {a}, 2) insertar(d, c, i)


Hacemos C2=‟d‟
invocamos a insertar ({c, b}, {a, d}, 3) vamos *2-b
responder { a, d, c ,b } regresamos *2

*2-b insertar ({ c, b}, {a, d}, 3) insertar(d, c, i)


Hacemos C3=‟c‟

Página Nº 9 Grafos
invocamos a insertar ({ b}, {a, d, c}, 4) vamos *2-c
responder { a, d, c ,b } regresamos *2-a

*2-c insertar ({ b}, {a, d, c }, 4) insertar(d, c, i)


Hacemos C4=‟b‟
invocamos a insertar ({}, {a, d, c, b}, 5) vamos *2-d
responder { a, d, c ,b } regresamos *2-b

*2-d insertar ({ }, {a, d, c, b }, 5) insertar(d, c, i)


d={} (d es vacia)
responder { a, d, c ,b } regresamos *2-c
------------------------------------------

*3 ciclo ({g}, 4, 6, {e, f}) ciclo (c, p, q, B)


pq
Tomo „f‟ con P(f) = {6, 5}
Invocamos a ciclo({g, f}, 4, 5, { e}) *3-a
responder {g, f, e}, { } regresamos a *3

*3-a ciclo({g, f}, 4, 5, { e}) ciclo (c, p, q, B)


pq
Tomo „e‟ con P(e) = {5, 4}
Invocamos a ciclo({g, f, e}, 4, 4, { }) *3-b
responder {g, f, e}, { } regresamos a *3

*3-b ciclo({g, f, e}, 4, 4, { }) ciclo (c, p, q, B)


p=q
responder {g, f, e}, { } regresamos a *3-a

-------------------------------------------------------

*4 insertar ({g, f, e}, {a, d, c, b }, 4) insertar(d, c, i)


si (d es vacia) c
sino insertar (resto(d), c (id1, cj cj-1, todo j > i ), i+1)
1º Hacemos un corrimiento c5 = b j > 4
2º Insertamos el elemento c4 = d1  c4 = g
3º queda c={a, d, c, g, b}

invocamos a insertar ({f, e}, {a, d, c, g, b}, 5) vamos *4-a


responder {a, d, c, g, f, e, b } regresamos a *4

*4-a insertar ({f, e}, {a, d, c, g, b }, 5) insertar(d, c, i)


invocamos a insertar ({e}, {a, d, c, g, f, b}, 6) vamos *4-b
responder {a, d, c, g, f, e, b } regresamos a *4

*4-b insertar ({ e}, {a, d, c, g, f, b }, 6) insertar(d, c, i)


invocamos a insertar ({ }, {a, d, c, g, f, e, b}, 7) vamos *4-c
responder {a, d, c, g, f, e, b } regresamos a *4-a

*4-c insertar ({ }, {a, d, c, g, f, e, b }, 7) insertar(d, c, i)


d ={ }

Página Nº 10 Grafos
responder {a, d, c, g, f, e, b } regresamos a *4-b

ESTRUCTURA DE DATOS PARA GRAFOS

Expresiones como “d es vacio” y “c( i  d1)” no son entendidas por un compilador.


Un lenguaje como Mathematica se acerca a esta riqueza de expresión pero los
algoritmos sobre grafos son notorios por largos en su ejecución y los procesadores como
el Mathematica son lerdos (el precio inevitable de la riqueza de expresión).
Tenemos que usar un lenguaje como C o Pascal y enseñarle como trabajar con
conjuntos y funciones definidas en datos.
Vamos a utilizar (por ahora) los array.
Se puede representar un conjunto de objetos de tipo T como una sucesión sobre T,
es decir, una sucesión en que cada si es un objeto de T. Entendemos que un objeto t está
en el conjunto si si = t para algún si.
La variable n nos indica el largo de la sucesión s.
Primero veremos sin recursión ( la recursión trae aparejado problemas de memoria y
de tiempo de ejecución).
La construcción de un programa es en etapa:
1º) Hacemos la expresión matemática pura.
2º) Progresamos, representando la expresión matemática primaria con tipos de
datos mas cercano a los tipos de datos suministrados por el lenguaje. (quizás
ocurra dos o mas refinamiento de esta etapa).
3º) Transformamos las funciones en pseudocódigo imperativo.
4º) Trasladamos el pseudocódigo en código.

Veamos las funciones:


1) x  C, donde C se representa por una suseción s de largo n.
t  (s, n) = si ( hay i con 1 i  n, y t = xi ) si sino no.

2) C  x ( abreviatura de C  {x})
(s, n)  t = si (t  (s, n)) (s, n) sino (s(n+1 t), n+1)
nota: s(n+1 t) significa que al vector s le agrego el elemento t en la posición n+1

3) C – x
(s, n) – t = si (hay i con 1 i  n, y t = xi ) (s( j sj+1 para i j < n), n-1)
Una vez que encuentro el elemento en la posición i, hago un corrimiento desde el
elemento hacia el final, eliminando el elemento y reduciendo a n-1 el tamaño de s.
j sj+1 significa s[j]= s[j+1] desde j:=i hasta n-1.

Veamos ahora el seudocódigo imperativo.

1) x  C, donde C se representa por una suseción s de largo n.


t  (s, n) = Para i = 1 hasta n
si (t = si) contestar sí
contestar no (si nunca contestó si)

2) C  x ( abreviatura de C  {x})

Página Nº 11 Grafos
agregar (x, s, n)
si (x  (s, n))
n = n+1;
sn = x;
3) C – x
sacar(x, s, n)
para (i =1 hasta n)
si ( si = x)
para ( i hasta n-1)
si = si+1;
n = n-1
----------------------------

Para representar una función ......

CAMINO MINIMO

Hay un montón de problemas en que cada arista tiene un costo de algún tipo, como
por ejemplo distancia, o tiempo, o precio del boleto de avión.
Definición: Un grafo con costos es (N, A, P, c) donde (N, A, P) es un grafo y c es
una función de las aristas a los números reales no negativos.
El costo de un camino a1, a2, ......,an es 1 c (ai).
n

Consideramos ahora el problema de encontrar el camino demínimo costo entre dos


nodos n y m.
Primero hacemos un algoritmo que evalue el costo del camino mínimo entre n, m, y
luego recuperamos el camino agregando una función predecesor, para llegar al
algoritmo de Dijkstra.
El principio del algoritmo de Dijstra es este: Supongamos que tengo un conjunto C
de nodos y para cada nodo p en C el costo d(p) de un camino mínimo de n a p. Si m  C
entonces d(m) es el resultado que buscamos, y si no, elegimos una arista con un pie en
C y el otro afuera, es decir, una arista a tal que P(a) = {p, q} con p  C y q  C.
Además imponemos la condición sobre la arista a: que d(p) + c(a) sea el mínimo
posible...

Expresemos el algoritmo:

costo (n, m) = costo1 ({n}, m, { n0})

donde
costo1(C, m, d) =
sea a una arista tal que P(a) = {p, q} con p C y q  C
y tal que d(p) + c(a) es lomás pequeño posible
si (q = m) d(p) + c(a)
sino costo1(C  {q}, m, d(qd(p) + c(a)))

Ejemplo Dada la siguiente figura, aplicar el algoritmo de costo

Página Nº 12 Grafos
grafo costos

costo (1, 8) = costo1 ({1}, 8, { 10})


respondemos 6 (el costo de llegar de 1 a 8)

donde
costo1({1}, 8, { 10}) = costo1(C, m, d)
{ 10} significa que el costo para llegar al nodo 1 es 0

sea a una arista tal que P(a) = {p, q} con p C y q  C


y tal que d(p) + c(a) es lomás pequeño posible
b tiene un costo de 2, k de 5 y a de 1
elegimos a=‟a‟ tal que P(a) = {1, 2}

qm
invocamos a costo1({1, 2}, 8, d(10, 20 + 1)) vamos a *1
respondemos 6

*1 costo1({1, 2}, 8, d(10, 21) )= costo1(C, m, d)


elegimos b tal que P(b)= {1, 3} 1 C y 3  C
y d(1) + c(b) = 0 + 2 = 2
si elijo k d(1)+c(k)=0+5=5, Si elijo d tiene costo 1+2=3, Si elijo i tiene costo 1+5= 6

qm
invocamos a costo1({1, 2, 3}, 8, d(10, 21, 32)) vamos
a *1-a
respondemos 6 regresamos a *1

*1-a costo1({1, 2, 3}, 8, d(10, 21, 32)) = costo1(C, m, d)


elegimos c tal que P(c)= {3, 4} 3 C y 4  C
y d(3) + c(c) = 2 + 1 = 3

48
invocamos a costo1({1, 2, 3, 4}, 8, d(10, 21, 32, 43)) vamos
a *1-b
respondemos 6 regresamos a *1a

*1-b costo1({1, 2, 3, 4}, 8, d(10, 21, 32, 43)) =


costo1(C, m, d)
elegimos e tal que P(e)= {3, 6} 3 C y 6  C
y d(3) + c(e) = 2 + 1 = 3

68

Página Nº 13 Grafos
invocamos a costo1({1, 2, 3, 4, 6}, 8, d(10, 21, 32, 43, 63)) vamos
a *1-c
respondemos 6 regresamos a *1b

*1-c costo1({1, 2, 3, 4, 6}, 8, d(10, 21, 32, 43, 63)) =


costo1(C, m, d)
elegimos l tal que P(l)= {6, 5} 6  C y 5  C
y d(6) + c(l) = 3 + 1 = 4

58
invocamos a costo1({1, 2, 3, 4, 6, 5}, 8, d(10, 21, 32, 43, 63, 54))vamos a *1-d
respondemos 6 regresamos a *1c

*1-d costo1({1, 2, 3, 4, 6, 5}, 8, d(10, 21, 32, 43, 63, 54)) =


elegimos j tal que P(j)= {5, 7} 5  C y 7  C
y d(5) + c(j) = 4 + 1 = 5

78
invocamos a costo1({1, 2, 3, 4, 6, 5, 7}, 8, d(10, 21, 32, 43, 63, 54, 75))vamos
a *1e
respondemos 6 regresamos a *1d

*1-e costo1({1, 2, 3, 4, 6, 5, 7}, 8, d(10, 21, 32, 43, 63, 54, 75)) =
elegimos h tal que P(h)= {7, 8} 7  C y 8  C
y d(7) + c(h) = 5 + 1 = 6

si (q = m) d(p) + c(a)
como 8 = 8 d(7) + c(h) = 5 + 1 = 6
respondemos 6 regresamos a *1e

Las siguientes figuras muestran las sucesivas llamadas a costo1. Los nodos con un
número escrito adentro son los que pertenecen a C, y el número es el valor de d para ese
nodo.

Página Nº 14 Grafos
caminoMinimo (n,m)=
si(m = n) { }
sino sacarCamino (nodoArista ( {n}, m, {n0} ))

donde
nodoArista (C, m, d) =
sea a una arista tal que P(a) = {p, q} con p  C y q  C
y tal que d(p) + c(a) es lo más pequeño posible
si ( q = m) e( qa)
sino nodoArista ( C {q}, m, d( q d(p) + c(a)), e(qa)

donde
sacarCamino (e, n) =
si (e(n) no está definida) {}
sino sea P(e(n))= {n, q} (e(n) siempre está anclada en n por construcción)
sacarCamino(e, q), e(n)

Ejemplo: Vamos a sacar el camino mínimo entre el nodo 1 y el nodo 5

Página Nº 15 Grafos
grafo costos

caminoMinimo (1,5)=
si(5 = 1) { }
sino sacarCamino (nodoArista ( {1}, 5, {10} ), 5) vamos a *1
invocamos a sacarCamino((5 l, 6e, 4c, 3b, 2a), 5) vamos a *2
Respondo: b, e, l

-----------------------------------------
donde
*1 nodoArista ( {1}, 5, {10} ) = nodoArista (C, m, d) =

sea a una arista tal que P(a) = {p, q} con p  C y q  C


y tal que d(p) + c(a) es lo más pequeño posible
elegimos „a‟ tal que P(a) = {1, 2}
y d(1) + c(a) = 0 + 1 = 1 (es el mínimo)

2 5
invoco a nodoArista ( {1, 2}, 5, d(10, 21)) vamos *1-a
Respondemos e(5 l, 6e, 4c, 3b, 2a) Regresamos a *1

*1-a nodoArista ({1, 2}, 5, d(10, 21) ) =


elegimos „b‟ tal que P(b) = {1, 3}
y d(1) + c(b) = 0 + 2 = 2 (es el mínimo)

3 5
invoco a nodoArista ( {1, 2, 3}, 5, d(10, 21, 32) ) vamos *1-b
respondemos e(5 l, 6e, 4c, 3b) Regresamos a *1

*1-b nodoArista ({1, 2, 3}, 5, d(10, 21, 32) )


elegimos „c‟ tal que P(c) = {3, 4}
y d(3) + c(c) = 2 + 1 = 3 (es el mínimo)

45
invoco a nodoArista ( {1, 2, 3, 4}, 5, d(10, 21, 32, 43) )vamos
*1-c
respondemos e(5 l, 6e, 4c) Regresamos a *1-a

*1-c nodoArista ({1, 2, 3, 4}, 5, d(10, 21, 32, 43) )


elegimos „e‟ tal que P(e) = {3, 6}
y d(3) + c(e) = 2 + 1 = 3 (es el mínimo)

65
invoco a nodoArista ( {1, 2, 3, 4, 6}, 5, d(10, 21, 32, 43, 63))
vamos *1-d
responder e( 5 l), e (qa)
respondemos e(5 l, 6e) Regresamos a *1-b

*1-d nodoArista ({1, 2, 3, 4, 6}, 5, d(10, 21, 32, 43, 63) ) =


elegimos „l‟ tal que P(l) = {6, 5}

Página Nº 16 Grafos
y d(6) + c(l) = 3 + 1 = 4 (es el mínimo)

si ( q = m) e( qa)
5=5 responder e( 5 l) Regresamos a *1-c
(significa que al nodo 5 lo accedemos mediante la arista l)

------------------------------------

donde
*2 sacarCamino((5 l, 6e, 4c, 3b, 2a), 5) = sacarCamino (e, n)
si (e(n) no está definida) { }
sino
sea P(e(n))= {n, q}
como (e(5) = l) P(l) = {5, 6}

invoco a sacarCamino((5 l,6e,4c,3b,2a ),6) ir *2-a


e(n)=e(5), Respondo b, e, l Retorno a *2

*2-a sacarCamino((5 l, 6e, 4c, 3b, 2a), 6) = sacarCamino (e, n)


como (e(6) = e) P(e) = {6, 3}

invoco a sacarCamino((5 l,6e,4c,3b,2a ),3) ir *2-b


e(n)=e(6) , Respondo b,e Retorno a *2

*2-b sacarCamino((5 l, 6e, 4c, 3b, 2a), 3) = sacarCamino (e, n)


como (e(3) = b) P(b) = {3, 1}

invoco a sacarCamino((5 l,6e,4c,3b,2a ),1) ir *2-c


Respondo e(3) , Respondo b Retorno a *2-a

*2-c sacarCamino((5 l, 6e, 4c, 3b, 2a), 1) = sacarCamino (e, n)


como (e(1) = No está definido e(1)
si (e(n) no está definida) { }
Respondo { } Retorno a *2-b
---------------------------------

Podemos sacar el camino mínimo mediante el uso de matrices, usando el


algoritmo de Floyd.
Arboles: Ver algoritmo de: a) arbolCubridor, b) arbolCubridorMin
Apareamiento: Ver algoritmo de: a)camino, b)húngaro
Ejemplo de aplicación usando el algoritmo de Dijkstra.

Para la red siguiente obtenga el árbol generado por el Router A considerando que
se aplica el algoritmo de Dijkstra. Los números indican el costo del enlace hacia
el vecino correspondiente.

Página Nº 17 Grafos
NOTA RB (10) ----- RE (2) SIGNIFICA QUE PARA IR DE RB A RE EL
COSTO ES 2 Y PARA IR DE RE A RB EL COSTO ES 10.

RESPUESTA:
DIJKSTRA: Inicializa E con el nodo origen, R con el resto de los nodos y O con
los caminos a partir del origen por orden creciente de métrica.
En P ponemos el camino elegido.

E=(RA); R=(RB,RC,RD,RE,RF,RG,RH);
P=( ).

O=(RARB(2),RARD(4), RARE(5)).

El camino mas corto es RARB(2) y vemos que RB no figura en E. Lo sacamos de


R y lo ponemos en E.
E=(RA,RB), R=(RC,RD,RE,RF,RG,RH);
P=( RARB(2)).
Sacamos de O la ruta RARB e incorporamos en O las rutas a partir de RARB y
cuyo nodo externo no este en E
Recuerde que el orden es creciente.

O=(RARD(4), RARBRE(4), RARE(5), RARBRC(7) ).

REPETIMOS LOS PASOS ANTERIORES:


Elijo RARD(4) (RD no se encuentra en E)
E=(RA,RB, RD), R=(RC,RE,RF,RG,RH);

Página Nº 18 Grafos
P=( RARB(2), RARD(4)).

O=(RARBRE(4), RARE(5), RARBRC(7), RARDRE(7),


RARDRG(9) ).

Elijo RARBRE(4 ) (RE no se encuentra en E)


E=(RA,RB, RD, RE), R=(RC,RF,RG,RH);
P=( RARB(2), RARD(4), RARBRE(4 )).

O=(RARE(5), RARBRERG(5), RARBRERF(6),


RARBRC(7), RARDRE(7), RARDRG(9), RARBRERH(12)).

Elijo RARE(5) pero RE ya está en E, se elimina RARE de O.


Elijo RARBRERG(5) (RG no se encuentra en E)
E=(RA,RB, RD, RE, RG), R=(RC,RF,RH);
Con RG me uno a RE y RD, pero ambos ya están en E. Por lo tanto no se
incorpora nuevo path a O.
P=( RARB(2), RARD(4), RARBRE(4 ), RARBRERG(5)).

O=(RARBRERF(6), RARBRC(7), RARDRE(7),


RARDRG(9), RARBRERH(12) ).

Elijo RARBRERF(6) (RF no se encuentra en E)


E=(RA,RB, RD, RE, RG, RF), R=(RC,RH);
P=( RARB(2), RARD(4), RARBRE(4 ), RARBRERG(5),
RARBRERF(6)).

O=(RARBRC(7), RARDRE(7), RARBRERFRC(8),


RARDRG(9), RARBRERH(12), RARBRERFRH(12) ).

Elijo RARBRC(7) (RC no se encuentra en E)


E=(RA,RB, RD, RE, RG, RF, RC), R=(RH);
Con RC me uno a RB y RF, pero ambos ya están en E. Por lo tanto no se
incorpora nuevo path a O.
P=( RARB(2), RARD(4), RARBRE(4 ), RARBRERG(5),
RARBRERF(6), RARBRC(7)).

O=(RARDRE(7), RARBRERFRC(8), RARDRG(9),


RARBRERH(12), RARBRERFRH(12) ).
Elijo RARDRE(7) (RE si se encuentra en E, saco el path de O)

O=(RARBRERFRC(8), RARDRG(9), RARBRERH(12),


RARBRERFRH(12) ).

Elijo RARBRERFRC(8) (RC si se encuentra en E, saco el path de O)

O=( RARDRG(9), RARBRERH(12), RARBRERFRH(12) ).

Elijo RARDRG(9) (RG si se encuentra en E, saco el path de O)

Página Nº 19 Grafos
O=( RARBRERH(12), RARBRERFRH(12))

Elijo RARBRERH(12) (RH no se encuentra en E)


E=(RA,RB, RD, RE, RG, RF, RC, RH), R=( );
Con RH me uno a RE y RF, pero ambos ya están en E. Por lo tanto no se
incorpora nuevo path a O.
P=(RARB(2), RARD(4), RARBRE(4), RARBRERG(5),
RARBRERF(6), RARBRC(7), RARBRERH(12)).

O=( RARBRERFRH(12))
Elijo RARBRERH (RH si se encuentra en E)

O=( ) R=( )  FIN.


E=(RA,RB, RD, RE, RG, RF, RC, RH)

Árbol resultante

RA 4
2

RB RD
7 4

RC RE

12 5
6

RH RF RG

Página Nº 20 Grafos

También podría gustarte