Story Transcript
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________ En esta segunda sesión vamos a trabajar los siguientes contenidos: - Procedimientos. - Programación Matemática: Aplicaciones.
Actividad 1: Procedimientos.
Actividades:
Proyecto “Visor de Imágenes” Mejora el Visor de Imágenes. Agrega un módulo que vas a llamar “mdlDibujando” al proyecto para escribir los procedimientos AbrirImagen y el procedimiento “DibujaBorde” y sustituye el código que se repite de Visor de Imagen por llamadas a estos procedimientos. Termina de programar las opciones del formulario frmOpciones. Solución: Recuerda que un Objeto es una estructura de programación que encapsula datos y funciones en una sola unidad y a la que solo se puede acceder a través de sus interfaces (propiedades, métodos y eventos). Las Propiedades son atributos que describen al Objeto. Los métodos acciones que el objeto es capaz de realizar. Los eventos son métodos que se ejecutan de un modo especial provocados por el usuario al interactuar con los controles de un formulario.
Recuerda también que un procedimiento es un conjunto discreto de código que puede ser ejecutado desde otros bloques de código. Existen dos tipos de procedimientos: • Que devuelven un valor (denominados funciones). • Que no devuelven un valor(denominados subrutinas o simplemente procedimientos) Funciones Public Function Nombre(ByVal o ByRef variable As …) As … Código… End Function Subrutinas o procedimientos Public Sub Nombre(ByVal o ByRef variable As …) Código… End Sub
Página 1
Paco Villegas & Luis Cabello
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________ Carga el proyecto VisorImagen que está en la carpeta sesion20 del escritorio. Pincha en Proyecto>Agregar Módulo… y ponle el nombre dibujando. Escribe el siguiente código: Public Sub AbrirImagen() 'Mostrar el cuadro de diálogo abrir If frmVisor.ofdSeleccionaImagen.ShowDialog = Windows.Forms.DialogResult.OK Then 'Cargar la Imagen en el cuadro de Imagen frmVisor.PicAlojoImagen.Image = _ Image.FromFile(frmVisor.ofdSeleccionaImagen.FileName) 'Mostrar el nombre del archivo en el titulo del formulario frmVisor.sbrBarraEstado.Items(0).Text = _ frmVisor.ofdSeleccionaImagen.FileName End If End Sub Public Sub DibujaBorde(ByRef objcajadibujo As PictureBox) Dim objGraphics As Graphics objGraphics = objcajadibujo.Parent.CreateGraphics objGraphics.Clear(System.Drawing.SystemColors.Control) objGraphics.DrawRectangle(Pens.Blue, _ objcajadibujo.Left - 1, objcajadibujo.Top - 1, _ objcajadibujo.Width + 1, objcajadibujo.Height + 1) objGraphics.Dispose() End Sub Observa que el código de AbrirImagen() coincide casi exactamente con el código asociado a los eventos del botón Imagen del Ítem del Menú Fichero Abrir Imagen y del icono de Barra de la barra de herramientas. Explica cuál es la diferencia. Repitamos y veamos las diferencias del trozo de código que dibuja el borde con el procedimiento DibujaBorde. Ahora sustituimos los trozos de códigos por llamadas a los procedimientos AbrirImagen () y DibujaBorde(picAlojoImagen) ¿Es mejor el procedimiento DibujaBorde que AbrirImagen? ¿Por qué motivo? Trata de mejorar el procedimiento AbrirImagen
Actividad 2: Programación Matemática. Programa: Criba.
Página 2
Paco Villegas & Luis Cabello
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________ Diseña una solución que simule la criba de Eratóstenes para buscar los números enteros y positivos primos que hay hasta un número entero positivo dado de antemano. Solución: Vamos a programar un método propuesto por Eratóstenes para obtener la lista de todos los primos menores, hasta cierto valor. Los pasos del método son: • Primero hacemos una lista de todos los números desde 1 hasta n. • Tachamos el 1. • Tomamos el 2 y tachamos todos los múltiplos de 2 en la lista. • Buscamos el primer número sin tachar y tachamos todos sus múltiplos mayores. • Repetimos el paso anterior hasta que se acaben los números. • Los que quedaron sin tachar son los que no tienen divisores (propios) o sea los primos. Monta el formulario como el que tienes en la imagen y programa los botones lista y criba, para ello escribe las líneas de programación oportunas e introduce los comentarios que creas necesarios para que aclaren el funcionamiento del código.
la
Utilizamos el control ListView para mostrar lista de todos los números cuando se pulse el botón
y ponga en azul los múltiplos
cuando se pulse el botón Utilizaremos una matriz en este ejercicio. Una matriz se declara de forma parecida a como se hace con una variable: Dim lista(n) as Integer. lista(n) es una caja con n+1 elementos. Para almacenar un valor lista(2)=3, el tercer elemento de la matriz es un 3. Una Matriz se puede llenar mediante un bucle. La idea es llenar una matriz con n+1 elementos. Colocamos en lista(0) un cero, en todos los demás elementos de la matriz un -1 ¿Con que bucle?. Un -1 en lista(i) indica que i está sin tachar (primo). Un 0 en lista (i) indica que i está tachado (no primo). Hacemos lista(1)=0 y lo ponemos azul. Y viene el bucle anidado milagroso que busca el primer número sin tachar y tachamos todos sus múltiplos mayores. Así sucesivamente. For p = 2 To n If lista(p) = -1 Then MsgBox("Tachamos mútiplos de " & p) Página 3
Paco Villegas & Luis Cabello
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________ For j = p * p To n Step p lista(j) = 0 ListView1.Items.Item(j - 1).BackColor = Color.Blue Next j End If Next p
Actividad 3: Programación matemática: Aplicaciones Programa: SuperCriba. Para cada primo p, el múltiplo (propio) más chico que no esta tachado es p2. Así que los primos menores que la raíz cuadrada de n pueden tener múltiplos sin tachar en la tabla, pero los mayores no. ¿Es cierto esto? ¿Porqué?. Basándote en el razonamiento anterior mejora el programa Criba (llámalo SuperCriba) para que sea más rápido. Puedes crear otra versión donde la salida sean los primos menores o iguales que el número de entrada.
Actividad 4: Programación matemática: Aplicaciones. Programa: Descomposición en factores primos. A veces para factorizar un número lo escribimos como producto de dos factores de forma fácil y repetimos el mismo proceso para cada parte hasta que lo descomponemos totalmente. Ejemplo: 140= 10*14 = (2*5)*(7*2). También puede ser 140 = 2*(70)=2*(7*10)=2*(7*(2*5)). Se podría hacer de muchas maneras pero el resultado siempre es el mismo aunque los factores no estén el mismo orden. Diseña un programa que descomponga de esta manera. Solución Observa como podríamos hacer para que el ordenador encontrara algún factor. Dim Raiz, otro, i, factor As Integer Raiz = Int(Math.Sqrt(Numero)) - 1 'Prueba solo hasta la raiz cuadrada por la que ya sabemos 'Pero no con 1 otro = 1 For i = 1 To ParaProbar * Raiz 'Prueba muchas veces a factorizarlo buscando un posible factor aleatorio menor que la raíz del número factor = 2 + Int(Rnd() * Raiz) If Numero Mod factor = 0 Then
Página 4
Paco Villegas & Luis Cabello
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________ otro = Numero \ factor Exit For End If Next ParaProbar es una constante para que intente encontrar el factor las veces que se quiera. Si no encuentra un factor podría ser primo. El siguiente código es para comprobar si es primo. If otro = 1 Then 'No ha encontrdo factor, quizas Numero es primo For factor = 2 To Raiz + 1 If Numero Mod factor = 0 Then otro = Numero \ factor Exit For End If Next factor End If Lo puedes ver en la carpeta de la sesion 20. En general ver si un número muy grande es primo o no, es un problema difícil aunque hay algoritmos rápidos, pero basados en matemática más avanzada. Si tratamos de utilizar los métodos que vimos antes para ver si 10000000200000001 es primo, utilizando la criba seguro que nos quedamos sin memoria, y si tratamos de probar dividiendo por los anteriores es demasiado lento. Tremendamente más difícil es factorizar un número si es muy grande, aunque sepamos que nos es primo. En esta dificultad se basa la seguridad de algunos sistemas de encriptación de clave pública por ejemplo el PGP, en este caso se tienen dos claves: • La pública: esta clave se da a conocer y permite que cualquiera sea capaza de cifrar un mensaje, pero no se puede usar para descifrarlo. • La privada: Es secreta y con ella se pueden descifrar los mensajes que se cifraron utilizando la clave pública. Resulta que la clave privada son dos números primos grandes y la pública es el producto de los dos primos grandes que componen la privada. Actividad 5: Programación matemática: Aplicaciones.
Programa: ContarFactorizaciones.
Diseña un solución en visual Basic que averigüe cuántos números enteros positivos menores que un cierto numero p, entero positivodado de antemano, tienen como únicos factores primos al 2, 3 ó 5. (Por ejemplo: 2, 8, 15, 30, 60, ...)
Página 5
Paco Villegas & Luis Cabello
ESTALMAT-Andalucía Oriental 07/08
Actividades
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática: Aplicaciones. _____________________________________________________________________________
Solución. El enunciado pide contar cuántos enteros n cumplen n >= 1, n < p, y n = 2a·3b·5c. Como el 1 no sirve, lo descartamos. Lo que vamos a hacer es un programa que pruebe todos los números enteros desde 2 hasta p, y los cuente cuando el número sólo tiene como factores primos al 2, 3 ó 5.
Vaquedando.
Primero hacemos una copia del valor del número en la variable
Eliminamos todos los doses de la factorización de Vaquedando. Para ello utilizamos Si tiene de factor al 2 Vaquedando lo dividimos por 2. Se repite este proceso hasta que estemos seguros de eliminar a todos los 2. Para ello utilizamos el bucle: Do While (Vaquedando Mod 2 = 0) Vaquedando = Vaquedando / 2 Loop Hacemos lo mismo con 3 y 5 Si al final Vaquedando es 1 hemos encontrado una factorización válida, la contabilizamos y pasamos al siguiente número. Se podría mejorar el programa para que el usuario eligiera los tres primos de la descomposición. El programa lo tienes en la carpeta sesión20.
Página 6
Paco Villegas & Luis Cabello
Programación matemática. Aplicaciones Recursividad Luis Cabello & Paco Villegas 26 de abril de 2008
ESTALMAT - ANDALUCÍA
Índice 1. Introducción: algoritmos 1.1. El coste de los algoritmos: . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1. El problema del viajante . . . . . . . . . . . . . . . . . . . . . . . . .
2 2 2
2. Funciones recursivas
5
3. Ejemplos 3.1. Factorial de un número . . . 3.2. Sucesión de Fibonacci . . . 3.3. Torres de Hanoi . . . . . . . 3.3.1. Un poco de historia: 3.3.2. Resolución . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
6 6 7 9 9 10
Introducción: algoritmos
1.
Estalmat-Andalucía 07/08
Introducción: algoritmos
La palabra algoritmo proviene del nombre de un matemático árabe del siglo IX llamado Muhammad ibn M˜usã al-Khwãrizm˜ı (es decir, Muhannmad, hijo de M˜usã, nacido en Khwãrezm), se trata de unos de los “padres” del Álgebra. Entre sus aportaciones destacar que fue uno los culpables de la introducción del sistema árabe de numeración. Pero ¿qué es un algoritmo? Conjunto de reglas que, aplicadas sistemáticamente a unos datos de entrada adecuados, resuelven un cierto problema en un número finito de pasos elementales [1] En el instituto y en el colegio se os han enseñado bastantes algoritmos (algunos que seguro se han olvidado ya), por ejemplo el del cálculo de la parte entera de la raíz cuadrada de un número natural, el algoritmo de Euclides para calcular el máximo común divisor, o por ejemplo, el algoritmo de la multiplicación (no confundirlo con las tablas de multiplicar, no son un algoritmo): Para multiplicar 4 · 3, se suma 4 tres veces (claro antes tengo que saber sumar), es decir: 4 · 3 = 4 + 4 + 4 = 8 + 4 = 12 NOTA: No penséis que multiplicar dos números es tan simple a nivel informático. Cuando se trabaja con números enteros muy grandes, puede ocurrir que los operandos se vuelvan tan grandes que no se puedan operar de forma directa (a partir del tamaño de palabra de la computadora). En este caso se usan algoritmos “especiales” (del tipo divide y vencerás) que permitan hacer los cálculos. Cuestiones antes de seguir: ¿Qué algoritmos conocéis más? Una receta de cocina ¿es un algoritmo? ¿Podemos encontrar un algoritmo para calcular de forma exacta el valor decimal del número π?
1.1.
El coste de los algoritmos: En informática (y matemáticas) uno de los mayores problemas que nos encontramos es el de medir la eficiencia de un algoritmo. Surge entonces la necesidad de estudiar qué recursos (en tiempo de cálculo y cantidad de memoria) se necesitan para conocer de antemano unos indicadores que, de alguna manera, nos permitan saber si el algoritmo encontrado es viable o no. Veamos esto con un ejemplo: 1.1.1.
El problema del viajante
Supongamos que tenemos una empresa de panadería que tiene 20 puntos de venta repartidos por la ciudad. Todas las mañanas, el furgón de reparto tiene que salir desde la panificadora y dejar en cada punto el pan y pasteles necesarios. El chófer de la furgoneta se plantea entonces el siguiente problema: Página 2
Luis Cabello & Paco Villegas
Introducción: algoritmos
Estalmat-Andalucía 07/08
¿Cuál el el camino más corto para pasar por todos los puntos de venta? Como es aficionado a las mates, piensa que esto es fácil: Sólo tengo que saber qué caminos hay disponibles, obtengo su suma de distancias y me quedo con las más corta, así que manos a la obra. Lo primero es saber cuántos caminos diferentes tengo: Veamos, si hay un sólo punto de reparto: el viaje de ida y vuelta, no hay problema Si son dos: casi que da igual, son dos caminos diferentes, pero en realidad es uno solo, le da igual Si son tres: tenemos 6 rutas (notar que el punto de partida no influye), pero se repiten dos a dos (podemos optar por hacer la ruta P123P o P321P, aunque son rutas diferentes a efectos de cómputo suponen el mismo tiempo. Es decir 3!2 = 3 ¿Con cuatro?: número de rutas 4!, pero caminos “diferentes” 4!2 Esto va bien, nuestro chófer concluye, para 20 punto de venta será (abre su wxMaxima y obtiene): 20! 2432902008176640000 = = 1216451004088320000 2 2 Conoce las distancias entre los puntos de venta y el tiempo que tarda en cada uno, así que sólo tiene que hacer esas “pocas de multiplicaciones y sumas” y listo, se queda con la más corta. Como además es aficionado a la informática, sabe que cada una de esas operaciones, en un ordenador actual, puede tardar 100 microsegundos (hay que hacer 20 multiplicaciones y una suma), así que el tiempo que tardará su ordenador será de: 1216451004088320000 · 100 · 10−6 s anos ˜ = 3857340,829808219 anos ˜ 60 · 60 · 24 · 365
¿Cómo? ¡más de 3 millones de años!, esto es fácil cambiará de ordenador y listo, pero claro, la mejora de capacidad de cálculo en un equipo de último modelo es de 1000 veces el que él Página 3
Luis Cabello & Paco Villegas
Introducción: algoritmos
Estalmat-Andalucía 07/08
tiene, así que sólo consigue saber que podrá resolver el problema (qué seguro es computable ya que sólo hay que hacer un número finito de cuentas) en poco más de 3000 años. Para pensar después: Si le aumentan el número de puntos de venta en tres más, ¿cuánto tiempo tardará el nuevo ordenador que se ha comprado? ¿Crees que la solución es cambiar de ordenador para resolver este tipo de problemas? Está claro que la forma de resolver el problema no es la adecuada, hay que buscar un algoritmo que permita resolver el problema en un tiempo real. A partir de problemas como este, surge la necesidad de intentar clasificar los problemas desde una perspectiva del tiempo de cómputo, y surge lo que se llama el análisis de la complejidad de los algoritmos. En informática se usa la notación O (grande) para intentar clasificar el coste teórico en tiempo de un algoritmo, algunas clases de complejidad, ordenadas de menor a mayor coste son: O(1), O(log n), O(n), O(n log n), O(n2 ), O(n3 ), O(2n ), O(n!), O(nn ) En general, los algoritmos de búsqueda son de orden O(n) (si buscamos una persona en un listado no ordenado de n personas, lo más “malo” que nos puede pasar es que esté la última). Los algoritmos estándar de ordenación, en general están en O(n2 ), los “buenos” en O(n log n). Ejemplo: con un algoritmo de tipo cuadrático, el tiempo empleado en ordenar un vector de 100,000 números enteros es unas 6,000 veces más lento que un algoritmo de los “buenos”. Es decir, el “bueno” tarda un minuto, el cuadrático tardaría ¡10 minutos! Para pensar después: busca en Internet algún algoritmo de ordenación de los costes anteriores Los algoritmos cuyo coste sea como máximo del tipo O(nk ) k ∈ Z se denominan polinomiales y son computables en un tiempo “razonable” (son tratables1 ). En la tabla que sigue hemos calculado de forma aproximada (en horas) el tiempo que tardaría cada uno de ellos si suponemos que el coste de una instrucción elemental en tiempo es un nanosegundo (10−9 s) Clase n = 10 n 2, 7 · 10−12 n log n 2, 7 · 10−12 n2 2, 7 · 10−11 n3 2, 7 · 10−10 n 2 2, 84 · 10−10 n! 1, 008 · 10−6 nn 2, 7 · 10−1
n = 100 2, 7 · 10−11 5, 5 · 10−11 2, 7 · 10−9 2, 7 · 10−7 3, 52 · 10+17 2, 59 · 10145 2, 7 · 10187
n = 200 5, 5 · 10−11 1, 28 · 10−10 1, 1 · 10−8 2, 2 · 10−6 4, 46 · 1047 2, 18 · 10362 4, 46 · 10447
n = 500 1, 38 · 10−10 3, 8 · 10−10 6, 94 · 10−8 3, 47 · 10−5 9, 02 · 10137 3, 38 · 101129 8, 48 · 101345
Está claro por qué a los algoritmos de coste mayor a los polinomiales se les llama intratables ¿a qué sí? Moraleja: no basta con encontrar el algoritmo que permita resolver un problema informático, además tiene que ser un buen algoritmo. 1 En
caso de coste mayor, se denominan intratables
Página 4
Luis Cabello & Paco Villegas
Funciones recursivas
2.
Estalmat-Andalucía 07/08
Funciones recursivas
Ya sabemos que ante un problema, no sólo hay que encontrar la forma de resolverlo, además hay que buscar un algoritmo que lo haga antes de que desesperemos (o algo peor). Cuando para resolver un problema, tenemos que repetir una serie de cálculos (muy parecidos) varias veces, los mecanismos que nos permiten los lenguajes de programación son la recursividad y la iteración (ya la habéis visto). La recursividad sólo es posible si un procedimiento o función pueden hacer referencia a sí mismos dentro de su propia definición: Siendo un poco más precisos, y para evitar el aparente círculo sin fin en esta definición, las instancias complejas de un proceso se definen en términos de instancias más simples, estando las finales más simples definidas de forma explícita. [5] Un ejemplo clásico (otro pueden ser los propios número naturales) para explicar en qué consiste una función recursiva, es el de la la función potencia de base entera y exponente natural, por ejemplo: 74 = 71 · 73 ¿qué significa esto? que, el problema de calcular una potencia de exponente 4 lo podemos reducir a calcular una de exponente 3, pero ¿cómo puedo calcular la de exponente 3, ¡pues! 73 = 71 · 72 Qué lío, pero ahora ¿cómo hago lo que me falta?, 72 = 71 · 71 Bien, pero el "ordenata" no es muy listo y ni siquiera sabe cuánto vale eso de 71 , no nos queda mas remedio que avisarle de que 70 = 1 (porque sí) y entonces, él sigue: 71 = 71 · 70 = 7 · 1 = 7 Ya está, deshaciendo el camino tenemos: 72 = 71 · 71 = 49 73 = 71 · 72 = 7 · 49 = 343 74 = 71 · 73 = 7 · 343 = 2401 Si formalizamos esto en lenguaje matemático tendríamos: 1 si n = 0 n 7 = n−1 7·7 si n ≥ 1 ¿Cuál sería la formula “recursiva” de la función potencia para cualquier base? 1 si n = 0 n x = n−1 x·x si n ≥ 1 Página 5
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
Y usando la nomenclatura matemática para las funciones (con dos letras2 , ¡qué fuerte!): 1 si n = 0 f (x, n) = x · f (x, n − 1) si n ≥ 1 En Visual Basic, podría ser:
Function Potencia(ByVal x As Integer, ByVal n As Integer) As Long If n = 0 Then Return 1 ' Si ya hemos llegado a exponente cero, devuelve 1 Else Return x*Potencia(x,n-1) End If End Function Otro ejemplo que ya hemos visto es el de la criba de Eratóstenes (275-195 a.C.): algoritmo para listar todos todos los números primos comprendidos entre el número 2 y un numero n 1. Escribir todos los números naturales comprendidos entre 2 y n 2. El primer número no tachado de la lista, y no contabilizado todavía es primo. Tachar todos sus múltiplos. Si no queda ningún múltiplo, terminar. 3. Volver al paso 2 Pero, como ya se ha visto, sigamos nosotros con otros ejemplos.
3.
Ejemplos
3.1.
Factorial de un número
Recordemos que la función factorial se escribe en lenguaje matemático como sigue 1 si n = 0 n! = n · (n − 1)! si n ≥ 1 Así, para calcular 4! lo haremos así: 4! = 4 · 3! 3! = 3 · 2! 2! = 2 · 1! 1! = 1 · 0! 2 de
dos variables
Página 6
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
0! = 1 Y escrita en forma de función sería f (x) =
1 si n = 0 x · f (x − 1) si n ≥ 1
Por fin, ya sabemos que 4! = 4 · 3 · 2 · 1 · 1 = 12. ¿por qué complicar esto tanto? pues porque así lo hacen los ordenadores. Y para que puedan hacerlo nosotros hemos de “programar” bien el algoritmo. ¿Serías capaz de programarlo? ¡seguro que sí!. Para facilitarte el trabajo te damos algunas pistas.
Function Factorial(ByVal N As Integer) As Long If N = 0 Then ' El factorial de 0 es 1 Return 1 ' No hacer más llamadas ElseIf N > 0 Then Return N * Factorial(N-1) ' Llamadas con N-1 End If End Function Cuando tengas el programa calcula 10! =
20! =
25! =
3.2.
Sucesión de Fibonacci
La sucesión de F IBONACCI es la sucesión infinita de números naturales 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, . . . donde el primer elemento es 0, el segundo es 1 y cada elemento restante es la suma de los dos anteriores. A cada elemento de esta sucesión se le llama número de Fibonacci. Se debe a Leonardo de Pisa, matemático italiano del siglo XIII también conocido como Fibonacci. La ideó como la solución a un problema de la cría de conejos: Cierto hombre tenía una pareja de conejos juntos en un lugar cerrado y uno desea saber cuántos son creados a partir de este par en un año cuando es su naturaleza parir otro par en un simple mes, y en el segundo mes los nacidos parir también. [3] Página 7
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
¿Cómo se construye? Se tienen que conocer los dos primeros valores, en este caso: f1 = 0 f2 = 1 y a partir de ellos obtenemos los demás teniendo en cuenta que el siguiente término es la suma de los dos anteriores, es decir: f3 = f1 + f2 = 0 + 1 = 1 f4 = f2 + f3 = 1 + 1 = 2 ...
fn = fn−2 + fn−1 En notación “más” funcional: si x = 1 0 1 si x = 2 f (x) = f (x − 2) + f (x − 1) si x ≥ 3 Ahora te toca, ¿puedes hacer un programa que calcule f (15)?. NOTA: Con este algoritmo para calcular términos para la sucesión de Fibonnaci, el número de sumas (y memoria) que utiliza el ordenador es muy elevado (está en el orden O(Φn ), √ donde Φ = 1+2 5 es el número de oro) es decir, es muy lento. Por ejemplo, para calcular f50 este algoritmo requiere efectuar 20365011073 sumas. [3] En este caso, los algoritmos usados son iterativos o una modificación del algoritmo recursivo (más complejo), versión Divide y Vencerás (Complejidad O(log n)) Página 8
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
Podemos modificar los dos primeros valores y obtenemos “otras” sucesiones de Fibonacci (Sucesiones de Fibonnaci Generalizadas). Por ejemplo, si los dos primeros valores son: f1 = 2 f2 = 2 ¿Cuanto vale f4 ? Práctica: haz un programa que permita obtener un término cualquiera de una Sucesión de Fibonnaci Generalizada. El programa te tendrá que pedir tres valores. Para la sucesión anterior halla f10
3.3.
Torres de Hanoi
Las Torres de Hanoi es un rompecabezas o juego matemático inventado en 1883 por el matemático francés Éduard Lucas d’Amiens3 . Consiste en tres varillas verticales y una serie de discos de diferente tamaño, la dificultad a la hora de resolver el juego dependerá del número de discos con el que juguemos.
No hay dos discos iguales e inicialmente están colocados de mayor a menor en la primera varilla. El juego consiste en pasar todos los discos a la tercera varilla manteniendo la figura original. Las reglas para conseguirlo son: Sólo se puede mover un disco cada vez Un disco de mayor tamaño no puede descansar sobre uno más pequeño que él. Sólo se puede mover en cada jugada el disco que se encuentra arriba de alguna de las tres varillas. 3.3.1.
Un poco de historia:
Una leyenda4 cuenta que Dios, después de crear el mundo, puso en la tierra tres barras hechas de diamantes, y 64 anillos de oro en la primera. Estos anillos son todos de diferente tamaño y al colocarlos se cuidó de ordenarlos por tamaño, estando el mayor en la parte inferior 3 Lo
publicó con el seudónimo de Profesor N. Claus de Siam, mandarín del colegio Li-Sou-Stian (trabajaba en el instituto de Sant Luis). Curioso el juego de letras del profe ¿verdad? 4 Esta leyenda fue un invento publicitario del creador del juego. [3] Página 9
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
y el menor en la parte superior. También creó un monasterio junto a las barras. A los monjes del monasterio encargó un trabajo: se trataba de que tenían que trasladar todos lo anillos a la segunda barra, pero con la condición de que la única operación permitida es trasladar un sólo anillo de una barra a la otra de manera que nunca puede situar un anillo sobre otro de menor radio. El trato es que el día que estos monjes consigan terminar el juego, el mundo acabará. Si la leyenda fuera cierta, ¿cuándo será el fin del mundo?, veamos:
Supongamos que los monjes mueven un anillo por segundo, de día y de noche y sin equivocarse, en ese caso tienen que realizar al menos 264 − 1 movimientos (¿por qué?), es decir, 584.942.417.355,07202 años (poco menos de 585 mil millones de años, más de 25 veces la edad estimada del universo y unas 100 veces la edad de la tierra).
3.3.2.
Resolución
Para resolver el problema analicemos un poco qué pasa con: Un anillo: no hay problema, un sólo movimiento. Es el caso “trivial” (el n=0). Movimientos 1 = 2 − 1 Página 10
Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
Con dos: en este caso, ponemos el disco pequeño en el centro y el problema se reduce a mover el disco más grande. Movimientos=3 = 2 · 1 + 1
Con tres: se tata de que primero conseguimos el paso dos (mover dos discos tal cual hemos hecho antes, pero cuidando la posición de las varillas). Movimientos5 7 = 2 ∗ 3 + 1 = 23 − 1
El truco a la hora de resolverlo reside en fijarnos en el disco más pequeño. El primer movimiento de ese disco se realiza hacia la varilla auxiliar (C). El disco número dos, se 5 Se
demuestra por inducción que si llamamos Mn al número de movimientos para una Torre de Hanoi de n discos, entonces: Mn = 2 · Mn−1 + 1 A partir de ella, se obtiene que Mn Página 11
= 2n − 1 Luis Cabello & Paco Villegas
Ejemplos
Estalmat-Andalucía 07/08
mueve a la varilla B. Después el disco pequeño se mueve a la varilla B para que quede sobre el disco dos. Es el momento de situar el disco grande, que está aún en la varilla A, en la varilla C. Ya casi, ahora el disco más pequeño regresa de la varilla A, el mediano se coloca ya en su sitio definitivo (varilla C) y finalmente ponemos el disco pequeño en su lugar. Fijémosnos en que para cuatro discos (o n), de lo que se trata es de que siempre tenemos que llegar a una situación en la que en se queda sólo el disco grande (en la varilla A) y en la varilla de en medio (B) tenemos una torre de Hanoi de 3 (en general (n − 1)) discos. Es decir, ¡recursividad!.
Si numeramos los discos desde 1 hasta n, y llamamos A a la primera varilla, C a la tercera (destino) y B a la intermedia (auxiliar) el algoritmo de la función sería el siguiente: 1. Si n = 1, lleva el disco de la barra A a la C y termina 2. Mueve la torre de (n − 1) fichas de la barra Aa la B 3. Lleva el disco grande (que se ha quedado solo en la varilla origen (A)) a la varilla destino (C) 4. Traslada la torre de (n − 1) discos, usando este mismo algoritmo, de (B) a (C), pero usando ahora como varilla auxiliar la (A) En forma de “función” fun Hanoi(origen, destino, auxiliar, n) si n = 1 entonces escribe Mueve disco de poste origen a poste destino si no hacer Hanoi(origen,auxiliar, destino, n − 1) Hanoi(origen,destino, axiliar, 1) Hanoi(auxiliar,destino, origen, n − 1) fsi ffun Para pensar después: 1. A partir de la función anterior, ¿podemos calcular ya en nuestro ordenador Hanoi(64, A, B)? Página 12
Luis Cabello & Paco Villegas
REFERENCIAS
Estalmat-Andalucía 07/08
2. Más cuestiones matemáticas de interés: revisa la Web http://descartes.cnice.mecd.es/materiales_didacticos/rompecabezas/ TorresHanoi.htm y te proponemos que realices las actividades que te propone el autor.
Referencias [1] Peña Marí, R. (2006), De Euclides a Java. Historia de los algoritmos y de los lenguajes de programación, Madrid, Nivola libros y ediciones, S.L. [2] Garfunkel, S. y más (1999), Las matemáticas en la vida cotidiana, Madrid, Addison-Wesley Iberoamericana españa, S.A. [3] Wikipedia, http://es.wikipedia.org/wiki/Portada [4] Brassard, G. y Bratley, P. (1997), Fundamentos de Algoritmia, Madrid, Prentice Hall. [5] Charte, Francisco (2002), Programación con Visual Basic .NET, Madrid, Anaya Multimedia. [6] Gonzalo Arroyo, J. y Rodríguez Artacho, M. (1997), Esquemas algorítmicos: enfoque metodológico y problemas resueltos, Madrid, UNED.
Página 13
Luis Cabello & Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________
Programación matemática. Aplicaciones Recursividad SOLUCIONES
Índice de contenido Potencia de base entera y exponente natural....................................................................................1 Factorial de un número.....................................................................................................................5 Sucesión de Fibonnacci....................................................................................................................8 Torres de Hanoi...............................................................................................................................13 1. POTENCIA DE BASE ENTERA Y EXPONENTE NATURAL Para crear una aplicación que calcule el factorial de un número crearemos un nuevo proyecto del tipo Aplicación para Windows al que podríamos llamar CalculoPotencias.
Una vez creado el proyecto lo guardaremos con el mismo nombre. En el formulario que se crea añadiremos varios componentes a través de los cuales el usuario podrá interactuar con la aplicación. En primer lugar añadiremos desde el cuadro de herramientas dos campos de texto (TextBox) donde se introducirán la base y el exponente. Les daremos un nombre representativo a los dos controles que hemos creado modificando la propiedad Name. Al primer campo de texto le llamaremos cajaTextoBase y al segundo cajaTextoExponente.
Página 1
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ También añadiremos dos etiquetas (Label) asociadas a cada uno de los campos de texto. Modificaremos la propiedad Text de cada una de ellas y, al igual que hacíamos con los campos de texto, les cambiaremos también el nombre modificando la propiedad Name. Etiqueta 1: ● Name: etiquetaBase ● Text : Base Etiqueta 2: ● Name: etiquetaExponente ● Text : Exponente Añadiremos el botón responsable de llamar a la función encargada de realizar el cálculo y, por último, una nueva etiqueta en la que se escribirá el valor de la potencia. A estos dos últimos controles les modificaremos las siguientes propiedades: Botón: ● ●
Name: botonCalcular Text: Calcular
Etiqueta: ● Name: etiquetaPotencia. ● Text: POTENCIA Para terminar cambiaremos la propiedad Text del formulario por Cálculo de potencias. Una vez creado el formulario tendremos que decirle a nuestra aplicación que responda al hacer clik sobre el botón calculando la potencia y escribiéndola en la etiqueta correspondiente. Para ello haremos doble click sobre el botón, generándose de forma automática el siguiente método: Private Sub botonCalcular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles botonCalcular.Click End Sub
Dentro del método escribiremos: Private Sub botonCalcular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles botonCalcular.Click
(2^32)-1]
'definimos las variables que vamos a utilizar Dim base, exponente As Integer 'Enteros de
32
bits
[-2^32,
Dim potencia As Long 'Enteros de 64 bits [-2^64, (2^64)-1] 'obtenemos los números introducidos en las cajas de texto base = CInt(Me.cajaTextoBase.Text) exponente = CInt(Me.cajaTextoExponente.Text) 'calculamos la potencia
Página 2
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ potencia = obtenerPotencia(base, exponente) 'escribimos la potencia en la etiqueta correspondiente Me.etiquetaPotencia.Text = Me.cajaTextoBase.Text + " ^ " Me.cajaTextoExponente.Text + " = " + potencia.ToString()
+
End Sub
Pero todavía nos falta lo más importante, nuestra aplicación aún no sabe cómo obtener la potencia de un número. Para ello deberemos crear una función (obtenerPotencia) a la que le pasaremos como parámetros la base y el exponente y nos devolverá como resultado la potencia. Una función no es más que una secuencia de instrucciones que realizan una determinada tarea y que al terminar ésta devuelven un valor o resultado. Las funciones se pueden llamar desde otras partes de la aplicación. A las funciones se les pueden pasar parámetros que podrán ser utilizados dentro de la función para conseguir la tarea. Hemos visto anteriormente que al pulsar sobre el botón botonCalcular se ejecuta el procedimiento botonCalcular_Click. La diferencia entre una función y un procedimiento es que los procedimientos no devuelven ningún valor. La forma de declarar una función es muy parecida a la de los procedimientos, basta con sustituir Sub por Function e indicarle el tipo del valor que va a devolver. De esta forma podríamos crear nuestra función obtenerPotencia: Function obtenerPotencia(ByVal Integer) As Long
base
As
Integer,
ByVal
exponente
As
If exponente = 0 Then Return 1 Else Return base * obtenerPotencia(base, exponente - 1) End If End Function
A nuestra función le pasamos dos parámetros de tipo entero (Integer): la base y el exponente de la potencia, y le indicamos, al final de la definición, el tipo del resultado que devuelve (Long). Para devolver el resultado se utiliza la palabra reservada Return. Cuando se llega a alguna línea en la que se encuentra esta palabra la función finaliza, devolviendo el valor que aparece tras Return. Ahora vamos a analizar el código encargado de calcular la potencia: If exponente = 0 Then Return 1 Else Return base * obtenerPotencia(base, exponente - 1) End If
Si lo observamos un poco veremos que es bastante similar al algoritmo que hemos visto anteriormente para el cálculo de potencias: ● Si el exponente es 0 la función devuelve como resultado 1. ● Si el expontente es distinto de 0 devuelve:
Página 3
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ base * obtenerPotencia(base, exponente – 1
(base * (base ^ (exponente – 1))) Veamos un ejemplo concreto analizando cómo actúa esta función. Si como base introducimos 2 y como exponente 3 (2 ^ 3) tendríamos: potencia = obtenerPotencia(2, 3)
obtenerPotencia(2, 3) = 2 * obtenerPotencia (2, 2) obtenerPotencia(2, 2) = 2 * obtenerPotencia (2, 1) obtenerPotencia(2, 1) = 2 * obtenerPotencia (2, 0) obtenerPotencia(2, 0) = 1 Al llegar a exponente 0 finaliza el proceso y se van sustituyendo las sucesivas funciones por su valor hasta obtener el resultado buscado: obtenerPotencia(2, 0) = 1 obtenerPotencia(2, 1) = 2 * 1 = 2 obtenerPotencia(2, 2) = 2 * 2 = 4 obtenerPotencia(2, 3) = 2 * 4 = 8 potencia = 8
2. FACTORIAL DE UN NÚMERO Para crear la aplicación encargada de calcular el factorial de un número seguiremos los mismos pasos que en el ejemplo anterior: ● ● ●
Crearemos un proyecto basado en una aplicación para Windows llamado CalculoFactorial Guardaremos el proyecto con el mismo nombre Añadiremos al formulario los siguientes controles: ○ Caja de texto ■ Name: cajaTextoNumero ○ Etiqueta ■ Name: etiquetaNumero ■ Text: Introducir el número ○ Botón: ■ Name: botonCalcular ■ Text: Calcular ○ Etiqueta: ■ Name: etiquetaFactorial ■ Text: FACTORIAL
Página 4
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ Haremos doble click sobre el botón para que se cree el método que capturará el evento generado al hacer click sobre dicho botón, y escribiremos el siguiente código: Private Sub botonCalcular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles botonCalcular.Click 'definimos las variables que vamos a utilizar Dim numero As Integer 'Enteros de 32 bits [-2^32, (2^32)-1] Dim factorial As Long 'Enteros de 64 bits [-2^64, (2^64)-1] 'obtenemos el número entero introducido en la caja de texto numero = CInt(Me.cajaTextoNumero.Text) 'calculamos el factorial
factorial = obtenerFactorial(numero) 'escribimos el factorial en la etiqueta correspondiente Me.etiquetaFactorial.Text = "El factorial de " + Me.cajaTextoNumero.Text + " es " + factorial.ToString() End Sub
Si observáis el código es bastante similar al que habíamos escrito para calcular las potencias. En este caso utilizamos una nueva función (obtenerFactorial), a la que pasamos como parámetro el numero del que queremos obtener el factorial. También podemos observar cómo el tipo de las variables que definimos son los mismos. El número lo definimos como un entero de 32 bits (Integer) y el factorial como un entero de 64 bits (Long). La función sería la siguiente: Function obtenerFactorial(ByVal numero As Integer) As Long If numero = 0 Then Return 1 'El factorial de 0 es 1 ElseIf numero > 0 Then Return numero * obtenerFactorial(numero - 1) End If End Function
A pesar de que la función devuelve un entero de tipo Long, la aplicación no permite calcular el factorial de números mayores de 20. Esto es debido a que una variable de tipo Long permite valores comprendidos entre -9223372036854775808 y 9223372036854775807, y el factorial de 21 es mayor que esta cifra. Si observamos el código de nuevo veremos que es similar al algoritmo matemático utilizado para el cálculo del factorial. ● ●
Si el número vale 0 devuelve (Return) 1 Si el número es mayor de 0 la función devuelve (Return) numero * obtenerFactorial(numero – 1) Página 5
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ Veamos un ejemplo concreto sobre cómo actúa la función calculando el factorial de 4: factorial = obtenerFactorial(4) obtenerFactorial(4) = 4 * obtenerFactorial(3) obtenerfactorial(3) = 3 * obtenerFactorial(2) obtenerFactorial(2) = 2 * obtenerFactorial(1) obtenerFactorial(1) = 1 * obtenerFactorial(0) obtenerFactorial(0) = 1 Finaliza el proceso Al llegar el número a 0 finaliza el proceso y se van sustituyendo las sucesivas funciones por su valor hasta obtener el resultado buscado: obtenerFactorial(0) = 1 obtenerFactorial(1) = 1 * 1 = 1 obtenerFactorial(2) = 2 * 1 = 2 obtenerFactorial(3) = 3 * 2 = 6 obtenerFactorial(4) = 4 * 6 = 24 Estamos suponiendo que los número introducidos para calcular el factorial son siempre enteros positivos, ya que en caso contrario los resultados no serían correctos. Por ejemplo, si introducimos números negativos, la función da como resultado 0. Está claro que este resultado no es correcto. Para evitar este tipo de errores habría que comprobar antes de llamar a la función que el texto introducido es un entero positivo. Os dejamos este trabajo como mejora de la aplicación. 3. SUCESIÓN DE FIBONACCI De forma similar a los ejemplos anteriores crearemos un nuevo proyecto llamado SucesionFibonacci, añadiendo al formulario los siguientes controles: ● ●
●
Caja de texto Name: cajaTextoElemento Etiqueta Name: etiquetaElemento Text: Elemento nº Botón: Name: botonCalcular Text: Calcular
Página 6
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ ● Etiqueta: Name: etiquetaFibonacci Text: FIBONACCI De forma similar a cómo hicimos en los ejemplos anteriores, en el método que se genera al hacer doble click en el botón Calcular escribiremos: Private Sub botonCalcular_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles botonCalcular.Click 'definimos las variables que vamos a utilizar Dim elemento As Integer 'Enteros de 32 bits [-2^32, (2^32)-1] Dim fibonacci As Long 'Enteros de 64 bits [-2^64, (2^64)-1] 'obtenemos el número entero introducido en la caja de texto elemento = CInt(Me.cajaTextoElemento.Text) 'calculamos el valor del elemento en la sucesión de Fibonacci fibonacci = obtenerFibonacci(elemento)
"
'escribimos el valor en la etiqueta correspondiente Me.etiquetaFibonacci.Text = "El valor del elemento " + Me.cajaTextoElemento.Text + " de la sucesión de Fibonacci es + fibonacci.ToString() End Sub
En este método para obtener el valor del elemento llamamos a la función obtenerFibonacci pasándole como argumento el elemento. El código de esta función podría ser el siguiente: Function obtenerFibonacci(ByVal elemento As Integer) As Long If elemento = 1 Then Return 0 'El primer elemento de la sucesión vale 0 ElseIf elemento = 2 Then Return 1 'El segundo elemento de la sucesión vale 1 ElseIf elemento > 2 Then 'A partir del tercer elemento el valor es la suma de los dos anteriores Return obtenerFibonacci(elemento - 1) + obtenerFibonacci(elemento - 2) End If End Function
De nuevo partimos de la base de que el texto introducido en la caja de texto es un valor válido (entero positivo). Al igual que los otros ejemplos os dejamos como ejercicio mejorar la aplicación para que realice las comprobaciones necesarias, y prevenir de esta forma los posibles errores que se producirán si se introducen valores no válidos. Veamos un ejemplo concreto sobre cómo actúa la función calculando el valor del elemento 5: fibonacci = obtenerFibonacci(5)
Página 7
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________
obtenerFibonacci(5) = obtenerFibonacci(4) + obtenerFibonacci(3) obtenerFibonacci(4) = obtenerFibonacci(3) + obtenerFibonacci(2) obtenerFibonacci(3) = obtenerFibonacci(2) + obtenerFibonacci(1) obtenerFibonacci(2) = 1 obtenerFibonacci(1) = 0 obtenerFibonacci(2) = 1 obtenerFibonacci(3) = obtenerFibonacci(2) + obtenerFibonacci(1) obtenerFibonacci(2) = 1 obtenerFibonacci(1) = 0 A pesar de que hemos seleccionado un elemento de los primeros, podéis observar como las funciones se repiten, esto hace que el número de sumas se vaya incrementando a medida que avanzamos en la sucesión.
Solución iterativa: para hacer después de la sesión Al igual que ocurre con la mayoría de los problemas matemáticos, no existe una única forma de solucionarlos. En este caso vamos a mostrar como se podría hacer de forma iterativa. Agregamos al formulario un nuevo botón con las propiedades: ● Botón: Name: botonCalcularIterativo Text: Calcular iterativo Como siempre hacemos doble clik en este nuevo botón y escribimos: Private Sub botonCalcularIterativo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles botonCalcularIterativo.Click 'definimos las variables que vamos a utilizar Dim elemento As Integer 'Enteros de 32 bits [-2^32, (2^32)-1] Dim fibonacci As Long 'Enteros de 64 bits [-2^64, (2^64)-1] 'obtenemos el número entero introducido en la caja de texto elemento = CInt(Me.cajaTextoElemento.Text) 'calculamos el valor del elemento en la sucesión de Fibonacci
fibonacci = obtenerFibonacciIterativo(elemento) Página 8
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ 'escribimos el valor en la etiqueta correspondiente Me.etiquetaFibonacci.Text = "ITERATVO: El valor del elemento " + Me.cajaTextoElemento.Text + " de la sucesión de Fibonacci es + fibonacci.ToString()
" End Sub
Y creamos una nueva función llamada obtenerFibonacciIterativo: Function obtenerFibonacciIterativo(ByVal elemento As Integer) As Long 'definimos las variables que vamos a utilizar Dim penultimo, ultimo, actual As Long Dim i As Integer 'iniciamos los valores de las variables de los dos valores iniciales penultimo = 0 ultimo = 1 If elemento = 1 Then Return 0 'El primer elemento de la sucesión vale 0 ElseIf elemento = 2 Then Return 1 'El segundo elemento de la sucesión vale 1 ElseIf elemento > 2 Then 'A partir del tercer elemento el valor es la suma de los dos anteriores 'Calculamos el valor de cada elemento de forma iterativa hasta llegar al elemento buscado ' For i = 3 To elemento Step 1 actual = ultimo + penultimo penultimo = ultimo ultimo = actual Next Return actual End If End Function
Intentad obtener el valor del elemento 40 con cada una de las funciones y observad el tiempo que tarda cada una de ellas. Veamos cómo actúa este método para calcular el valor del elmento 5 de la sucesión de Fibonacci: fibonacci = obtenerFibonacciIterativo(5) penultimo = 0 ultimo = 1 Desde el elemento 3 hasta el 5 elemento 3:
Página 9
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ actual = ultimo (1) + penultimo (0) = 1 penultimo = 1 ultimo = 1 elemento 4: actual = ultimo (1) + penultimo (1) = 2 penultimo = 2 ultimo = 1 elemento 5: actual = ultimo (2) + penultimo (1) = 3 penultimo = 3 ultimo = 2 Termina el bucle y la función devuelve (Return) el valor actual (3) 4. TORRES DE HANOI Para este ejemplo crearemos un proyecto llamado TorresHanoi.
Añadiremos los controles necesarios para que el formulario quede como el de la imagen: Los controles que hemos añadido al formulario tienen las siguientes propiedades: Etiquetas: ● Name: etiquetaAnillos ● Text: Nº de anillos ● ● ● ● ● ● ● ● ● ●
Name: etiquetaOrigen Text: A = Origen Name: etiquetaAuxiliar Text: B = Auxiliar Name: etiquetaDestino Text: C = Destino Name: etiquetaHanoi Text: MOVIMIENTOS TORRES DE HANOI Name: etiquetaMovimientos Text: Nº total de movimientos
Cajas de texto:
Página 10
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ ● Name: cajaTextoAnillos ● ● ●
Name: cajaTextoMovimientos Multiline: true ScrollBars: Vertical
Botón: ● ●
Name: botonVerMoimientos Texto: Ver movimientos
En la caja de texto donde irán apareciendo los movimientos de los anillos (cajaTextoMovimientos) hemos modificado las propiedades Multiline a true para que admita varias líneas, y la propiedad ScrollBars a Vertical para que muestre una barra de desplazamiento vertical en caso de que el número de líneas a mostrar sea mayor que el número de líneas que caben en la caja de texto. También necesitamos hacer algo que no habíamos hecho en los ejemplos anteriores, y es declarar variables a nivel de la clase Form1, ya que las vamos a necesitar en varios de los métodos utilizados. Si observáis el código de los ejemplos anteriores podéis observar que hemos declarado variables (mediante la palabra reservada Dim) dentro de los métodos, ya que estas variables sólo iban a ser utilizadas dentro de dichos métodos (funciones o procedimientos). Las variables declaradas dentro de un método sólo son visibles dentro de ese método. Declaramos las siguientes variables: Public Class Form1 Dim movimientos As Long Dim origen, destino, auxiliar As String ................................. ................................. End Class
En el método que se genera al hacer doble click sobre el botón Ver movimientos escribimos el siguiente código: ByVal
Private Sub botonVerMovimientos_Click(ByVal sender As System.Object, e As System.EventArgs) Handles botonVerMovimientos.Click 'definimos las variables que vamos a utilizar Dim anillos As Integer 'Enteros de 32 bits [-2^32, (2^32)-1] 'obtenemos el número entero introducido en la caja de texto anillos = CInt(Me.cajaTextoAnillos.Text) 'reiniciamos el número de movimientos a 0, así como la letra asignada a cada barra
Página 11
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ 'y borramos el texto de cajaTextoMovimientos movimientos = 0 origen = "A" auxiliar = "B" destino = "C" Me.cajaTextoMovimientos.Text = "" 'realizamos los movimientos para pasar los anillos del origen al destino
moverAnillos(origen, destino, auxiliar, anillos) 'escribimos el valor en la etiqueta correspondiente Me.etiquetaMovimientos.Text = "Nº total de movimientos = " + movimientos.ToString() End Sub
Cada vez que pulsamos sobre el botón le damos los valores iniciales a las variables movimientos, origen, destino y auxiliar. También llamamos al procedimiento moverAnillos que tiene el siguiente código: Sub moverAnillos(ByVal origen As String, ByVal destino As String, ByVal auxiliar As String, ByVal anillos As Integer) If anillos = 1 Then movimientos = movimientos + 1 Me.cajaTextoMovimientos.Text = Me.cajaTextoMovimientos.Text + movimientos.ToString + ".- Mover anillo de " + origen + " a " + destino + vbCrLf Else 'movemos los n-1 anillos de arriba del origen al auxiliar moverAnillos(origen, auxiliar, destino, anillos - 1) 'movemos el que queda sólo, del origen al destino moverAnillos(origen, destino, auxiliar, 1) 'volvemos a mover los n-1 anillos del auxiliar al destino moverAnillos(auxiliar, destino, origen, anillos - 1) End If End Sub
Ya que en este ejemplo no necesitamos que el método devuelva ningún valor, creamos un procedimiento en lugar de una función. Para ello cambiamos la palabra Function que utilizábamos para crear las funciones por la palabra Sub. Veamos cómo funciona este procedimiento: Si el número de anillos a mover es 1: ● se realizará el movimiento, contabilizándose éste ● se escribirá en la caja de texto una nueva línea con el número del Página 12
Luis Cabello y Paco Villegas
ESTALMAT-Andalucía Oriental
Actividades 07/08
Sesión: 20 Fecha: 26-04-2008 Título: Programación Matemática. Aplicaciones _____________________________________________________________________________ movimiento y los datos del origen y el destino. Para que cada movimiento se escriba en una nueva línea añadimos al final del texto vbCrLf, que equivale a un salto de línea. Si el número de anillos a mover es mayor de 1: ● movemos los n-1 anillos de arriba del origen al auxiliar, convirtiéndose el auxiliar en el destino y el destino en el auxiliar. ● movemos el que queda sólo del origen al destino, quedándose ya correctamente colocado ● volvemos a mover los n-1 anillos del auxiliar al destino, convirtiéndose el auxiliar en el origen, y el origen en el auxiliar
Página 13
Luis Cabello y Paco Villegas