0% encontró este documento útil (0 votos)
75 vistas13 páginas

xaoD04ErRuxp0RKl - rub6K3lNRKPv - 5gQ-Lectura Fundamental 6

Este documento describe dos problemas clásicos de programación dinámica: el cálculo del enésimo número de Fibonacci y la distancia de edición entre cadenas. Explica cómo la programación dinámica puede resolver estos problemas de forma más eficiente que los enfoques recursivos directos.

Cargado por

pruebas pruebas
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
75 vistas13 páginas

xaoD04ErRuxp0RKl - rub6K3lNRKPv - 5gQ-Lectura Fundamental 6

Este documento describe dos problemas clásicos de programación dinámica: el cálculo del enésimo número de Fibonacci y la distancia de edición entre cadenas. Explica cómo la programación dinámica puede resolver estos problemas de forma más eficiente que los enfoques recursivos directos.

Cargado por

pruebas pruebas
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 13

Unidad 3

1 //Escenario
Escenario26
Lectura fundamental
Fundamental

Programación
Etapas de un plan
dinámica
de comunicación
estratégica

Contenido

1 Cálculo del enésimo número de la sucesión de Fibonacci

2 Distancia de edición

3 Resumen

Palabras clave: algoritmos, programación dinámica, ecuación de recurrencia.


Introducción
Como quedó en evidencia al estudiar la técnica dividir y conquistar, la recursividad es una herramienta
valiosa en lo que al diseño de algoritmos eficientes se refiere. Sin embargo, en algunos casos, su
aplicación directa también puede redundar en algoritmos ineficientes, generalmente de complejidad
exponencial. Afortunadamente, esto no quiere decir que la idea, es decir, la ecuación de recurrencia
en que se basan estos algoritmos, deba ser descartada.

La programación dinámica es una técnica de diseño de algoritmos que permite evaluar eficientemente
ecuaciones de recurrencia con algunas propiedades. El enfoque recursivo toma un problema y lo
divide en problemas más pequeños, pero de igual naturaleza, hasta alcanzar un caso base en el que la
recursión termina. A este enfoque se le conoce como enfoque hacia abajo, ya que el tamaño de los
subproblemas decrece. La programación dinámica se caracteriza por su enfoque hacia arriba, esto
es, primero se resuelven los problemas más pequeños (casos base) y a partir de ellos se construyen
las soluciones a subproblemas cada vez más grandes, hasta obtener la solución del problema original.
Este enfoque, como se evidenciará a continuación, permite obtener algoritmo de orden polinómico
cuando el enfoque hacia abajo produce algoritmos de orden exponencial.

La presente Lectura fundamental expone las principales ideas de la programación dinámica tomando
como ejemplo los problemas clásicos de la sucesión de Fibonacci y la distancia de edición. La sección
1 describe el algoritmo recursivo para calcular el enésimo número de la sucesión de Fibonacci junto
con su respectivo análisis, expone las características del algoritmo recursivo que lo hacen ineficiente
y presenta la técnica de programación dinámica y como esta es aplicada al problema discutido. La
sección 2 realiza un estudio similar del problema del cálculo de la distancia de edición entre dos
cadenas de textos.

1. Cálculo del enésimo número de la sucesión de Fibonacci


La sucesión de Fibonacci es definida por la siguiente ecuación de recurrencia:

POLITÉCNICO GRANCOLOMBIANO 2
La anterior ecuación sugiere inmediatamente el siguiente algoritmo recursivo para su evaluación:
fib(n)
if n=0 ∨ n=1 then
return n
return fib(n-1) + fib(n-2)

Como ejemplo, al calcular fib(5) el algoritmo produciría el siguiente árbol de recursión:

Figura 1. Árbol de recursión resultado de la invocación de fib (5)


Fuente: elaboración propia.

1.1. Análisis del algoritmo

Sea T(n) el tiempo requerido por el algoritmo para calcular fib(n). Además del tiempo que toma
la evaluación de la instrucción condicional y la suma, se debe considerar el tiempo que toma la
evaluación de fib(n-1) y fib(n-2). Se tiene entonces que T(n) es descrito por la ecuación de
recurrencia:

Para analizar la ecuación, note la siguiente propiedad:


