You are on page 1of 6

4.

4 ◆ Algoritmos recursivos 173

Como este algoritmo tiene un solo ciclo “for” que corre de 1 mina en el índice i agregando un término adicional en
a n, el tiempo de máx_sum3 es (n). El tiempo asintótico de lugar de calcular la suma que termina en el índice i des-
este algoritmo no se puede mejorar más. Para encontrar la de el principio. Este último método habría significado
suma máxima de valores consecutivos, por lo menos debe recalcular la suma que termina en el índice i − 1).
verse cada elemento de la sucesión, lo que toma (n).
Comentarios
Resumen de las técnicas de solución de problemas
De acuerdo con [Bentley], el problema analizado en esta sec-
■ Al desarrollar un algoritmo, una buena manera de comen- ción es la versión de una dimensión del problema original de
zar es hacer la pregunta “¿cómo resolvería esto a mano?” dos dimensiones que maneja patrones coincidentes en imá-
■ Al desarrollar un algoritmo, inicialmente tome un enfo- genes digitales. El problema original era encontrar la suma
que directo. máxima en una submatriz rectangular de una matriz n × n de
■ Después de desarrollar un algoritmo, estudie con cuida- números reales.
do el seudocódigo para ver si es posible mejorarlo.
Analice las partes que realizan los cálculos clave para Ejercicios
tener un buen panorama de cómo mejorar la eficiencia 1. Modifique máx_sum3 de manera que calcule no sólo la
del algoritmo. suma máxima de valores consecutivos sino también los
■ Igual que en la inducción matemática, amplíe la solu- índices del primero y último términos de una subsucesión
ción de un problema pequeño a un problema mayor. (En de suma máxima. Si no existe una subsucesión de suma
este problema, se extendió una suma que termina en el máxima (lo que ocurriría, por ejemplo, si todos los valo-
índice i − 1 a una suma que termina en el índice i). res de la sucesión fueran negativos), el algoritmo debe es-
■ No repita cálculos. (En este problema, se extendió una tablecer el primero y último índices iguales a cero.
suma que termina en el índice i − 1 a una suma que ter-

4.4 ➜ Algoritmos recursivos


Una función recursiva (seudocódigo) es una función que se invoca a sí misma. Un algo-
ritmo recursivo es un algoritmo que contiene una función recursiva. La recursión es una
forma poderosa, elegante y natural de resolver una clase amplia de problemas. Un proble-
ma de esta clase se resuelve mediante una técnica de divide y vencerás en la que el problema
WWW se descompone en problemas del mismo tipo que el problema original. Cada subproble-
ma, a su vez, se descompone aún más hasta que el proceso produce subproblemas que se
pueden resolver de manera directa. Por último, las soluciones de los subproblemas se com-
binan para obtener una solución del problema original.

Ejemplo 4.4.1

Recuerde que si n ≥ 1, n! = n(n − 1) · · · 2 · 1 y 0! = 1. Observe que si n ≥ 2, n factorial


se puede escribir “en términos de sí mismo” ya que, si “despegamos” n, el producto que
queda es simplemente (n − 1)!; es decir.
n! = n(n − 1)(n − 2) · · · 2 · 1 = n · (n − 1)!.
Por ejemplo,
5! = 5 · 4 · 3 · 2 · 1 = 5 · 4!.
La ecuación
TABLA 4.4.1 ■ Descomposición n! = n · (n − 1)!,
del problema factorial.
que es verdadera aun para n = 1, muestra cómo descomponer el problema original (calcu-
Problema Problema simplificado lar n!) en subproblemas cada vez más sencillos [calcular (n − 1)!, calcular (n − 2)!, . . .]
5! 5 · 4! hasta que el proceso llega al problema directo de calcular 0!. Las soluciones de estos pro-
4! 4 · 3! blemas se pueden combinar, multiplicando, para resolver el problema original.
3! 3 · 2! Por ejemplo, el problema de calcular 5! se reduce a calcular 4!; el problema de cal-
2! 2 · 1! cular 4! se reduce a calcular 3!, y así sucesivamente. La tabla 4.4.1 resume este proceso.
1! 1 · 0!
0! Ninguno
None
174 Capítulo 4 ◆ Algoritmos

TABLA 4.4.2 ■ Combinación Una vez que el problema de calcular 5! se reduce a resolver subproblemas, la solu-
de subproblemas en el problema ción al problema más sencillo se utiliza para resolver el siguiente subproblema, etcétera,
factorial. hasta que se resuelve el problema original. La tabla 4.4.2 ilustra cómo se combinan los sub-


problemas para calcular 5!.
Problema Solución
0! 1
1! 1 · 0! = 1
2! 2 · 1! = 2 Ahora se escribirá un algoritmo recursivo que calcula factoriales. El algoritmo es una
3! 3 · 2! = 3 · 2 = 6 traducción directa de la ecuación
4! 4 · 3! = 4 · 6 = 24
5! 5 · 4! = 5 · 24 = 120 n! = n · (n − 1)!.

Algoritmo 4.4.2 Cálculo de n factorial


Este algoritmo recursivo calcula n!.
Entrada: n, un entero mayor o igual que 0
Salida: n!
1. factorial(n)
2. if (n == 0)
3. return 1
4. return n * factorial(n − 1)
5. }

