Está en la página 1de 44

Tema 1

Estructura de datos
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Índice Pág.

1
1. Algoritmos recursivos
3
1.1 Objetivo
3
1.2 Introducción
7
1.3 Función Factorial
13
1.4 Función de Ackerman
21
1.5 Torres de Hanoi
28
1.6 Recorrido de la pieza caballo
42
1.7 Ejercicios de aplicación

43
1.8 Bibliografía

1.9 Recursos complementarios 44

2
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Objetivo
Entender el concepto de recursividad y diseñar y desarrollar programas en lenguaje
C++ y Python que utilicen recursividad para ser aplicados en algoritmos inteligentes.

Introducción
En ciencias de la computación, es una forma de reducir y solventar problemas.
De hecho, recursión es una de las ideas centrales de ciencia de computación.
Resolver un problema mediante recursión significa que la solución depende de
las soluciones de pequeñas instancias del mismo problema.

El poder de la recursión evidentemente se fundamenta en la posibilidad de


definir un conjunto infinito de objetos con una declaración finita. Igualmente, un
número infinito de operaciones computacionales puede describirse con un
programa recursivo finito, incluso en el caso de que este programa no contenga
repeticiones explícitas."

La mayoría de los lenguajes de programación dan soporte a la recursión


permitiendo a una función llamarse así misma desde el texto del programa. Los
lenguajes imperativos definen las estructuras de loops como while y for que son
usadas para realizar tareas repetitivas. Algunos lenguajes de programación
funcionales no definen estructuras de loops, sino que posibilitan la recursión
llamando código de forma repetitiva. La teoría de la computabilidad ha
demostrado que estos dos tipos de lenguajes son matemáticamente
equivalentes, es decir que pueden resolver los mismos tipos de problemas,
aunque los lenguajes funcionales carezcan de las típicas estructuras while y for.

Para dar soporte a la recursividad, se necesita que el compilador administre


automáticamente una pila, misma que se define como una secuencia de
elementos del mismo tipo, en la cual el último elemento en ingresar, es el primero
en salir. A este tipo de estructura de datos también se les conoce como
estructuras LIFO (last input first output). Existen muchos ejemplos en la vida real:
a) Cuando en un restaurante se tiene un apilamiento de platos, en el que el último
plato que se puso es el que queda disponible para su uso; b) es el caso de una
alimentadora de una pistola, en la cual se van empujando los proyectiles, en este
caso el disponible para disparar es el último que se ingresó a la alimentadora.

3
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

1. Algoritmos recursivos
Existen dos maneras de que un algoritmo sea recursivo: directa e indirecta.
La recursión directa se da cuando el algoritmo se define y se llama a si mismo.
La recursión indirecta ocurre cuando un algoritmo A, llama a un algoritmo B y
viceversa, esto puede extenderse para más de dos algoritmos. En este curso, se
tratará solo el primer caso.

Recursividad
La recursividad o recursión es una propiedad que puede tener una función de
llamarse a sí misma bien sea directamente o mediante otra función. La
recursividad se puede utilizar como una solución alternativa a la iteración.
Una solución recursiva a un problema de computación, es por lo general
menos eficiente en términos de tiempo de computadora que una solución
utilizando iteraciones. Sin embargo, en muchas ocasiones el uso de la recursión
permite a los programadores específicar soluciones más naturales, intuitivas y
sencillas que en caso contrario serían más díficiles de resolver. Por esta razón,
la recursividad representa una herramienta poderosa e importante en la
resolución de problemas de computación.
En términos generales, si por ejemplo se tienen dos funciones: funcion1 y
funcion2. La organización de un programa con funciones no recursivas podría
ser:

Funcion_1(parametros)
{
Linea 1
Linea 2
Linea n
}
Funcion_2(parametros)
{
Funcion_1(parametros);
}

Una solución usando recursividad podría ser:

Funcion_1(parametros)
{

4
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

funcion1(parametros);
}

En programación una función que contiene sentencias entre las que se


encuentra al menos una que llama a la propia función se dice que es recursiva.
Un requisito para que un algoritmo recursivo sea correcto es que no genere una
secuencia infinita de llamadas sobre sí mismo. Si se genera una secuencia de
este tipo no puede terminar nunca.

Así, la definición recursiva de una función F(n) debe incluir un componente base
(condición de salida) en el que la función F(n) se defina directamente (en otras
palabras, no de manera recursiva) para uno o más valores de n.

Hay varios factores a considerar en la decisión sobre si usar o no una solución


recursiva en un problema. La solución recursiva puede necesitar un considerable
gasto de memoria para las múltiples llamadas a la función, puesto que deben
almacenarse las direcciones de vueltas y copias de las variables locales y
temporales.

Si un programa ha de ejecutarse frecuentemente (como un compilador) y/o debe


ser muy eficiente, el uso de programación recursiva puede no ser una buena
elección. Sin embargo, para algunos problemas una solución recursiva es más
natural y sencilla de escribir para el programador.

Si el costo del tiempo y el espacio de memoria de las computadoras disminuye


y aumenta el costo del tiempo del programador, puede ser útil usar soluciones
recursivas.

En general, si la solución no recursiva no es mucho más larga que la versión


recursiva, usar la no recursiva.
La iteración y la recursividad cumplen con el mismo objetivo: ejecutar un bloque
de sentencias n veces o que con una condición de fin adecuada lo termine.

