Está en la página 1de 10

REPÚBLICA BOLIVARIANA DE VENEZUELA

MINISTERIO DEL PODER POPULAR PARA LA


EDUCACION UNIVERSITARIA
UNIVERSIDAD POLITÉCNICA TERRITORIAL
“JOSÉ ANTONIO ANZOÁTEGUI”
ESCUELA DE INGENIERÍA INFORMATICA
SEDE BARCELONA

ADMINISTRACIÓN BÁSICA

Unidad: Arquitectura del computador I

Docente: ING. Jesús

Autor: José Gil


C.I. 27.275.675
Sección: 03
Turno: Tarde
Trayecto: 01
Semestre: 02

Barcelona, Marzo 2023


Recursividad

Es la propiedad que tienen los procedimientos y funciones de llamarse a


sí mismos para resolver un problema.

Es un método para resolver problemas a través de categorías más


pequeñas de la misma cuestión. Resolviendo los problemas a través de sub-
problemas hasta llegar a su versión menor, también conocida como el caso
base. Una función recursiva sigue llamándose a sí misma hasta que la
ejecución se detiene, y una condición base se hace realidad. Hay dos partes
dos una función recursiva:

 El caso base
 La estructura recursiva

Caso base

La base es la parte más pequeña del problema. Sorprendentemente,


conocemos la condición de terminación o solución donde la función podría
devolver inmediatamente los resultados.

La estructura recursiva

Encontrar la respuesta a un problema a través de la solución de su sub-


problema es una estructura recursiva.

Ejemplo:

El cálculo del factorial de un número entero.

El factorial de un número se define como ese número multiplicado por el


anterior, éste por el anterior, y así sucesivamente hasta llegar a 1. Así, por
ejemplo:

El factorial del número 5 sería: 5x4x3x2x1 = 120.

El factorial del número 4 sería: 4x3x2x1 = 24

Si nos damos cuenta la definición del factorial de 5 incluye dentro la


definición del factorial de 4

1
Donde podríamos decir que:

5! = 4!

Con esto estamos definiendo el factorial de un numero utilizando la


propia definición de factorial, esto se le llama recursividad

Si se generaliza esta definición para que sirva para cualquier número se


puede definir que:

factorial(N)=N*factorial(N-1)

Esta definición es una definición recursiva valida, pero si la intentamos


utilizar a la práctica para calcular el factorial de un numero obtendremos un
problema.

El problema es que nuestra recurrencia no tiene final, cuando lleguemos


a calcular el factorial de uno (1) necesitaremos calcular el factorial de cero y
para calcular el factorial de cero (0) necesitaremos el factorial de menos uno (-
1) y así hasta el infinito a la práctica acabamos de crear un bucle infinito y todo
porque nos falta un punto clave cuando utilizamos algoritmos recursivos: El
caso base

El caso base es la condición de parada de nuestra reclusión, es el punto


donde paramos, donde dejamos de llamar a la función y decimos hasta aquí se
termina.

En el caso de factorial este punto de parada sería el factorial de cero (0)

factorial(N)=N*factorial(N-1) si N>0
Caso Recursivo

Si consideramos un caso donde definimos que el factorial de cero (0) es


uno (1) conseguimos determinar un final para la recursión, el factorial de cero

2
no depende de ningún factorial de ningún numero mas pequeño que cero (0),
es simplemente uno (1) asi conseguimos un final para la recursión, si todos los
números de un factorial dependiendo de los factoriales anteriores siempre
vamos hacia atrás pero cuando llegamos al factorial de cero cortamos ese
camino.

factorial(N)=1 si N=0
Caso Base

Toda recursión necesita un caso base si queremos que pueda terminar,


al igual que la mayoría de bucles tenemos un punto de finalización de la
iteración, vamos interando hasta que i llega a tal valor o hasta que se cumple
cierta condición.

Sabiendo esto el código que calcula el factorial de un número sería el


siguiente:

def factorial(n):
if n=0:
return 1
else:
return n * factorial(n-1)