Se mostrará ahora cómo calcula n! el algoritmo 4.4.2, para varios valores de n. Si


n = 0, en la línea 3 la función regresa correctamente el valor 1.
Si n = 1, se procede a la línea 4 ya que n 苷 0. Se usa esta función para calcular 0!.
Acabamos de ver que la función calcula 1 como el valor de 0!. En la línea 4, la función cal-
cula correctamente el valor de 1!:

n · (n − 1)! = 1 · 0! = 1 · 1 = 1.

Si n = 2, se procede a la línea 4 ya que n 苷 0. Se emplea esta función para calcular


1!. Acabamos de ver que la función calcula 1 como el valor de 1!. En la línea 4, la función
calcula correctamente el valor de 2!:

n · (n − 1)! = 2 · 1! = 2 · 1 = 2.

Si n = 3 se procede a la línea 4 ya que n 苷 0. Se utiliza esta función para calcular


2!. Acabamos de ver que la función calcula 2 como el valor de 2!. En la línea 4, la función
calcula correctamente el valor de 3!:

n · (n − 1)! = 3 · 2! = 3 · 2 = 6.

Los argumentos anteriores se pueden generalizar usando inducción matemática para


probar que el algoritmo 4.4.2 regresa el valor correcto de n! para cualquier entero no ne-
gativo n.

Teorema 4.4.3 El algoritmo 4.4.2 regresa el valor de n!, n ≥ 0.


Demostración

Paso base (n = 0)
Ya se ha observado que si n = 0, el algoritmo 4.4.2 regresa el valor correcto de 0! (1).
4.4 ◆ Algoritmos recursivos 175

Paso inductivo
Suponga que el algoritmo 4.4.2 regresa el valor correcto de (n − 1)!, n > 0. Ahora su-
ponga que n es la entrada al algoritmo 4.4.2. Como n 苷 0, cuando se ejecuta la función
en el algoritmo 4.4.2 se procede a la línea 4. Por la suposición inductiva, la función cal-
cula el valor correcto de (n − 1)!. En la línea 4, la función calcula el valor correcto de
(n − 1)! ˙ n = n!.
Por lo tanto, el algoritmo 4.4.2 regresa el valor correcto de n! para todo entero
n ≥ 0.

Si se ejecuta en una computadora, en general el algoritmo 4.4.2 no será tan eficien-


te como la versión no recursiva a causa de todas las llamadas recurrentes generales.
Debe haber algunas situaciones en las que una función recursiva no se invoque a
sí misma; de otra manera, se invocaría a sí misma sin detenerse. En el algoritmo 4.4.2, si
n = 0, la función no se invoca a sí misma. Estos valores para los cuales una función recur-
siva no se invoca a sí misma se llaman casos base. Para resumir, toda función recursiva de-
be tener casos base.
Se mostró cómo utilizar la inducción matemática para probar que un algoritmo re-
cursivo calcula el valor que asegura calcular. La relación entre la inducción matemática y
los algoritmos recursivos es profunda. Con frecuencia, una prueba por inducción matemá-
tica se considera como un algoritmo para calcular un valor o llevar a cabo una construcción
en particular. El paso base de una prueba por inducción matemática corresponde a los ca-
sos base de una función recursiva, y el paso inductivo de una prueba por inducción mate-
mática corresponde al paso de una función recursiva donde la función se invoca a sí misma.
En el ejemplo 1.7.6 se demostró usando inducción matemática que, dado un tablero
deficiente de n × n (un tablero que le falta un cuadro), donde n es una potencia de 2, se
puede enlosar el tablero con trominos derechos (tres cuadros que forman una “L”; vea la
figura 1.7.3). Ahora se traduce la prueba inductiva en un algoritmo recursivo para construir
un enlosado de una tablero deficiente de n × n con trominos derechos, donde n es una po-
tencia de 2.