Hay problemas que aparecen más naturales con iteraciones y otros con
recursividad, por ejemplo, en problemas con contadores y sumatorias lo natural
es la iteración, pero en problemas en los que distinguimos un caso base y uno
general, como factorial, Fibonacci, MCD lo natural es la recursión.

5
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Existe otro factor muy importante a tener en cuenta: la eficiencia. Hay problemas
altamente complejos en lo que es casi imposible aplicar iteración.

En matemáticas y también en muchas situaciones y circunstancias de la vida


cotidiana, pueden tener un carácter recursivo.

Ejemplos:

1. Los números naturales se pueden definir de la siguiente forma: 0 es un número


natural y el sucesor de un número natural es también un número natural. La
condición de salida o componente base puede ser cuando el numero natural sea
0.

2. El factorial de un número natural n, es 1 si dicho número es 0, o caso contrario,


n multiplicado por el factorial del número n-1. La condición de salida o
componente base puede ser cuando el numero natural sea 1.

3. La n-ésima potencia de un número x, es 1 si n es igual a 0, o cuando n es


mayor que 0, es el producto de x por la potencia (n-1)-ésima de x. La condición
de salida o componente base puede ser cuando el numero sea 0.

1.1. Función Factorial

Matemáticamente, La función factorial es una fórmula representada por el


signo de exclamación “!”. En la fórmula Factorial se deben multiplicar todos los
números enteros y positivos que hay entre el número que aparece en la fórmula
y el número 1.

Es muy fácil, aquí tienes un ejemplo:


7! = 1 * 2 * 3 * 4 * 5 * 6 * 7 = 5.040

En esta fórmula el número 7 se llamaría 7 factorial o factorial de 7 y


multiplicaremos todos los números que aparecen en la fórmula hasta llegar al 1.

Ejemplos de fórmulas factoriales


1! = 1 * 1 = 1
3! = 1 * 2 * 3 = 6
10! = 1 * 2 * 3 … 8 * 9 * 10 = 3.628.800

6
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Qué pasa con la 0 factorial, ¿cómo calcularlo? Si volvemos a la definición de


función factorial podemos ver que no tiene sentido aplicarla en el caso del “0”.
No existen números positivos anteriores al 0 por lo que 0 x 0 = 0.

No obstante, se ha acordado que en el caso de 0 factorial el resultado será igual


a 1: Solución: 0! = 0 x 0 = 1

1.1.1 Algoritmo del factorial:

Figura 1.1: Algoritmo del Factorial, Tomado de: https://byspel.com/algoritmo-que-calcula-el-


factorial-de-un-numero-en-pseint/

Analizando el código anterior iniciamos la variable del factorial “f“, en “1”,


posteriormente solicitamos el número que deseamos obtener el factorial; usando
un ciclo para hacer las iteraciones, estas son necesarias para multiplicar todos
los números.

Como salida obtendríamos

*** Ejecución Iniciada. ***


Ingrese un número:
>7
El factorial de 7 es: 5040
*** Ejecución Finalizada. ***

1.1.2 Código fuente y resultados

A continuación, aprenderemos a hacer una función de factorial en c++


mediante la estructura de control for y también aplicando recursividad. Luego

7
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

veremos cuáles son las ventajas de usar recursividad sobre algunas estructuras
de control iterativas. Primero veamos en qué consiste la función factorial. El
factorial de un número es la multiplicación de los números que están
comprendidos entre 1 hasta dicho número. Para expresar el factorial se suele
utilizar la notación n!. Así la definición es la siguiente:

n! = 1 x 2 x 3 x 4 x 5 x ... x (n-1) x n.

Estructura de control for: Un bucle for hace que una instrucción o bloque de
instrucciones se repita un número determinado de veces mientras se cumpla la
condición.

For (inicializacion; condicion; incremento/decremento) {


instrucción 1;
...........
instrucción N;
}

A continuación de la palabra for y entre paréntesis debe haber siempre tres


zonas separadas por punto y coma:

zona de inicialización
zona de condición
zona de incremento ó decremento.

En alguna ocasión puede no ser necesario escribir alguna de ellas. En ese


caso se dejarían en blanco, pero los punto y coma deberían aparecer.

El funcionamiento de un bucle for es el siguiente:


Se inicializa la variable o variables de control.
Se evalúa la condición.
Si la condición es cierta se ejecutan las instrucciones. Si es falsa,
finaliza la ejecución del bucle y continúa el programa en la siguiente
instrucción después del for
Se actualiza la variable o variables de control
(incremento/decremento)
Se pasa al punto 2.

8
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Ahora veremos el ejemplo del factorial implementado en c++:

#include
using namespace std;
int main(void){
int i;
int fact=1;
int numero;
cout<<"ingresa un numero: ";
cin>>numero;
if(numero<0) fact =0;
else if(numero==0) fact=1;
else{
for (i = 1; i <= numero; i++){
fac = fact*i;
}
}
cout<<"Factorial de "<
system("pause");
}

Como podemos observar en el ejemplo, primero inicializamos nuestras


variables i, fact y numero. Después de ingresar el número por consola
procedemos a evaluarlo. Si este es menor a cero, entonces el factorial también
será 0, si es cero nuestro factorial será 1, caso contrario procedemos a calcular
el factorial con el for.

En nuestro for inicializamos nuestra variable iterativa i en 1, esta se