Como se ve en el código tenemos dos casos, uno define el caso base y


otro define el caso recursivo de esta forma se pueden expresar algoritmos de
forma mas ``elegante`` ya que con recursividad se simplifica la forma de pensar
en ellos.

Esta estrategia es una alternativa al uso de bucles. Una solución


recursiva es, normalmente, menos eficiente que una solución basada en
bucles. Esto se debe a las operaciones auxiliares que llevan consigo las
llamadas a las funciones. Cuando un programa llama a una función que llama a
otra, la cual llama a otra y así sucesivamente, las variables y valores de los
parámetros de cada llamada a cada función se guardan en la pila o stack, junto
con la dirección de la siguiente línea de código a ejecutar una vez finalizada la
ejecución de la función invocada.

Esta pila va creciendo a medida que se llama a más funciones y decrece


cuando cada función termina. Si una función se llama a sí misma
recursivamente un número muy grande de veces existe el riesgo de que se
agote la memoria de la pila, causando la terminación brusca del programa.

3
Regiones de memoria que distingue el sistema operativo.

A pesar de todos estos inconvenientes, en muchas circunstancias el uso


de la recursividad permite a los programadores especificar soluciones naturales
y sencillas que sin emplear esta técnica serían mucho más complejas de
resolver. Esto convierte a la recursión en una potente herramienta de la
programación. Sin embargo, por sus inconvenientes, debe emplearse con
cautela.

Aplicación

Se puede aplicar la recursión con casi cualquier problema. Sin embargo,


hay situaciones particulares en las que la recursión le resultará especialmente
útil.

 Diseño de algoritmos de aproximación relacionados


 Algoritmos de clasificación bien reconocidos como Merge Sort o Quick
Sort
 Solución de problemas mediante el rastreo y la búsqueda exhaustiva
 Solución de problemas con la ayuda de la programación dinámica
 Usando un enfoque de divide y vencerás para resolver problemas
 Encontrar soluciones a los problemas de los gráficos
 Buscando respuestas a los problemas de los árboles
 Resolver los problemas de las listas de enlaces y matrices

Utilidad:

Las soluciones recursivas son en su mayoría más limpias comparadas


con una solución iterativa. Hay casos en los que se puede reducir una línea de
50 a 5 o 10 líneas de recursividad.

4
Pensar en la recursividad es más natural en algunos casos. La
recursividad es posiblemente la forma más directa y transparente de resolver
problemas de estructura de datos, por ejemplo, árboles con estructuras
recursivas simples.

Algunos problemas son bastante complicados, y en algunos casos,


imposibles de resolver mediante el uso de la Iteración.

La recursividad es una excelente opción, ya que divide los problemas en


subproblemas independientes y más pequeños, lo que hace más sencillo el
paralelismo.

Ventajas.

 Mayor simplicidad del código en problemas recursivos.


o Si un problema se puede definir fácilmente de forma recursiva
(por ejemplo, el factorial o la potencia) es código resultante puede
ser más simple que el equivalente iterativo.
 También es muy útil para trabajar con estructuras de datos
que se pueden definir de forma recursiva, como los
árboles.

 Posibilidad de “marcha atrás”: backtracking.


o Las características de la pila de llamadas hacen posible recuperar
los datos en orden inverso a como salen, posibilitando cualquier
tipo de algoritmo que precise volver hacia atrás.

Desventajas.

 Mayor uso de la pila de memoria.


o Cada llamada recursiva implica una nueva entrada en la pila de
llamadas dónde se cargará tanto la dirección de retorno como
todos los datos locales y argumentos pasados por valor.
o El tamaño que reserva el compilador a la pila de llamadas es
limitado y puede agotarse, generándose un error en tiempo de
compilación

 Mayor tiempo en las llamadas.


o Cada llamada a un subprograma implica:
 Cargar en memoria el código del procedimiento.
 Meter en la pila la dirección de retorno y una copia de los
parámetros pasados por valor.
 Reservar espacio para los datos locales.
 Desviar el flujo del programa al subprograma, ejecutarlo y