2T(n-2) ≤ T(n) ≤ 2T(n-1)

POLITÉCNICO GRANCOLOMBIANO 3
Puede probarse que la ecuación de recurrencia con caso inductivo T(n) = 2T(n-2) y caso base Θ(1)
tiene como solución T(n) = Θ(√2ⁿ). Además, la ecuación de recurrencia con caso inductivo T(n)
= 2T(n-1) y caso base Θ(1) tiene como solución T(n) = Θ(2ⁿ). Se tiene entonces que T(n) =
Ω(√2ⁿ) y T(n) = O(2ⁿ). Por tanto, el algoritmo tiene complejidad temporal exponencial.

1.2. Examen de la ineficiencia del algoritmo recursivo

Para entender por qué el algoritmo recursivo para calcular los números de Fibonacci es ineficiente,
considere el árbol de recursión resultante de invocar fib(n) presentado en la figura 2.

Figura 2. Árbol de recursión resultado de la invocación de fib (n)


Fuente: elaboración propia.

Una observación rápida que puede hacerse sobre el árbol es que fib(n-2) es calculado dos veces y
fib(n-3) es calculado tres veces. De manera similar, fib(n-4) será calculado cinco veces y fib(n-5)
ocho veces. En general, fib(n-k) será calculado fib(k+1) veces. Dado que los números de Fibonacci
crecen exponencialmente, fib(1) será calculado un número exponencial de veces.

Note que el número ideal de veces que la función fib debería ser invocada es n + 1, una vez por
cada valor entero en el intervalo [0, n]. Se hace evidente que la función recursiva fib repite cálculos
innecesariamente.

POLITÉCNICO GRANCOLOMBIANO 4
1.3. Algoritmo de programación dinámica

La técnica de programación dinámica es apropiada cuando la solución de un problema puede ser


expresada mediante una ecuación de recurrencia, pero el algoritmo recursivo que se deriva de ella
presenta la propiedad anteriormente ilustrada con los números de Fibonacci: la función recursiva es
invocada múltiples veces con idénticos parámetros (Cormen et al., 2009).

La programación dinámica puede resumirse en dos ideas básicas:

El uso de una estructura de datos para mantener los valores ya calculados: esta estructura sirve como
memoria de los resultados intermedios y así evitar realizar cálculos duplicados.

La evaluación de los subproblemas de menor a mayor tamaño: en programación dinámica, primero


son evaluados los subproblemas correspondientes a los casos base. En segundo lugar, se calcula la
solución a los subproblemas que dependen únicamente de los casos base. Posteriormente, se calcula
la solución a los subproblemas que dependen únicamente de estos últimos y los casos base. De
esta manera, incrementalmente, se obtiene la solución al problema inicial. Este enfoque en general
requiere una estructura iterativa del algoritmo y tiene como consecuencia una reducción considerable
de la complejidad de los algoritmos. Para evidenciar esto último, considere el diagrama presentado
en la figura 3, que muestra el orden en que son evaluados los subproblemas en el cálculo de fib(n).
Compare el número de nodos del diagrama de esta figura con el de la figura 2.

Figura 3. Orden en que son evaluados los subproblemas de la recurrencia de Fibonacci por un algoritmo de programación
dinámica
Fuente: elaboración propia.

POLITÉCNICO GRANCOLOMBIANO 5
La estructura de datos idónea para el problema de Fibonacci es un arreglo F con n + 1 posiciones, tal
que F[i] = fib(i).

El primer paso del algoritmo será llenar las posiciones correspondientes a los casos base:

F[0] := 0
F[1] := 1

Luego, deberán llenarse las posiciones 2 a n, en orden no decreciente. Esto es fácilmente realizable
con una instrucción iterativa:

for i := 2 to n
F[i] := F[i-1] + F[i-2]

La solución al problema quedará almacenada en la posición n del arreglo. El algoritmo completo


obtenido es:

fib(n)
F[0] := 0
F[1] := 1
for i := 2 to n
F[i] := F[i-1] + F[i-2]
return F[n]

Dado que el algoritmo consiste en un ciclo que itera n-1 veces, su complejidad es 𝚯(n)1.

2. Distancia de edición
Como segundo ejemplo de aplicación de programación dinámica, se estudiará el problema de calcular
la distancia de edición entre dos palabras.

