jueves, 14 de enero de 2016

Algoritmos - 2 de 5



5.3 Algoritmos iterativos

Un componente básico de los algoritmos es la iteración. Esta palabra implica repetición, ya que iterar significa ejecutar repetidamente algunos pasos elementales de un algoritmo acercándose, paso a paso, a la solución. Para realizar un programa de ordenador es muy conveniente localizar los pasos que se repiten y anidarlos dentro de un sistema iterativo.
En el ejemplo anterior en el que se determinaba si el número 5 era o no primo, se ha seguido un método que funciona para el caso concreto del número 5. Si se quisiera desarrollar un programa más genérico (por ejemplo, un programa que determinara si un número N es o no primo) haría falta identificar la parte repetitiva del algoritmo y anidarla dentro de un proceso iteración en el algoritmo. 


Ejemplo 6. Determinar iterativamente si un número N es o no primo

Vamos a rehacer el algoritmo para calcular si el número 5 es primo de manera que sea un algoritmo válido para un número genérico N. En el diagrama de flujo del ejemplo para el número 5 se observan tres repeticiones:
Se divide el número N entre un número natural.
Se observa si la división es entera.
Se incremento del número natural que va a dividir al número N.
En el caso que estamos tratando hemos localizado tres pasos que se repiten, los cuales se realizan desde el número 2 hasta el número 4 en el ejemplo concreto del número 5. De lo que se trata es de anidar estos tres pasos dentro de un bucle que se ejecute mientras que se cumpla una cierta condición; siendo la condición que el número natural que divide a N sea inferior a N. Vamos a llamar al número que divide i, al resultado de la división D y al número a chequear N. El diagrama de flujo resultante es el siguiente:


En el diagrama de flujo hay tres instrucciones que se repiten iterativamente (la tres que se han descrito anteriormente) y aparece una condición de chequeo para saber si debe detenerse el algoritmo o no (si i ha llegado a obtener el valor de N). La manera elegida para programar el bucle ha sido la de anidarlo dentro de un bucle WHILE con la condición de chequeo en TRUE (while 1). Este bucle resultaría ser un bucle infinito (ya que la condición de chequeo siempre se cumple) si no fuera porque dentro del bucle se han anidado dos bifurcaciones IF que contienen una ruptura del bucle (BREAK). Esta ruptura se produce cuando la condición de alguna de las dos bifurcaciones se cumple, las cuales son:

1ª Si se encuentra una división exacta (N no es primo).
2ª Si resulta que el divisor a probar (i) ha llegado a ser igual a N (N es primo).



Ejemplo 7. Mejora del algoritmo para calcular si un número N es primo 

A continuación se van a presentar dos mejoras del anterior proceso iterativo desde el punto de vista de la computación. Como ya sabemos, para determinar si, por ejemplo, el número 231 es primo, hay que dividirlo entre todos los números naturales entre el 2 y el 230. Pero también es cierto que todos aquellos números naturales superiores al √231 no dividen de forma entera al 231, por lo que la condición de chequeo puede modificarse por comprobar que la variable i no rebase el valor de √231 . Con esto se consigue que, en el caso del número 231, en lugar de repetir el proceso iterativo 229 veces (desde i=2 hasta i=230), se repita solo 14 veces (desde i=2 hasta i=15), ya que por encima del 15 es seguro que no existe ningún número que divida al 231 de manera entera, con lo que se reduce la carga computacional del algoritmo.



Otra mejora sobre el algoritmo anterior consiste en evitar que en cada paso del bucle iterativo se calcule la raíz cuadrada de N, ya que calcular la raíz cuadrada de un número es muy costoso para un ordenador. Para ello basta con calcular una vez la raíz de N, almacenarla en una variable (llamémosle K) y utilizar esta variable en la condición de chequeo, como puede verse en el diagrama de flujo siguiente.


Si se prueba a cronometrar los dos últimos programas presentados (PrimoII y PrimoIII) puede observarse que PrimoIII es mucho más rápido que PrimoII. ¿Cómo cronometrar un programa? Generalmente se utiliza el propio reloj del ordenador para medir el tiempo. Además, cuando son programas cuyo tiempo de ejecución es muy corto, se le suele pedir al ordenador que los ejecute varias veces (por ejemplo 1000) y se establece una media del tiempo que ha tardado, ya que medir lo que tarda el programa una sola vez es muy impreciso.


Ejemplo 8. Obtención del factorial de un número N iterativamente

Un ejemplo claro de un proceso iterativo es el cálculo del factorial de un número N. La regla del factorial de un número expresada matemáticamente es:


De forma que para calcular el factorial del número 5 se siguen los siguientes pasos:
Se multiplica a 2 por 3 y se almacena el resultado.
Se multiplica el resultado anterior por 4 y se almacena de nuevo.
Se multiplica el resultado anterior por 5 y se almacena de nuevo.
Este último resultado es el factorial de 5. Si observamos el proceso se ve necesario utilizar una variable que varíe su valor de uno en uno desde el 2 hasta el 5 (llamémosle a esta variable i) Asimismo se necesita otra variable para almacenar el resultado de la multiplicación (llamémosle a esta variable Resultado).

Los pasos que se repiten son 3:
Multiplicación de Resultado por la variable i.
Almacenamiento de la operación anterior.
Incremento de la variable i en una unidad.

En realidad los pasos 1º y 2º se pueden hacer en una sola operación "informática" (multiplicar Resultado por i) y asignar el resultado de la multiplicación a Resultado).

Por último faltaría la condición de chequeo para continuar o para detener los tres pasos durante el proceso iterativo. La condición de chequeo en este caso es comprobar que la variable i no supera el valor del número del que se calcula el factorial N.



Ejemplo 9. Descomposición factorial de un número N.

Obtener la descomposición factorial de un número natural N consiste en determinar todos aquellos números naturales de menor valor que multiplicados entre si dan como resultado el número N. Para ello hay que probar a dividir el número N entre todos los números naturales que hay entre 2 y N . A continuación se describen los pasos que se repetirán en el proceso iterativo:

Siendo I el número con el que se prueba a dividir N en un momento dado, hay que chequear que el valor de I no supere el valor de √N :

Si lo supera, se termina el proceso iterativo.
Si no lo supera, se comprueba si N es divisible de manera entera entre I:
Si N no es divisible entre I hay que pasar al siguiente número (el I+1).
Si N es divisible entre I hay que:
Almacenar a I como factor del número N
Actualizar el valor de N como N=N/I
Los factores se van a guardar en un vector que llamaremos factores. Para guardar ordenadamente los factores nos valdremos de una variable auxiliar que llamaremos f.
Inicialmente esta variable f valdrá 1 y, cada vez que aparezca un nuevo factor de N, se incrementará en una unidad el valor de f. De esta manera, sirviéndonos de la variable auxiliar f, podremos guardar el primer factor en la primera celda del vector factores, el segundo factor en la segunda celda y así sucesivamente.



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










  

No hay comentarios:

Publicar un comentario en la entrada