incrementará en 1 por cada iteración hasta que sea igual a número, dentro de
las instrucciones por cada iteración se efectúa una multiplicación de todos los
valores que va tomando i, en nuestro caso de 1 hasta el número y los va
almacenando en la variable fact. Una vez terminado el programa, mostramos en
pantalla nuestro resultado.

9
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

1.1.3 Factorial Recursivo.

Se plantea la definición de la función factorial recursiva:

1, para x = 0 o x =1
Factorial(x) =
x * Factorial(x – 1), para x > 1

A continuación, veremos el ejemplo de la función factorial usando recursividad


en C++:

// Factorial
#include <iostream>
using namespace std;
double FacR(int X) {
if (X < 2) return 1;
return X * FacR(X - 1);
}
int main()
{
int n;
for (;;) {
cout << "Ingresar un entero (salir < 0 o > 20): ";
cin >> n;
if (n < 0 || n > 20) break;
cout << "Factorial(" << n << ") = " << FacR(n) << endl;
}
return 0;
}

Los resultados de la corrida del programa se indican a continuación:

10
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Seguidamente se plantea el código fuente en Python:

# Factorial.py

def FacR(X):
if X < 2:
return 1
return X * FacR(X - 1)

# Funcion principal:
while 1:
n = int(input('Ingresar un entero (salir < 0 o > 20): '))
if n < 0 or n > 20:
break
print('Factorial(', n, ') = ', FacR(n))

Resultados de la corrida:

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC


v.1914 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.

11
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

>>>
====== RESTART: C: \EstructuraDeDatos2020\Python\Factorial.py
======
Ingresar un entero (salir < 0 o > 20): 0
Factorial( 0 ) = 1
Ingresar un entero (salir < 0 o > 20): 3
Factorial( 3 ) = 6
Ingresar un entero (salir < 0 o > 20): 5
Factorial( 5 ) = 120
Ingresar un entero (salir < 0 o > 20): 10
Factorial( 10 ) = 3628800
Ingresar un entero (salir < 0 o > 20): 21
>>>

1.1.4 Explicación del funcionamiento de la pila:

En este caso se realiza la prueba de escritorio para FacR(5):

Como se puede observar, la pila crece hacia abajo indicada con la


flecha en negro y se elimina hacia arriba indicada con la flecha en
rojo. Al final FacR(5) retorna 120.

1.2. Función de Ackerman

Se plantea la definición de la función de Acherman recursiva:

12
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

y + 1, para x = 0 [1]
Ack(x, y) = Ack(x – 1, 1) para y = 0 [2]
Ack(x – 1, Ack(x, y – 1)), para x > 0, y > 0 [3 externa] [3’ interna]

Antes de plantear los programas, se realiza el siguiente análisis:

Para x = 1:
Ack(1, 0) = Ack(0, 1) = (1) + 1 = 2
Ack(1, 1) = Ack(0, Ack(1, 0)) = Ack(1, 0) + 1 = (2) + 1 = 3
Ack(1, 2) = Ack(0, Ack(1, 1)) = Ack(1, 1) + 1 = (3) + 1 = 4

Por induction:
Ack(1, y) = Ack(0, Ack(1, y-1)) = Ack(1, y-1) + 1 = (y + 1) + 1 = y + 2 [4]

Para x = 2:
Ack(2, 0) = Ack(1, 1) = (1) + 2 = 3
Ack(2, 1) = Ack(1, Ack(2, 0)) = Ack(1, 3) = (3) + 2 = 5
Ack(2, 2) = Ack(1, Acl(2, 1)) = Ack(1, 5) = (5) + 2 = 7

Por induction:
Ack(2, y) = Ack(1, Ack(2, y-1)) = Ack(1, 2y + 1) = (2y + 1) + 2 = 2y + 3 [5]

Para x = 3:
Ack(3, 0) = Ack(2, 1) = 2(1) + 3 = 2(0 + 3) – 3 = 5
Ack(3, 1) = Ack(2, Ack(3, 0)) = Ack(2, 2(0 + 3) – 3) = 2(2(0 + 3) – 3) + 3 = 2(1 + 3) – 3
Ack(3, 2) = Ack(2, Ack(3, 1)) = Ack(2, 2(1 + 3) – 3) = 2(2(1 + 3) – 3) + 3 = 2(2 + 3) – 3

Por induction:
Ack(3, y) = Ack(2, Ack(3, y-1)) = Ack(2, 2(y + 3) – 3) = 2(2(y-1 + 3) – 3) = 2(y + 3) – 3
[6]

Para x = 4:
2
Ack(4, 0) = Ack(3, 1) = 2(1 + 3) – 3 = 22 − 3 = 24 – 3 = 13
2 2 22
Ack(4, 1) = Ack(3, Ack(4, 0)) = Ack(3,22 − 3) = 2(22 − 3) + 3 = 22 −3

Por induction: …
22
Ack(4, y) = Ack(3, Ack(4, y – 1)) = 22 − 3, la exponenciación se extiende (y +
4) veces

Programa en C++:

// Ackermann
#include <iostream>
#define MaxX 4

13
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

#define MaxY 20
using namespace std;
// Prototipos
long Ackermann(long X, long Y);
// Funciones
int main()
{
long X, Y;
for (;;) {
cout << "Ingresar X (salir < 0 o > " << MaxX << "): ";
cin >> X;
if (X < 0 || X > MaxX) break;
cout << "Ingresar Y (salir < 0 o > " << MaxY << "): ";
cin >> Y;
if (Y < 0 || Y > MaxY) break;
cout << "Ackermann(" << X << ", " << Y << ") = " <<
Ackermann(X, Y) << endl;
}
return 0;
}