¹El análisis del algoritmo supone que una suma de dos números arbitrariamente grandes es (1).

POLITÉCNICO GRANCOLOMBIANO 6
Sean S y T cadenas de caracteres. La distancia de edición entre S y T es el mínimo número de cambios
que deben realizarse sobre la cadena S para obtener la cadena T. Existen tres tipos de cambios
(Dasgupta et al., 2006):

• Insertar en S un carácter.

• Eliminar de S un carácter.

• Sustituir un carácter de S por otro.

Por ejemplo, el mínimo número de cambios requeridos para transformar S = “ALGORITMO” en T =


“AL-KHWARIZMI” 2 es 7:

1. Reemplazar la segunda ‘O’ por una ‘I’. → ALGORITMI

2. Reemplazar la segunda ‘T’ por una ‘Z’. → ALGORIZMI

3. Insertar ‘A’ luego de la ‘O’ → ALGOARIZMI

4. Insertar ‘W’ luego de la ‘O’ → ALGOWARIZMI

5. Reemplazar la ‘O’ por una ‘H’ → ALGHWARIZMI

6. Reemplazar la ‘G’ por una ‘K’ → ALKHWARIZMI

7. Insertar ‘-’ luego de la ‘L’ → AL-KHWARIZMI

A manera de ejercicio, el lector puede verificar que no existe una forma de transformar S en T en un
menor número de cambios.

Para deducir la ecuación de recurrencia que permite calcular la distancia de edición entre S y T se
considerarán dos casos:

El último carácter de S es igual al último carácter de T: cuando las posiciones finales de S y T


contienen elementos idénticos, estos caracteres pueden ser ignorados. El problema se reduce
entonces a calcular la distancia de edición entre S[0..m-2] (la cadena S eliminando el último carácter)
y T[0..n-2] (la cadena T eliminando el último carácter). m y n son los tamaños de las cadenas S y T,
respectivamente.

²Muhammad ibn Musa al-Khwarizmi fue un matemático persa de cuyo nombre se deriva la palabra algoritmo.

POLITÉCNICO GRANCOLOMBIANO 7
Figura 4. Cuando los últimos caracteres de S y T coinciden, el problema se simplifica ignorando estos caracteres
Fuente: elaboración propia.

Esto último puede expresarse mediante la siguiente ecuación:

distancia(S, T) = distancia(S[0..m-2], T[0..n-2])

El último carácter de S es diferente al último carácter de T: cuando los últimos caracteres de S y T


difieren, existen tres formas de convertir la cadena S en T:

i. Reemplazar S[m-1] por T[n-1], y transformar recursivamente S[0..m-2] en T[0..n-2].

ii. Insertar el carácter T[n-1] en S, y transformar recursivamente S[0..m-1] en T[0..n-2].

iii. Eliminar el carácter S[m-1] en S, y transformar recursivamente S[0..m-2] en T[0..n-1].

Figura 5. (a) El último carácter de S es sustituido por el último carácter de T. (b) El último carácter de T es agregado al
final de S. (c) El último carácter de S es eliminado.
Fuente: elaboración propia.

El mínimo número de cambios requeridos en cada alternativa es igual a 1 más el número de cambios
requeridos en cada subproblema:

i. 1 + distancia(S[0..m-2], T[0..n-2])

ii. 1 + distancia(S[0..m-1], T[0..n-2])

iii. 1 + distancia(S[0..m-2], T[0..n-1])

POLITÉCNICO GRANCOLOMBIANO 8
distancia(S, T) es el mínimo valor entre los tres anteriores

distancia(S, T) = 1 + min ( distancia(S[0..m-2], T[0..n-2]),

distancia(S[0..m-1], T[0..n-2]),

distancia(S[0..m-2], T[0..n-1]) )

La ecuación de recurrencia se completa con los casos base (“” representa la cadena vacía)
distancia(“”, T) = n
distancia(S, “”) = m

Se tiene ya una versión completa de la ecuación de recurrencia

2.1. Algoritmo recursivo

De la ecuación de recurrencia de distancia de edición se desprende el siguiente algoritmo recursivo:


distancia(S[0..m-1], T[0..n-1])