retornar al programa llamador.

5
o Esto implica un mayor tiempo de ejecución, sobre todo si hay
muchas llamadas anidadas, algo normal en programas recursivos.

 Estos dos inconvenientes se pueden agravar si se pasan estructuras de


datos muy grandes por valor, ya que implica copiar estos datos en la pila
de procedimientos

En resumen: Un programa recursivo puede ser menos eficiente que uno


iterativo en cuanto a uso de memoria y velocidad de ejecución.

Diseño y escritura de Programas Recursivos

Para que una función o procedimiento recursivo funcione se debe


cumplir que:

 Existe una salida no recursiva del procedimiento o función y funciona


correctamente en ese caso.

 Cada llamada al procedimiento o función se refiere a un caso más


pequeño del mismo.

 Funciona correctamente todo el procedimiento o función.

Para poder construir cualquier rutina recursiva teniendo en cuenta lo


anterior, podemos usar el siguiente método:

 Primero, obtener una definición exacta del problema.

 A continuación, determinar el tamaño del problema completo a resolver.


Así se determinarán los valores de los parámetros en la llamada inicial al
procedimiento o función.

 Tercero, resolver el caso base en el que problema puede expresarse no


recursivamente. Esto asegurará que se cumple el punto 1 del test
anterior.

 Por último, resolver correctamente el caso general, en términos de un


caso más pequeño del mismo problema (una llamada recursiva). Esto
asegurará cumplir con los puntos 2 y 3 del test.

Cuando el problema tiene una definición formal, posiblemente


matemática, como el ejemplo del cálculo del factorial, el algoritmo deriva
directamente y es fácilmente implementable, en otros casos debemos
encontrar la solución más conveniente

6
Otro ejemplo clásico es: Las Torres de Hanoi

Cuenta una leyenda que en un templo de Hanoi se dispusieron tres


pilares de diamante y en uno de ellos 64 discos de oro, de distintos tamaños y
colocados por orden de tamaño con el mayor debajo

Torre de ocho discos

Cada monje, en su turno, debía mover un único disco de un pilar a otro,


para con el tiempo conseguir entre todos llevar la torre del pilar inicial a uno de
los otros dos; respetando una única regla: nunca poner un disco sobre otro de
menor tamaño. Cuando lo hayan conseguido, ¡se acabará el mundo!

[Se requieren al menos 264 ‐1 movimientos; si se hiciera uno por


segundo, se terminaría en más de 500 mil millones de años]

Queremos resolver el juego en el menor número de pasos posible

¿Qué disco hay que mover en cada paso y a dónde?

Identifiquemos los elementos (torre de cuatro discos):

Cada pilar se identifica con una letra

Mover del pilar X al pilar Y:

Coger el disco superior de X y ponerlo encima de los que haya en Y

Resolución del problema en base a problemas más pequeños

Mover N discos del pilar A al pilar C: Mover N‐1 discos del pilar A al pilar
B Mover el disco del pilar A al pilar C Mover N‐1 discos del pilar B al pilar C

7
Para llevar N discos de un pilar origen a otro destino se usa el tercero
como auxiliar

Mover N‐1 discos del origen al auxiliar Mover el disco del origen al
destino Mover N‐1 discos del auxiliar al destino

Mover N‐1 discos se hace igual, pero usando ahora otros origen y
destino

Mover N‐1 discos del pilar A al pilar B: Mover N‐2 discos del pilar A al
pilar C Mover el disco del pilar A al pilar B Mover N‐2 discos del pilar C al pilar
B

Naturaleza recursiva de la solución

8
Caso base: no quedan discos que mover

void hanoi(int n, char origen, char destino, char auxiliar) {


if (n > 0) {
hanoi(n ‐ 1, origen, auxiliar, destino);
cout << origen << " ‐‐> " << destino << endl;
hanoi(n ‐ 1, auxiliar, destino, origen);
}
}

int main() {
int n;
cout << "Número de torres: ";
cin >> n;
hanoi(n, 'A', 'C', 'B');
return 0;
}

También podría gustarte