long Ackermann(long X, long Y)


{ // Ackermann
if (X == 0)
return (Y + 1);
else if (Y == 0)
return Ackermann(X - 1, 1);
else
return Ackermann(X - 1,Ackermann(X, Y - 1));
} // Ackermann

Resultados de la corrida:

14
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Programa en Python:

# Ackerman.py
MaxX = 4
MaxY = 20

def Ack(X, Y):


if X == 0:

15
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

return Y + 1
elif Y == 0:
return Ack(X - 1, 1)
else:
return Ack(X - 1, Ack(X, Y - 1))

# Funcion principal:
while 1:
x = int(input('Ingresar x (salir < 0 o > ' + str(MaxX)+ '): '))
if x < 0 or x > MaxX:
break
y = int(input('Ingresar y (salir < 0 o > ' + str(MaxY)+ '): '))
if y < 0 or y > MaxY:
break
print('Ackerman(', x, ', ', y, ') = ', Ack(x, y))

Resultados de la corrida del programa:


Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC
v.1914 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>
===== RESTART: C:/EstructuraDeDatos2020/Python/Ackerman.py
=====
Ingresar x (salir < 0 o > 4): 1
Ingresar y (salir < 0 o > 20): 0
Ackerman( 1 , 0 ) = 2
Ingresar x (salir < 0 o > 4): 1
Ingresar y (salir < 0 o > 20): 1
Ackerman( 1 , 1 ) = 3
Ingresar x (salir < 0 o > 4): 1
Ingresar y (salir < 0 o > 20): 2
Ackerman( 1 , 2 ) = 4
Ingresar x (salir < 0 o > 4): 1
Ingresar y (salir < 0 o > 20): 20
Ackerman( 1 , 20 ) = 22
Ingresar x (salir < 0 o > 4): 2
Ingresar y (salir < 0 o > 20): 0
Ackerman( 2 , 0 ) = 3
Ingresar x (salir < 0 o > 4): 2
Ingresar y (salir < 0 o > 20): 1
Ackerman( 2 , 1 ) = 5

16
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Ingresar x (salir < 0 o > 4): 2


Ingresar y (salir < 0 o > 20): 2
Ackerman( 2 , 2 ) = 7
Ingresar x (salir < 0 o > 4): 2
Ingresar y (salir < 0 o > 20): 3
Ackerman( 2 , 3 ) = 9
Ingresar x (salir < 0 o > 4): 2
Ingresar y (salir < 0 o > 20): 20
Ackerman( 2 , 20 ) = 43
Ingresar x (salir < 0 o > 4): 3
Ingresar y (salir < 0 o > 20): 0
Ackerman( 3 , 0 ) = 5
Ingresar x (salir < 0 o > 4): 3
Ingresar y (salir < 0 o > 20): 1
Ackerman( 3 , 1 ) = 13
Ingresar x (salir < 0 o > 4): 3
Ingresar y (salir < 0 o > 20): 2
Ackerman( 3 , 2 ) = 29
Ingresar x (salir < 0 o > 4): 3
Ingresar y (salir < 0 o > 20): 3
Ackerman( 3 , 3 ) = 61
Ingresar x (salir < 0 o > 4): 3
Ingresar y (salir < 0 o > 20): 5
Ackerman( 3 , 5 ) = 253
Ingresar x (salir < 0 o > 4): 4
Ingresar y (salir < 0 o > 20): 0
Ackerman( 4 , 0 ) = 13
Ingresar x (salir < 0 o > 4): 4
Ingresar y (salir < 0 o > 20): 1
Traceback (most recent call last):
File "C:/EstructuraDeDatos2020/Python/Ackerman.py", line 21, in
<module>
print('Ackerman(', x, ', ', y, ') = ', Ack(x, y))
File "C:/EstructuraDeDatos2020/ Python /Ackerman.py", line 11, in
Ack
return Ack(X - 1, Ack(X, Y - 1))
File "C:/EstructuraDeDatos2020/ Python /Ackerman.py", line 11, in
Ack
return Ack(X - 1, Ack(X, Y - 1))

17
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

File "C:/EstructuraDeDatos2020/ Python /Ackerman.py", line 11, in


Ack
return Ack(X - 1, Ack(X, Y - 1))
[Previous line repeated 988 more times]
File "C:/EstructuraDeDatos2020/ Python /Ackerman.py", line 9, in
Ack
return Ack(X - 1, 1)
File "C:/EstructuraDeDatos2020/ Python /Ackerman.py", line 6, in
Ack
if X == 0:
RecursionError: maximum recursion depth exceeded in comparison
>>>

1.2.4 Explicación del funcionamiento de la pila:

En este caso se realiza la prueba de escritorio para Ack(2, 3):

18
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

19
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

1.3. Torres de Hanoi

Definición del problema:

Se tiene el siguiente juego para simular el problema de las Torres de Hanoi,