if m = 0 then
return n
if n = 0 then
return m
if S[m-1] = T[n-1] then
return distancia(S[0..m-2], T[0..n-2])
return 1 + min( distancia(S[0..m-2], T[0..n-2]),
   distancia(S[0..m-1], T[0..n-2]),
   distancia(S[0..m-2], T[0..n-1]))

Se deja como ejercicio al lector verificar en el árbol de recursión resultante que la función
es invocada múltiples veces con idénticos parámetros. Debido a la repetición de cálculos, el
algoritmo es en peor caso Ω(√3m+n).

POLITÉCNICO GRANCOLOMBIANO 9
2.2. Algoritmo de programación dinámica

La estructura de datos requerida para la evaluación de la recurrencia de distancia de edición mediante


programación dinámica es una matriz bidimensional (m+1) x (n+1):

D[i][j]: Número de cambios requeridos para transformar S[0..i-1] en T[0..j-1].

El algoritmo primero debe rellenar las posiciones de la matriz correspondientes a los casos base,
esto es, la primera fila y columna de la matriz.

for i := 0 to m
D[i][0] := i
for j := 0 to n
D[0][j] := j

Para llenar el resto de la matriz se deben considerar los dos casos inductivos (S[m-1] = T[n-1] y S[m-1]
≠ T[n-1]). La matriz debe ser recorrida de arriba hacia abajo y de izquierda a derecha.

for i := 1 to m
for j := 1 to n
if S[i-1] = T[j-1] then
D[i][j] := D[i-1][j-1]
else
D[i][j] := 1 + min(D[i-1][j-1], D[i][j-1], D[i-1][j]))

El algoritmo resultante es

distancia(S[0..m-1], T[0..n-1])
for i := 0 to m
D[i][0] := i
for j := 0 to n
D[0][j] := j
for i := 1 to m
for j := 1 to n
if S[i-1] = T[j-1] then
D[i][j] := D[i-1][j-1]
else
D[i][j] := 1 + min(D[i-1][j-1], D[i][j-1], D[i-1][j]))
return D[m][n]

Dado que el código consiste en dos ciclos anidados, iterando cada uno sobre una de las cadenas, el
algoritmo es (mn).

POLITÉCNICO GRANCOLOMBIANO 10
3. Resumen
La programación dinámica permite evaluar una ecuación de recurrencia en un orden de complejidad
polinómico, en contraste con el orden súper polinómico del correspondiente algoritmo recursivo,
cuando la evaluación directa de la ecuación exhibe la propiedad de repetir invocaciones con idénticos
parámetros una cantidad considerable de veces.

La programación dinámica se basa en dos ideas fundamentales:

• El uso de una estructura de datos para mantener los valores ya calculados.

• Un enfoque de abajo hacia arriba en la evaluación de los subproblemas. Esto es, resolviendo
primero los subproblemas más pequeños.

Los algoritmos de programación dinámica son, por lo general, iterativos.

El problema del cálculo de enésimo número de la sucesión de Fibonacci puede ser efectiva y
eficientemente resuelto mediante el enfoque de la programación dinámica. De igual forma, el
problema del cálculo de la distancia de edición entre dos cadenas de texto es resuelto mediante la
misma técnica.

POLITÉCNICO GRANCOLOMBIANO 11
Referencias
Cormen, T., Leiserson, C., Rivest, R. y Stein, C. (2009). Introduction to Algorithms (3rd ed.). The MIT
Press.

Dasgupta, S., Papadimitriou, C. H. y Vazirani, U. V. (2006). Algorithms. McGraw-Hill Education.

POLITÉCNICO GRANCOLOMBIANO 12
INFORMACIÓN TÉCNICA

Módulo: Análisis y Verificación de Algoritmos


Unidad 3: Dividir y conquistar y programación dinámica
Escenario 6: Programación dinámica

Autor: Edwin Andrés Niño


Asesor Pedagógico: Edwin Mojica
Diseñador Gráfico: Andrés Felipe Figueroa
Asistente: Eveling Patricia Peñaranda

Este material pertenece al Politécnico Grancolombiano.


Prohibida su reproducción total o parcial.

POLITÉCNICO GRANCOLOMBIANO 13

También podría gustarte