domingo, 17 de enero de 2016

Algoritmos - 3 de 5



5.4 Algoritmos recursivos

Se dice que un objeto es recursivo cuando el propio objeto está compuesto parcialmente por sí mismo. Los sistemas recursivos tienen una importancia particular para realizar ciertas definiciones matemáticas. Un ejemplo lo encontramos en la definición del factorial de un número. La función factorial para los números naturales puede ser expresada de la manera siguiente:


Esta es la expresión recursiva del factorial de un número, frente a la iterativa, expresada en la ecuación (5.2)

Los sistemas recursivos no se encuentran sólo en imaginario mundo de las matemáticas, sino que también en la más prosaica realidad, como puede verse al enfrentar dos espejos o al mirar una coliflor o un helecho. Poder establecer una expresión recursiva implica poder definir una serie infinita de objetos mediante la inicialización de un número finito de objetos. Desde el punto de vista informático esto abre posibilidades que dan vértigo, ya que se pueden describir un número infinito de operaciones mediante un programa finito, aunque, al contrario que los programas que contienen bucles, no presente repeticiones de código explícitas. Los algoritmos recursivos, sin embargo, son particularmente adecuados cuando el problema a resolver, o la función matemática a computar o la estructura de datos a ser procesada está definido en términos recursivos. En general un programa recursivo P es función (Π) de una serie de instrucciones Si distintos de P y de P mismo:


La herramienta necesaria y suficiente para poder expresar programas de manera recursiva en un determinado lenguaje de programación es que el lenguaje de programación admita funciones, procedimientos o subrutinas (como prefiera llamarse). 
Cuando una función contiene una llamada explícita a sí misma, se dice entonces que hay una recursividad directa. Cuando, por el contrario, una función no realiza una llamada directa a sí misma, sino que lo hace a través de otra función, se dice que tiene una recursividad indirecta.


Ejemplo 10. Obtención del factorial de un número N recursivamente

Anteriormente se determinó el factorial de un número N de manera iterativa según la fórmula:


Y acabamos de ver cómo también puede expresarse de manera recursiva según esta otra forma:


La cual define el factorial de un número como el producto de ese número con el factorial del número anterior; salvo el factorial de 0, que es 1 por definición. El diagrama de flujo del algoritmo para calcular el factorial de un número de manera recursiva es el siguiente:


Hay que observar que en el diagrama de flujo no hay un retroceso en la línea del programa, sino que lo que aparece es una nueva llamada al propio programa (Factorial) cada vez que se trata de calcular el factorial de un número superior a 0. A su vez, esta llamada pide calcular el factorial del número anterior al que se encuentra en cada momento, por lo que llegará un momento en el que se tratará de calcular el factorial de 0. En este momento, debido a la bifurcación que hay, se determina el primer resultado, que es 1, y finaliza el programa en el que se está calculando el factorial de 0. Al finalizar este programa se continúa con el cálculo del factorial de 1, donde se establece el segundo resultado (1*1) y finaliza este segundo programa. A su vez vuelve a continuar el programa superior, que se corresponde con el cálculo del factorial de 2, y así continúa la ascensión sucesivamente hasta llegar a determinar el valor del factorial del número que se ha solicitado.


Ejemplo 11. Determinar recursivamente si un número N es o no primo 

Recordamos que un número entero N es primo si es mayor o igual a dos y sus únicos factores son el 1 y el propio número. En el Ejemplo 6 y en el Ejemplo 7 se estableció un método iterativo para determinar si un número cumple esta condición. Este método consiste en dividir al número entre todos los números naturales que hay entre el 2 y la raíz cuadrada del número a estudiar, observando si en algún caso la división era exacta.

De lo que ahora se trata es de seguir el mismo razonamiento anterior de manera recursiva. Para ello se va a plantear el diagrama de flujo en dos partes: la inicialización y el cálculo recursivo.

En la inicialización el usuario pasa el número que quiere saber si es primo, el cual queda guardado en la variable N. A continuación se guarda la raíz cuadrada de N en una variable global -K- y por último se inicializa el proceso recursivo en el que se chequea si es divisible N entre algún número natural comprendido entre el 2 y K. Para ello se utiliza una función auxiliar que llamaremos Mirar_si_es_divisible(N,k), la cual se utiliza en la inicialización para k=2. (Nótese que se está diferenciando entre K mayúscula y k minúscula).

Esta función auxiliar contendrá los pasos que hay que dar para determinar si N es divisible entre k. En caso de que no lo sea, realizará otra llamada recursiva a si misma para chequear si N es divisible entre k+1, hasta que suceda que, o bien k es superior a K -en cuyo caso N es primo- o bien N sea divisible por k -en cuyo caso N no es primo. 