mismo que consiste en tener tres postes izquierdo, medio y derecho y un grupo
de discos de diferente tamaño los cuales se encuentran ubicados de abajo hacia
arriba de mayor a menor tamaño en uno de los tres postes denominado poste
origen; seguidamente deben moverse todos los discos a cualquiera de los otros
dos postes denominado destino y el otro queda como auxiliar; los movimientos
de los discos deben ajustarse a las siguientes reglas:
a) Solo puede moverse un disco a la vez.
b) Un disco de mayor tamaño no puede quedar sobrepuesto a otro de menor
tamaño.
c) Debe realizarse en la menor cantidad de movimientos.
d) Uno de los postes queda como auxiliar.
Siguiendo la metodología para resolver problemas en la computadora,
implementar un programa en C++ y en Python para ingresar el número de discos
y otros dos números que representan el nombre del poste origen y destino (0 →
Izquierdo; 1 → Medio; 2 → Derecho) y a continuación generar y desplegar la
secuencia de pasos para resolver el problema. Hacer la prueba de escritorio
MoverDiscos(3, 0, 2, 1), es decir, mover 3 discos desde el poste izquierdo hacia
el poste derecho, teniendo como auxiliar el poste medio, mostrando el estado
de la pila. Hacer el proceso repetitivo.

Análisis:
Se Plantean dos casos: (1) mover un disco desde el poste izquierdo hacia el
poste derecho y (2) mover tres discos desde el poste izquierdo hacia el poste
derecho, cuya solución grafica es:

20
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Esta última gráfica, nos conduce al diseño en seudocódigo:

Diseño en seudocódigo:

21
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Procedimiento MoverDiscos(N, Org, Dst, Aux)


Inicia MoverDiscos
Si N = 1 Entonces
PasarDisco(N, Org, Dst);
Caso_Contrario
Inicia MayorQue1
MoverDiscos(N - 1, Org, Aux, Dst);
PasarDisco(N, Org, Dst);
MoverDiscos(N - 1, Aux, Dst, Org);
Fin Proceso MayorQue1
Fin Procedimiento MoverDiscos;

Prueba de escritorio:

22
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Programa en C++:

// TorresHanoi
#include <iostream>
using namespace std;
// Prototipado:
void MoverDiscos(int, int, int, int);
void PasarDisco(int, int, int);
void EscribirPoste(int);

// Definicion de funciones:
int main()
{
int n, origen, destino;
while (1) {
cout << "Ingresar el numero de discos (salir < 1 o > 10): ";
cin >> n;
if (n < 1 || n > 10) break;
cout << "Ingresar el poste origen:\n";
cout << "0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro
valor): ";
cin >> origen;
if (origen < 0 || origen > 2) break;
cout << "Ingresar el poste destino:\n";
cout << "0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro
valor): ";
cin >> destino;
if (destino < 0 || destino > 2 || origen == destino) break;
cout << "Secuencia de pasos para mover " << n << " discos desde el
poste ";
EscribirPoste(origen);
cout <<" hacia el poste ";
EscribirPoste(destino);
cout << endl;
MoverDiscos(n, origen, destino, 3 - origen - destino);
cout << endl;
}
return 0;
}

void MoverDiscos(int nm, int org, int dst, int aux) {


if (nm == 1)
PasarDisco(nm, org, dst);
else {
MoverDiscos(nm - 1, org, aux, dst);

23
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

PasarDisco(nm, org, dst);


MoverDiscos(nm - 1, aux, dst, org);
}
}

void PasarDisco(int n, int org, int dst) {


cout << "Pasar el disco " << n << " desde el poste ";
EscribirPoste(org);
cout << " hacia el poste ";
EscribirPoste(dst);
cout << endl;
}

void EscribirPoste(int p) {
switch (p) {
case 0: cout << "izquierdo"; break;
case 1: cout << "medio"; break;
case 2: cout << "derecho"; break;
}
}

Resultados de la corrida:

24
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Programa en C++:

# TorresHanoi.py
def NomPoste(argument):
Poste = {
0: "Izquierdo",
1: "Medio",
2: "Derecho"
}
return Poste.get(argument, "N/A")

25
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

def PasarDisco(n, org, dst):


