Manos a la obra: Recursión, división y listas Taller de Álgebra I Cuatrimestre de verano de 2015 Calentando motores La clase pasada vimos ejemplos de definiciones recursivas. Hoy vamos a continuar con el tema. Recordemos un ejemplo: reverso :: [ Integer ] -> [ Integer ] reverso [] = [] reverso xs = ( reverso ( tail xs ) ) ++ [ head xs ] Truco, cómo pensar recursivamente con listas Dada una lista cualquiera: [1,2,3,4,5] Si ya tengo el resultado recursivo sobre la cola de la lista, es decir el reverso de [2,3,4,5], ¿cómo puedo combinarlo con la cabeza de la lista (1) para obtener el resultado que quiero? Implementar la siguiente función potencia :: Float -> Integer -> Float que calcule la potencia: potencia a n an (n ≥ 0). Debe utilizarse el producto y no ** ni ^ ni ^ ^ Algoritmo de división Teorema: Dados a, d ∈ Z, d 6= 0, existen únicos q, r ∈ Z tales que a = qd + r , 0 ≤ r < |d|. Demostración: (caso a ≥ 0, d > 0). Por inducción en a. Si 0 ≤ a < d, tomamos q = 0, r = a. Si no, dividimos a − d por d. Eso da un cociente q 0 y un resto r 0 . Tomamos r = r 0 , q = q 0 + 1. Implementar la siguiente función division :: Integer -> Integer -> (Integer, Integer) division a d (q, r) Debe funcionar para a ≥ 0, d > 0 y no se pueden usar div, mod ni /. Algoritmo de división division :: Integer -> Integer -> ( Integer , Integer ) division a d | a < d = (0 , a ) | otherwise = ( fst ( division (a - d ) d ) + 1 , snd ( division (a - d ) d ) ) ¿Se puede no poner dos veces division (a-d) d? Sı́: division :: Integer -> Integer -> ( Integer , Integer ) division a d | a < d = (0 , a ) | otherwise = ( fst qr ’ + 1 , snd qr ’) where qr ’ = division (a - d ) d Primos Un número p ∈ Z es primo si tiene 4 divisores. Un entero p > 1 es primo si ningún natural k tal que 1 < k < p divide a p. ¿Se puede hacer una función esPrimo :: Integer -> Bool que diga si un número es primo o no? Una posibilidad serı́a buscar todos los divisores. Algo como divisores(n) = {k ∈ Z | 1 ≤ k ≤ n y k|n} Esto se puede hacer en Haskell de forma directa, aunque todavı́a no vimos cómo. Podemos sin embargo definir una lista “parcial” de divisores: divParcial(n, m) = {k ∈ Z | 1 ≤ k ≤ m y k|n} Implementar las siguientes funciones divParcial :: Integer -> Integer -> [Integer] Tiene que funcionar bien divParcial n m cuando m ≤ n. Utilizando divParcial, programar divisores :: Integer -> [Integer] Utilizando divisores, programar esPrimo :: Integer -> Bool Primos divParcial :: Integer -> Integer -> [ Integer ] divParcial n 1 = [1] divParcial n m | mod n m == 0 = m : divParcial n (m -1) | otherwise = divParcial n (m -1) divisores :: Integer -> [ Integer ] divisores n = divParcial n n esPrimo :: Integer -> Bool esPrimo n = length ( divisores n ) == 2 Ejercicios Implementar mult, que sirve para multiplicar matrices de 2 × 2; mult :: ( Float , Float , Float , Float ) -> ( Float , Float , Float , Float ) -> ( Float , Float , Float , Float ) Usando mult, implementar una función que sirva para calcular potencias de matrices de 2 × 2. Adaptar esPrimo para todo p ∈ Z. Adaptar division para a < 0 y/o d < 0. Observación: las funciones div y mod de Haskell no coinciden con el algoritmo de división cuando d < 0. Ver también quot y rem. Implementar noT ien e D i v i s o r e s Hasta :: Integer -> Integer -> Bool noTieneDivisoresHasta m n da True sii ningún número entre 2 y m divide a n. Utilizando noTieneDivisoresHasta, programar esPrimo :: Integer -> Bool Algoritmo de Euclides El Algoritmo de Euclides calcula el máximo común divisor entre dos números a, b ∈ Z+ . Se basa en que si a, b ∈ Z y q ∈ Z es un número cualquiera, entonces (a : b) = (a + qb : b). Si q y r son el cociente y el resto de la división de a por b, tenemos a = qb + r , entonces a − qb = r . Por lo tanto, (a : b) = (a − qb : b) = (r : b) = (b : r ) Por ejemplo, para calcular (108 : 30), se hace: 1 = (108 : 30) -- Dividimos 108 por 30, q = 3, r = 18 2 = (30 : 18) -- Dividimos 30 por 18, q = 1, r = 12 3 = (18 : 12) -- q = 1, r = 6 4 = (12 : 6) -- q = 2, r = 0 5 = (6 : 0) 6 =6 Algoritmo de Euclides Para hacer Programar en Haskell mcd :: Integer -> Integer -> Integer que dé el mcd. El peor caso del algoritmo se obtiene cuando a y b son dos números consecutivos de la sucesión de Fibonacci (Lamé, 1844). El número de divisiones que realiza el algoritmo nunca supera 5 veces el número de dı́gitos de a y b, con lo cual la cantidad de llamadas recursivas está acotada por 5 log10 (max{|a|, |b|}). Ejercicios sobre Listas Implementar las siguientes funciones pertenece :: Integer -> [Integer] -> Bool que indica si un elemento aparece en la lista. Implementar la función hayRepetidos :: [Integer] -> Bool que indica si una lista tiene elementos repetidos. menores :: Integer -> [Integer] -> [Integer] que calcula los elementos de la lista que son menores al primer parámetro. Últimas quitar :: Integer -> [Integer] -> [Integer] elimina la primera aparición del elemento en la lista. maximo :: [Integer] -> Integer que dada una lista no vacı́a calcula el mayor elemento de la misma. Cambio de base en Haskell Problema: Dados a, b ∈ Z+ con b > 1, escribir el número a en base b. Por ejemplo ... 1 2 3 Si a = 10 y b = 2, entonces la respuesta es 10102 . Si a = 17 y b = 3, entonces la respuesta es 1223 . Si a = 145 y b = 10, entonces la respuesta es 14510 . Implementar en Haskell La función enBase :: Integer -> Integer -> [Integer] Esta función toma un número y lo transforma a la base pasada como segundo parámetro. Para ello devuelve una lista representando al número. Ejemplo: enBase 3 17 [1,2,2]. La función deBase :: Integer -> [Integer] -> Integer que toma una base y un número en forma de la lista de sus dı́gitos y devuelve el número. deBase 3 [1,2,2] 17. Ejercicios Parte 1: La conjetura del 196 Los números de Lychrel (http://gaussianos.com/la-conjetura-del-196/) Tomemos un número natural cualquiera, por ejemplo el 180 Si damos vuelta sus cifras (081) y lo sumamos al original nos queda 180 + 081 = 261 Ahora repetimos el proceso con el 261: 261 + 162 = 423 Lo mismo con el 423: 423 + 324 = 747. Un número capicúa! ¿Será verdad que para cualquier natural, siempre llegamos a un número capicúa? Los llamados números de Lychrel son los números naturales en base 10 que no llegan a dar un número capicúa como resultado del proceso. Implementar La función capicuaPara :: [Integer] -> [Integer] Que toma un número y realiza el proceso hasta que se consiga su número capicua asociado. Ejemplo: capicuaPara [1,8,0] [7,4,7]. Preparar el “ctrl+c” y ejecutar capicuaPara [1,9,6], ¿qué ocurre? Ejercicios Parte 2 1 2 3 4 5 6 7 8 Consideremos la sucesión t1 , t2 , . . . , tal que tn es la cantidad de llamadas recursivas del Algoritmo de Euclides cuando se ejecuta con a = Fn y b = Fn−1 (donde Fn representa el n-ésimo número de Fibonacci). Escribir una función que tome como parámetro un entero n y que retorne tn . Como función preliminar, analizar cómo cambia la función mcd si uno quiere contar la cantidad veces que se llama a la recursión. Escribir una función que cambie de base un número. Debe tomar dos enteros b1 y b2 y un entero a representado en base b1 (expresado como una lista de enteros) y retornar a en base b2 (también como una lista de enteros). Escribir una función similar a maximo pero que retorne el mı́nimo. (*) Escribir una función que indique si una lista está ordenada. Escribir una función que elimine los repetidos, dejando sólo la última aparición de cada uno. (*) Idem anterior pero dejando la primera aparición de cada uno. Escribir una función que reciba dos listas ordenadas en forma no-decreciente, y que retorne una lista que contenga la unión de los elementos de las dos listas recibidas, y que también esté ordenada en forma no-decreciente. Por ejemplo, al pasarle [2, 3, 7] y [1, 3, 3, 9], deberı́a reducir a [1, 2, 3, 3, 3, 7, 9]. (difı́cil) Escribir una función que, dada una lista, retorne la misma pero ordenada de menor a mayor.