El diagrama de flujo queda de la siguiente manera:




Ejemplo 12. El triángulo de Sierpinski

El triángulo de Sierpinski entra en el ámbito de los fractales. Fractal procede del adjetivo latino ’fractus’, que significa "interrumpido" o "irregular". 
La geometría del fractal fue utilizada por Benoît Mandelbrot en 1975 para describir ciertas formas complejas de la naturaleza como nubes, sistemas montañosos, galaxias, flores, ramificaciones arbóreas y bronquiales, rocas, cuencas hidrográficas, el sistema neuronal, las líneas costeras, esponjas,... y objetos matemáticos como el conjunto de Cantor o el triángulo de Sierpinski, cuyo comportamiento caía fuera del marco de la matemática tradicional.



El triángulo de Sierpinski es una construcción geométrica realizada mediante una sucesión de triángulos. La base del método está en que un triángulo puede dividirse en tres triángulos exactamente iguales. A su vez, cada uno de estos tres triángulos puede dividirse en otros tres triángulos exactamente iguales y así sucesivamente. De manera que el triángulo de Sierpinski es un triángulo formado a partir de una sucesión sucesiva de sucesivos triángulos que se suceden sucesivamente, formándose a partir de triángulos pequeños otros triángulos cada vez más grandes.



La manera adecuada para realizar esta construcción geométrica es mediante un sistema de dibujo recursivo. En la parte inicial del proceso se indica el orden del triángulo de Sierpinski, donde el orden es el número de subdivisiones triangulares con las que se divide el triángulo mayor. Posteriormente se procede a dibujar los triángulos según el orden indicado.

Para dibujar los triángulos se va a establecer una subrutina propia con la que se dibujará un triángulo mediante tres variables (X,Y,h). La variables X, Y se corresponden con la posición del vértice superior del triángulo y h se corresponde con la altura o la base del triángulo, como puede verse en la Figura 5.3. Obsérvese que en la Figura 5.3, las proporciones no se mantienen, ya que aunque está dibujado como un triángulo equilátero, es un isósceles. La subrutina para dibujarlo recibirá el nombre de Dibujar_triangulo(x,y,h).


La inicialización la realizaremos dentro de una rutina principal a la que llamaremos Triangulo_Sierpinski(N), donde N es el orden del triángulo. En esta rutina se inicializarán las coordenadas X, Y del primero de los triángulos que va a ser dibujado. Por real decreto vamos a hacer que el punto de origen esté en la posición (0,0) -punto A0 de la Figura 5.4-. Siguiendo el mismo criterio, se asigna una longitud inicial a h de una unidad. Por último esta rutina iniciará una subrutina a la que llamaremos triangulo(N,x,y,h). Esta subrutina es quien contendrá las instrucciones para ir dibujando los triángulos recursivamente.


Básicamente la subrutina triangulo(N,X,Y,h) se va a limitar a localizar el lugar en el que ha de ser dibujado cada uno de los triángulos, así como de determinar el tamaño que tendrán los triángulos. Como se ha dicho en la introducción al ejemplo, cada triángulo se compone de otros tres triángulos más pequeños. En concreto, un triángulo de altura h se compone de otros tres triángulos de altura h/2, como puede verse en la Figura 5.5. De forma que el triángulo que tiene su vértice superior en la posición Ai tiene una altura h y está formado por tres triángulos de altura h/2 que tienen su vértice superior en las posiciones Ai+1, Bi+1 y Ci+1. Con este esquema queda planteado el sistema recursivo ya que cada vez que se ejecuta la subrutina triangulo(N,X,Y,h), esta se llama a si misma 3 veces - triangulo(N-1,X,Y,h)- para elaborar un triángulo de orden menor, modificando las posiciones X, Y según el lugar donde debe encontrarse el vértice superior de cada triángulo y reduciendo h a la mitad.



Cuando el orden del triángulo a dibujar es 0, detienen las llamadas recursivas. En ese momento se procede a dibujar el triangulo y finaliza el hilo de esa subrutina. La evolución del dibujo de un triángulo de Sierpinski de orden 3 según el esquema planteado es la siguiente:



Asimismo, el diagrama de flujo para dibujar el triángulo de Sierpinski de la manera que acaba de ser planteada es el siguiente:





Espero haber ayudado en algo. Hasta la próxima oportunidad!











  

No hay comentarios:

Publicar un comentario en la entrada