Clase 2

Anuncio
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.
Descargar