Recursión 2 + Recursión sobre listas

Anuncio
Recursión 2 + Recursión sobre listas
Taller de Álgebra I
Verano de 2016
Recordando Recursión
Ejercicios
I
potencia :: Float -> Integer -> Float
Que calcule la potencia: an con (n ≥ 0).
Debe utilizarse el producto (no ^, ∗∗ ni similares).
I
(Ejercicio 4 clase pasada) sumaImparesCuyoCuadSeaMenorQue :: Integer -> Integer
Suma los números impares positivos cuyo cuadrado sea menor que n.
sumaImparesCuyoCuadSeaMenorQue 30
1 + 3 + 5
9.
s u m a I m p a r e s C u y o C u a d S e a M e n o r Q u e :: Integer -> Integer
s u m a I m p a r e s C u y o C u a d S e a M e n o r Q u e umbral = sumaAuxiliar ... ...
-- Construimos una funcion que nos facilita el trabajo
sumaAuxiliar :: Integer -> Integer -> Integer
sumaAuxiliar umbral num
| num ^ 2 >= umbral = 0
| otherwise = num + sumaAuxiliar umbral ( num + 2)
Algoritmo de división
Teorema: Dados a, d ∈ Z, d 6= 0, existen únicos q, r ∈ Z tales que
I
a = qd + r ,
I
0 ≤ r < |d|.
Idea de la demostración: (caso a ≥ 0, d > 0). Por inducción en a.
I
Si 0 ≤ a < d, tomamos q = 0, r = a.
I
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)
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 )
division a d | otherwise
= ( fst ( division (a - d ) d ) + 1 ,
snd ( division (a - d ) d ) )
¿Se puede evitar la doble evaluación de division (a-d) d?
Sı́:
division :: Integer -> Integer -> ( Integer , Integer )
division a d | a < d
= (0 , a )
division a d | otherwise
= ( fst qr ' + 1 , snd qr ')
where qr ' = division (a - d ) d
Primos
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 entero mayor a
cero 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.
Pero podemos definir una lista parcial de divisores:
divisoresHasta(n, m) = {k ∈ Z | 1 ≤ k ≤ m y k|n}
Implementar las siguientes funciones
I
divisoresHasta :: Integer -> Integer -> [Integer]
Tiene que funcionar bien divisoresHasta n m cuando m ≤ n.
I
Utilizando divisoresHasta, programar
divisores :: Integer -> [Integer]
I
Utilizando divisores, programar
esPrimo :: Integer -> Bool
Primos
divis oresHas ta :: Integer -> Integer -> [ Integer ]
divis oresHas ta n 1 = [1]
divis oresHas ta n m | mod n m == 0 = m : div isoresHa sta n (m -1)
| otherwise
= d ivisore sHasta n (m -1)
divisores :: Integer -> [ Integer ]
divisores n = d ivisores Hasta n n
esPrimo :: Integer -> Bool
esPrimo n = length ( divisores n ) == 2
Recursión sobre Listas
No todo son números
suma :: [ Integer ] -> Integer
suma lista | length lista == 0
suma lista | otherwise
= 0
= head lista + suma ( tail lista )
Implementemos las siguientes funciones
I
Implementar y dar el tipo de la productoria de una lista.
I
Implementar la función reverso :: [a] -> [a]
I
Mostrar los pasos de reducción para la evaluación de reverso ['a', 'b', 'c']
I
Implementar la función capicua :: [Integer] -> Bool que devuelve verdadero si el
reverso de una lista de números es la misma lista.
Recursión sobre Listas
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
I
Dada una lista cualquiera: [1,2,3,4,5]
I
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?
Ejercicios (parte 1)
1
Definir suma :: [Integer] -> [Integer] -> [Integer]
que dadas dos listas del mismo tamaño devuelva la suma elemento a elemento.
Por ejemplo: suma [1, 4, 6] [2, -1, 0]
[3, 3, 6].
2
Definir prodInterno :: [Float] -> [Float] -> Float que calcule el producto interno
entre dos listas del mismo tamaño, pensadas como vectores en Rn . Por ejemplo:
prodInterno [1, -2, 3, 4] [1, 0, 3, 2]
1*1 + -2*0 + 3*3 + 4*2
18.
3
Implementar noTieneDivisoresHasta :: Integer -> Integer -> Bool
noTieneDivisoresHasta m n devuelve True sii ningún número entre 2 y m divide a n.
4
Programar esPrimo :: Integer -> Bool, esta vez usando noTieneDivisoresHasta.
Intervalo
Ejercicios sobre Listas
Implementar las siguientes funciones
I
pertenece :: Integer -> [Integer] -> Bool
que indica si un elemento aparece en la lista.
I
hayRepetidos :: [Integer] -> Bool
que indica si una lista tiene elementos repetidos.
I
menoresQue :: Integer -> [Integer] -> [Integer]
que devuelve la lista pero sólo con los elementos que son menores que el primer parámetro
(es decir, filtra aquellos que son mayores o iguales que el número dado).
Y dos más
I
quitarPrimeraAparicionDe :: Integer -> [Integer] -> [Integer]
que elimina la primera aparición del elemento en la lista (de haberla).
I
maximo :: [Integer] -> Integer
que, dada una lista no vacı́a, calcula el mayor elemento de la misma.
Cambio de base en Haskell
I
Problema: Dados a, b ∈ Z+ con b > 1, escribir el número a en base b.
I
Por ejemplo ...
1
2
3
Si a = 10 y b = 2, entonces la respuesta es 1010.
Si a = 17 y b = 3, entonces la respuesta es 122.
Si a = 145 y b = 10, entonces la respuesta es 145.
Implementar en Haskell
I
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].
I
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
como Integer.
deBase 3 [1,2,2]
17.
La conjetura del 196
Los números de Lychrel
(http://gaussianos.com/la-conjetura-del-196/)
I
Tomemos un número natural cualquiera, por ejemplo el 180
I
Si damos vuelta sus cifras (081) y lo sumamos al original nos queda 180 + 081 = 261
I
Ahora repetimos el proceso con el 261: 261 + 162 = 423
I
Lo mismo con el 423: 423 + 324 = 747. ¡Un número capicúa!
I
¿Será verdad que para cualquier natural, siempre se llega a un número capicúa?
Los llamados números de Lychrel son los números naturales en base 10 que nunca llegan
a dar un número capicúa como resultado del proceso.
Implementar
I
La función capicuaEmpezandoCon :: [Integer] -> [Integer]
que toma un número (es decir sus dı́gitos en base 10)
y realiza el proceso antedicho hasta que se consiga su número capicúa asociado.
Ejemplo: capicuaEmpezandoCon [1,8,0]
I
[7,4,7].
Preparar el “ctrl+c” y ejecutar capicuaPara [1,9,6], ¿qué ocurre?
Ejercicios (parte 2)
1
Escribir una función que cambie un número de una base a otra. Debe tomar dos enteros b1
y b2, y un entero n representado en base b1 (expresado como una lista de enteros) y
retornar n en base b2 (también expresado como una lista de enteros).
2
Escribir una función que determine si una lista de números enteros está ordenada de manera
no-decreciente.
3
Escribir una función que elimine los números repetidos, de haberlos, dejando sólo la última
aparición de cada uno.
4
Idem anterior, pero dejando la primera aparición de cada uno.
5
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
devolver [1, 2, 3, 3, 3, 7, 9].
6
(difı́cil) Escribir una función que dada una lista, la retorne ordenada de menor a mayor.
Descargar