Algoritmo 4.4.4 Enlosado de un tablero deficiente con trominos


Este algoritmo construye un enlosado con trominos derechos de un tablero deficiente de
WWW n × n donde n es una potencia de 2.
Entrada: n, una potencia de 2 (el tamaño del tablero); y la localización L del cua-
dro que falta
Salida: Enlosado de un tablero deficiente de n × n
1. enlosar(n, L){
2. if(n == 2){
// el tablero es un tromino derecho T
3. enlosar con T
4. return
5. }
6. dividir el tablero en cuatro tableros de (n/2) × (n/2)
7. rotar el tablero para que el cuadro que falta esté en el cuadrante superior izquierdo
8. colocar un tromino derecho en el centro // como en la figura 1.7.5
// considere cada cuadro cubierto por el tromino central como
// faltante, y denote los cuadros faltantes por m1, m2, m3, m4
9. enlosar(n/2, m1)
10. enlosar(n/2, m2)
11. enlosar(n/2, m3)
12. enlosar(n/2, m4)
13. }
176 Capítulo 4 ◆ Algoritmos

Usando el método de la prueba del teorema 4.4.3, se puede probar que el algoritmo
4.4.4 es correcto (ejercicio 4).
Se presenta un último ejemplo de un algoritmo recursivo.

Ejemplo 4.4.5


Un robot puede dar pasos de 1 o 2 metros. Se escribe un algoritmo para calcular el núme-
ro de maneras que el robot puede caminar n metros. Como ejemplos:

Distancia Secuencia de pasos Número de maneras de caminar

1 1 1
2 1, 1 or 2 2
3 1, 1, 1 or 1, 2 or 2, 1 3
4 1, 1, 1, 1 or 1, 1, 2 5
or 1, 2, 1 or 2, 1, 1 or 2, 2

Sea walk(n) el número de maneras en que el robot puede caminar n metros. Se ha ob-
servado que
walk(1) = 1, walk(2) = 2.
Ahora suponga que n > 2. El robot comienza por dar un paso de 1 metro o un paso de 2
metros. Si el robot da primero un paso de 1 metro, queda una distancia de n − 1 metros;
pero, por definición, el resto de la caminata se puede completar en walk(n − 1) maneras.
De manera similar, si el robot da primero un paso de 2 metros, queda una distancia de
n − 2 metros y, en este caso, el resto de la caminata puede completarse en walk(n − 2) ma-
neras. Como la caminata debe comenzar con un paso de 1 metro o bien de 2 metros, todas
las maneras de caminar n metros están tomadas en cuenta. Se obtiene la fórmula
walk(n) = walk(n − 1) + walk(n − 2).
Por ejemplo,
walk(4) = walk(3) + walk(2) = 3 + 2 = 5.
Se puede escribir un algoritmo para calcular walk(n) traduciendo la ecuación
walk(n) = walk(n − 1) + walk(n − 2)


directamente en un algoritmo. Los casos base son n = 1 y n = 2.

Algoritmo 4.4.6 Caminata de un robot


Este algoritmo calcula la función definida por

1, n=1
walk(n) = 2, n=2
walk(n − 1) + walk(n − 2) n > 2.

Entrada: n
Salida: walk(n)
walk(n){
if(n == 1 ∨ n == 2)
return n
return walk(n − 1) + walk(n − 2)
}

Usando el método de la prueba del teorema 4.4.3, se puede probar que el algoritmo
4.4.6 es correcto (vea el ejercicio 7).
La sucesión
walk(1), walk(2), . . . ,
4.4 ◆ Algoritmos recursivos 177

cuyos valores comienzan


1, 2, 3, 5, 8, 13, . . . ,