print( "Pasar el disco " + str( n)+ " desde el poste " + NomPoste(org)+ " hacia el
poste " + NomPoste(dst))

def MoverDiscos(nm, org, dst, aux):


if (nm == 1):
PasarDisco(nm, org, dst)
else :
MoverDiscos(nm - 1, org, aux, dst)
PasarDisco(nm, org, dst)
MoverDiscos(nm - 1, aux, dst, org)

# Funcion Principal:
while (1):
n = int(input( "Ingresar el numero de discos (salir < 1 o > 10): "))
if (n < 1 or n > 10):
break
print("Ingresar el poste origen:")
origen = int(input( "0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro
valor): "))
if (origen < 0 or origen > 2):
break
print("Ingresar el poste destino:")
destino = int(input( "0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro
valor): "))
if (destino < 0 or destino > 2 or origen == destino):
break
print( "Secuencia de pasos para mover " + str( n) + " discos desde el poste " +
NomPoste(origen) + " hacia el poste " + NomPoste(destino))
MoverDiscos(n, origen, destino, 3 - origen - destino)
print("")

Resultados de la corrida:

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit


(Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>
===== RESTART: C:/FunProg/May_Sep2020/NRC6492/II
Parcial/TorresHanoi1.py =====
Ingresar el numero de discos (salir < 1 o > 10): 3
Ingresar el poste origen:
0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro valor): 0
Ingresar el poste destino:

26
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro valor): 2
Secuencia de pasos para mover 3 discos desde el poste Izquierdo hacia el poste
Derecho
Pasar el disco 1 desde el poste Izquierdo hacia el poste Derecho
Pasar el disco 2 desde el poste Izquierdo hacia el poste Medio
Pasar el disco 1 desde el poste Derecho hacia el poste Medio
Pasar el disco 3 desde el poste Izquierdo hacia el poste Derecho
Pasar el disco 1 desde el poste Medio hacia el poste Izquierdo
Pasar el disco 2 desde el poste Medio hacia el poste Derecho
Pasar el disco 1 desde el poste Izquierdo hacia el poste Derecho

Ingresar el numero de discos (salir < 1 o > 10): 4


Ingresar el poste origen:
0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro valor): 2
Ingresar el poste destino:
0 --> Izquierdo, 1 --> Medio, 2 --> derecho (salir --> otro valor): 1
Secuencia de pasos para mover 4 discos desde el poste Derecho hacia el poste
Medio
Pasar el disco 1 desde el poste Derecho hacia el poste Izquierdo
Pasar el disco 2 desde el poste Derecho hacia el poste Medio
Pasar el disco 1 desde el poste Izquierdo hacia el poste Medio
Pasar el disco 3 desde el poste Derecho hacia el poste Izquierdo
Pasar el disco 1 desde el poste Medio hacia el poste Derecho
Pasar el disco 2 desde el poste Medio hacia el poste Izquierdo
Pasar el disco 1 desde el poste Derecho hacia el poste Izquierdo
Pasar el disco 4 desde el poste Derecho hacia el poste Medio
Pasar el disco 1 desde el poste Izquierdo hacia el poste Medio
Pasar el disco 2 desde el poste Izquierdo hacia el poste Derecho
Pasar el disco 1 desde el poste Medio hacia el poste Derecho
Pasar el disco 3 desde el poste Izquierdo hacia el poste Medio
Pasar el disco 1 desde el poste Derecho hacia el poste Izquierdo
Pasar el disco 2 desde el poste Derecho hacia el poste Medio
Pasar el disco 1 desde el poste Izquierdo hacia el poste Medio

Ingresar el numero de discos (salir < 1 o > 10): 12


>>>

1.4. Recorrido de la pieza caballo en un tablero de ajedrez

Definición del problema:

Efectuar el análisis, diseño, implementación en C++ y en Python que tenga una


función recursiva para generar los movimientos de la pieza caballo en su
recorrido por el tablero de ajedrez. Generar aleatoriamente los tipos de
movimiento que hará el caballo; y prueba de escritorio mostrando el estado de la

27
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

pila, para un tablero de 3x3 e Intentar(1, 0, 0), asumir la secuencia aleatoria de


tipos de movimiento: 0 1 2 3 4 5 6 7. Hacer el proceso repetitivo.

Análisis:

Entradas: dimensión del tablero y coordenadas de ubicación inicial del caballo


Salidas: matriz cuadrada que representa el tablero con los números secuenciales
que indican el recorrido que ha dado la pieza caballo, en el caso de que exista
solución.
Proceso:
Se puede considerar un tablero de NxN, para 3 <= N <= 8, especificado en el
siguiente esquema:

Los posibles movimientos que puede dar la pieza caballo son, si se considera
que el origen de coordenadas es el punto (2, 2) y se mueve en sentido horario:

Lo cual conlleva a almacenar las coordenadas de los posibles movimientos en el


siguiente par de arreglos:

Para simular un fenómeno de la realidad, se conoce que los tipos de movimiento


que puede dar la pieza caballo es al azar, por tanto debe considerarse también

28
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

un arreglo de 8 enteros generado aleatoriamente del 0 al 7 y con esto se puede


plantear el siguiente algoritmo en seudocódigo:

Algoritmo en seudocódigo:

Procedimiento Intentar(Este, X, Y)
Inicia Intentar
Inicializar todos los posibles candidatos a movimiento;
Repetir
Calcular la siguiente posición considerando el tipo aleatorio del movimiento (U, V);
Si es aceptable Entonces
Inicia Completar
Anotar el siguiente movimiento;
Si el tablero no se encuentra lleno Entonces
Inicia Avanzar
Intentar(Siguiente, U, V);
Si No ha Terminado el recorrido Entonces
Borrar el siguiente movimiento;
Fin Proceso Avanzar
Caso_contrario
El recorrido ha sido exitoso;
Fin Proceso Completar;
Mientras No haya terminado el recorrido Y existan otros tipos de movimiento;
Fin Procedimiento Intentar;

Prueba de escritorio:
Forman parte de la pila, las variables locales y los parámetros pasados por valor
de la función recursiva.

29
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Modulo para generar números aleatorios en C++:

#ifndef CALEATORIOS_H
#define CALEATORIOS_H
#include <iostream>
#include<ctime>
#include<cstdlib>
using namespace std;
class CAleatorios
{
public:
int *a, n;
CAleatorios(int n = 0);
void Mostrar();
unsigned Aleatorio(unsigned, unsigned);
virtual ~CAleatorios();
protected:
private:
int Buscar(int, int);
};
#endif // CALEATORIOS_H

#include "CAleatorios.h"
CAleatorios::CAleatorios(int n)
{
//ctor
int pos, alt;
srand((unsigned)time(NULL));
if (n == 0)
return;
this->n = n;
a = new int[n];
for (int i = 0;;) {
alt = Aleatorio(0, n - 1);
pos = Buscar(i, alt);
if (pos < 0) {
a[i] = alt;
i ++;
}
if (i >= n) break;

30
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

}
}
CAleatorios::~CAleatorios()
{
//dtor
delete a;
}
void CAleatorios::Mostrar()
{
for (int i = 0; i < n; i ++)
cout << a[i] << " ";
cout << endl;
}
unsigned CAleatorios::Aleatorio(unsigned A, unsigned B)
{
return (A + abs(rand())%(B-A+1));
}
int CAleatorios::Buscar(int tl, int x)
{
//Buscar
int pos;
for (pos = 0; pos < tl; pos ++)
if (x == a[pos])
return pos;
return -1;
}

Programa en C++:

// MovCaballo
#include <iostream>
#include <CAleatorios.h>
#define MaxTab 8
using namespace std;
// Variables globales
CAleatorios Alt(8);
static int A[] = {2, 1, -1, -2, -2, -1, 1, 2},
B[] = {1, 2, 2, 1, -1, -2, -2, -1};
int Tablero[MaxTab][MaxTab], TerminoRecorrido, DimTab, X0, Y0;
// Prototipos
void Intentar(int I, int X, int Y);
void EscribirResultado();
// Funciones
int main()

31
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

{
while (1) {
cout << "INGRESAR LA DIMENSION DEL TABLERO (DE 3 A " <<
MaxTab << ", SINO SALE): ";
cin >> DimTab;
if (DimTab < 3 || DimTab > MaxTab)
break;
cout << "INGRESAR LAS COORDENADAS INICIALES DEL CABALLO \n";
cout << "PARA SALIR DIGITAR VALORES FUERA DE RANGO: ";
cin >> X0 >> Y0;
if (X0 < 0 || X0 > DimTab || Y0 < 0 || Y0 > DimTab)
break;
EscribirResultado();
}
}
void Intentar(int I, int X, int Y)
{ // Intentar
int U,V,K = -1;
do {
U = X + A[Alt.a[++K]];
V = Y + B[Alt.a[K]];
if (U >= 0 && U < DimTab && V >= 0 && V < DimTab &&
Tablero[U][V] == 0) {
Tablero[U][V] = I;
if (I < DimTab * DimTab ) {
Intentar(I+1,U,V);
if (!TerminoRecorrido)
Tablero[U][V] = 0;
}
else
TerminoRecorrido = 1;
}
}
while (!TerminoRecorrido && K < 7);
} // Intentar;
void EscribirResultado() {
int I, J;
cout << "\nMOVIMIENTOS DEL CABALLO\n"
"DIMENSION DEL TABLERO: " << DimTab << endl <<
"UBICACION INICIAL DEL CABALLO: (" << X0 << ", " << Y0 <<
")\nTIPOS DE MOVIMIENTOS ALEATORIOS:\n";
Alt.Mostrar();
cout << endl;
TerminoRecorrido = 0;
for (I = 0; I < DimTab ; I++)
for (J = 0; J < DimTab; J++)

32
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Tablero[I][J] = 0;
Tablero[X0][Y0] = 1;
Intentar(2, X0, Y0);
if (TerminoRecorrido)
for (I = 0; I < DimTab; I++)
for (J = 0; J < DimTab; J++)
cout << Tablero[I][J] << (J == DimTab - 1 ? "\n" : " ");
else
cout << "NO HAY SOLUCION\n";
cout << endl << endl;
}

Resultados de la corrida:

33
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

34
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

35
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

36
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Modulo para generar números aleatorios en Python:

# CAleatorios.py
import time
import random
t = int(time.time())
random.seed(t)
class CAleatorios:
def __init__(self, a, n):
self.a = a
self.n = n

def Aleatorio(elm, m, n):


return random.randint(m, n)

def GetVal(elm, i):


return elm.a[i]

def Buscar(elm, tl, x):


for pos in range(tl):
if x == elm.a[pos]:

37
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

return pos
return -1

def Generar(elm):
i=0
n = elm.n
while (i < n):
alt = elm.Aleatorio(0, n - 1)
pos = elm.Buscar(i, alt)
if pos < 0:
elm.a.append(alt)
i += 1

def Mostrar(elm):
cad = ""
for i in range(elm.n):
cad += str(elm.a[i])+ " "
print(cad)

Programa en Python:

# MovCaballo.py
from ModulosPY.CAleatorios import CAleatorios
def Intentar(I, X, Y):
global TerminoRecorrido
K = -1
while (not TerminoRecorrido and K < 7):
K += 1
Val = alt.GetVal(K)
U = X + A[Val]
V = Y + B[Val]
if (U >= 0 and U < DimTab and V >= 0 and V < DimTab and Tablero[U][V] == 0):
Tablero[U][V] = I
if (I < DimTab * DimTab ):
Intentar(I+1,U,V)
if (not TerminoRecorrido):
Tablero[U][V] = 0
else:
TerminoRecorrido = True
def EscribirResultado():
print( "\nMOVIMIENTOS DEL CABALLO")
print("DIMENSION DEL TABLERO: ", DimTab)
print("UBICACION INICIAL DEL CABALLO: (" , X0, ", ", Y0, ")\nTIPOS DE
MOVIMIENTOS ALEATORIOS:")

38
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

alt.Mostrar()
print("")
for I in range(DimTab):
Tablero.append([])
for J in range(DimTab):
Tablero[I].append(0)
Tablero[X0][Y0] = 1
Intentar(2, X0, Y0)
if (TerminoRecorrido):
for I in range(DimTab):
cad = ""
for J in range(DimTab):
cad += str(Tablero[I][J]) + " "
print(cad)
else:
print("NO HAY SOLUCION")
print("\n")

# Funcion Principal:
MaxTab = 8
alt = CAleatorios([], 8)
alt.Generar()
A = [2, 1, -1, -2, -2, -1, 1, 2]
B = [1, 2, 2, 1, -1, -2, -2, -1]
Tablero = []
TerminoRecorrido = False
DimTab = 0
X0 = 0
Y0 = 0
while (1):
DimTab = int(input( "INGRESAR LA DIMENSION DEL TABLERO (DE 3 A " +
str(MaxTab)+ ", SINO SALE): "))
if (DimTab < 3 or DimTab > MaxTab):
break
print( "INGRESAR LAS COORDENADAS INICIALES DEL CABALLO ")
X0 = int(input( "X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: "))
Y0 = int(input( "Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: "))
if (X0 < 0 or X0 > DimTab or Y0 < 0 or Y0 > DimTab):
break
Tablero = []
TerminoRecorrido = False
EscribirResultado()

39
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Resultados de la corrida:
Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:06:47) [MSC v.1914 32 bit
(Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>>
============== RESTART:
C:\EstructuraDeDatos2020\Python\MovCaballo.py ==============
INGRESAR LA DIMENSION DEL TABLERO (DE 3 A 8, SINO SALE): 3
INGRESAR LAS COORDENADAS INICIALES DEL CABALLO
X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 0
Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 0

MOVIMIENTOS DEL CABALLO


DIMENSION DEL TABLERO: 3
UBICACION INICIAL DEL CABALLO: ( 0 , 0 )
TIPOS DE MOVIMIENTOS ALEATORIOS:
47635201

NO HAY SOLUCION

INGRESAR LA DIMENSION DEL TABLERO (DE 3 A 8, SINO SALE): 4


INGRESAR LAS COORDENADAS INICIALES DEL CABALLO
X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 3
Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 3

MOVIMIENTOS DEL CABALLO


DIMENSION DEL TABLERO: 4
UBICACION INICIAL DEL CABALLO: ( 3 , 3 )
TIPOS DE MOVIMIENTOS ALEATORIOS:
47635201

NO HAY SOLUCION

INGRESAR LA DIMENSION DEL TABLERO (DE 3 A 8, SINO SALE): 5


INGRESAR LAS COORDENADAS INICIALES DEL CABALLO
X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 4
Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 4

MOVIMIENTOS DEL CABALLO


DIMENSION DEL TABLERO: 5
UBICACION INICIAL DEL CABALLO: ( 4 , 4 )
TIPOS DE MOVIMIENTOS ALEATORIOS:
47635201

40
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

23 12 3 18 25
4 17 24 13 8
11 22 7 2 19
16 5 20 9 14
21 10 15 6 1

INGRESAR LA DIMENSION DEL TABLERO (DE 3 A 8, SINO SALE): 6


INGRESAR LAS COORDENADAS INICIALES DEL CABALLO
X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 5
Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 5

MOVIMIENTOS DEL CABALLO


DIMENSION DEL TABLERO: 6
UBICACION INICIAL DEL CABALLO: ( 5 , 5 )
TIPOS DE MOVIMIENTOS ALEATORIOS:
47635201

25 18 15 34 31 36
16 5 24 3 14 33
19 26 17 32 35 30
6 9 4 23 2 13
27 20 11 8 29 22
10 7 28 21 12 1

INGRESAR LA DIMENSION DEL TABLERO (DE 3 A 8, SINO SALE): 7


INGRESAR LAS COORDENADAS INICIALES DEL CABALLO
X0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 6
Y0, PARA SALIR DIGITAR VALORES FUERA DE RANGO: 6

MOVIMIENTOS DEL CABALLO


DIMENSION DEL TABLERO: 7
UBICACION INICIAL DEL CABALLO: ( 6 , 6 )
TIPOS DE MOVIMIENTOS ALEATORIOS:
47635201

41
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

1.5. Ejercicio de aplicación

Efectuar el análisis, diseño, implementación en C++ y en Python que tenga una


función recursiva para generar todas las posibles soluciones al ubicar en el
tablero de ajedrez, 8 Reinas de tal forma que no se puedan poner en jaque entre
sí. Hacer la prueba de escritorio de una de las posibles soluciones.

42
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Bibliografía:
1) Niklaus Wirth: Algoritmos + Estructuras de Datos = Programas.

2) Fausto Meneses Becerra: Estructuras de Datos en C++.

43
Algoritmos Recursivos
Universidad de las Fuerzas Armadas ESPE Unidad de Educación a Distancia

Recursos complementarios:
Los siguientes recursos complementarios son sugerencias para que puedas
ampliar la información sobre el tema trabajado, como parte de un proceso de
aprendizaje autónomo:

• Programación y Recursividad. Recuperado de:


https://elvex.ugr.es/decsai/c/apuntes/recursividad.pdf
• La recursividad
https://www.uv.mx/personal/ocastillo/files/2011/04/Recursividad.pdf
• Definicion y uso de funciones
http://decsai.ugr.es/~jfv/ed1/c/cdrom/cap6/cap66.htm

o Uso de la recursividad y su implementación

https://www.youtube.com/watch?v=0NBPd81uhJE&ab_channel=Programaci%C3%B3n
DesdeCero

44
Algoritmos Recursivos

También podría gustarte