WWW está relacionada con la sucesión de Fibonacci. La sucesión de Fibonacci {fn} está defini-
da por las ecuaciones
f1 = 1
f2 = 1
fn = fn −1 + fn −2 para toda n ≥ 3.
La sucesión de Fibonacci comienza
1, 1, 2, 3, 5, 8, 13, . . . ,
Como
walk(1) = f2, walk(2) = f3,
y
walk(n) = walk(n − 1) + walk(n − 2), fn = fn −1 + fn −2 para toda n ≥ 3,
Se sigue que
walk(n) = fn+1 para toda n ≥ 1.
(Este argumento se puede formalizar usando inducción matemática; vea el ejercicio 8).
La sucesión de Fibonacci recibe su nombre en honor a Leonardo Fibonacci (alrede-
dor de 1170-1250), un comerciante y matemático italiano. La sucesión surgió por un enig-
ma referente a conejos (vea los ejercicios 18 y 19). Después de regresar de Oriente en 1202,
Fibonacci escribió su trabajo más famoso, Liber Abaci (disponible en una traducción al in-
glés de [Sigler]), que, además de contener lo que ahora se conoce como sucesión de Fibo-
nacci, defendía el uso del sistema de números hindú-arábigos. Este libro fue una de las
influencias principales para adoptar el sistema decimal en Europa Occidental. Fibonacci
firmó muchos de sus trabajos como “Leonardo Bigollo”. Bigollo se traduce como “viaje-
ro” o “cabeza dura”. Existe evidencia de que Fibonacci disfrutaba que sus contemporáneos
los consideraran cabeza dura por recomendar el uso de un nuevo sistema numérico.
La sucesión de Fibonacci surge en lugares inesperados. La figura 4.4.1 muestra una pi-
ña de pino con 13 espirales en sentido de la manecillas de reloj y 8 espirales en sentido con-
trario. Muchas plantas distribuyen sus semillas tan parejo como les es posible, con lo que
maximizan el espacio para cada semilla. El patrón en el que el número de espirales es un nú-

Figura 4.4.1 Piña de pino. Tiene


13 espirales en sentido de las
manecillas del reloj (marcadas con
hilo blanco) y 8 espirales en sentido
contrario (marcadas con hilo
oscuro). [Foto del autor; piña de
pino cortesía de André Berthiaume y
Sigrid (Anne) Settle].
178 Capítulo 4 ◆ Algoritmos

mero de Fibonacci proporciona la distribución más uniforme (vea [Naylor, Mitchison]). En la


sección 5.3, la sucesión de Fibonacci aparece en el análisis del algoritmo euclideano.

Ejemplo 4.4.7


Utilice inducción matemática para demostrar que


n
f k = f n+2 − 1 para toda n ≥ 1.
k=1

Para el paso base (n = 1), debemos demostrar que


1
f k = f 3 − 1.
k=1
1
Como k=1 f k = f 1 = 1 y f3 − 1 = 2 − 1 = 1, la ecuación se verifica.
Para el paso inductivo, se supone el caso n

n
f k = f n+2 − 1
k=1

y se prueba el caso n + 1


n+1
f k = f n+3 − 1.
k=1

Ahora

n+1 
n
fk = f k + f n+1
k=1 k=1
= ( f n+2 − 1) + f n+1 por la suposición inductiva
= f n+1 + f n+2 − 1
= f n+3 − 1.

La última igualdad es cierta por la definición de los números de Fibonacci:

fn = fn −1 + fn −2 para toda n ≥ 3.

Puesto que se han verificado el paso base y el paso inductivo, la ecuación dada es verdade-


ra para toda n ≥ 1.

Sugerencias para resolver problemas

Una función recursiva es una función que se invoca a sí misma. La clave para escribir una
función recursiva es encontrar una instancia más pequeña del problema dentro del proble-
ma más grande. Por ejemplo, se puede calcular n! de manera recursiva porque n! = n ˙ (n
− 1)! para toda n ≥ 1. La situación es análoga al paso inductivo de la inducción matemá-
tica cuando debemos encontrar un caso más pequeño (como el caso n) dentro del caso gran-
de (caso n + 1).
Como otro ejemplo, el enlosado del tablero deficiente de n × n con trominos cuan-
do n es una potencia de 2 se puede hacer de manera recursiva porque es posible encontrar
cuatro subtableros de (n/2) × (n/2) dentro del tablero original de n × n. Observe la simi-
litud del algoritmo para enlosar con el paso inductivo de la prueba de que todo tablero de-
ficiente de n × n se puede enlosar con trominos cuando n es una potencia de 2.
Para probar una afirmación acerca de los números Fibonacci, use la ecuación
fn = fn −1 + fn −2 para toda n ≥ 3.
Con frecuencia, la demostración usará inducción matemática y la ecuación anterior
(vea el ejemplo 4.4.7).