I1M2013: 3º examen de programación con Haskell
En la clase de hoy del curso Informática (de 1º de Grado en Matemáticas) se ha realizado el 3º examen del curso en dos turnos.
Los ejercicios y soluciones del primer turno se muestran a continuación
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
-- --------------------------------------------------------------------- -- § Librerías auxiliares -- -- --------------------------------------------------------------------- import Data.List -- --------------------------------------------------------------------- -- Ejercicio 1.1. Definir la función -- divisoresConUno :: Integer -> Bool -- tal que (divisoresConUno n) se verifica si todos sus divisores -- contienen el dígito 1. Por ejemplo, -- divisoresConUno 671 == True -- divisoresConUno 100 == False -- ya que los divisores de 671 son 1, 11, 61 y 671 y todos contienen el -- número 1; en cambio, 25 es un divisor de 100 que no contiene el -- dígito 1. -- --------------------------------------------------------------------- divisoresConUno :: Integer -> Bool divisoresConUno n = all contieneUno (divisores n) -- 2ª definición (sin all) divisoresConUno2 :: Integer -> Bool divisoresConUno2 n = and [contieneUno x | x <- divisores n] -- 3ª definición (por recursión) divisoresConUno3 :: Integer -> Bool divisoresConUno3 n = aux (divisores n) where aux [] = True aux (x:xs) = contieneUno x && aux xs -- 4ª definición (por plegado) divisoresConUno4 :: Integer -> Bool divisoresConUno4 n = foldr f True (divisores n) where f x y = contieneUno x && y -- 5ª definición (por plegado y lambda) divisoresConUno5 :: Integer -> Bool divisoresConUno5 n = foldr (\x y -> contieneUno x && y) True (divisores n) -- (divisores n) es la lista de los divisores de n. Por ejemplo, -- divisores 12 == [1,2,3,4,6,12] divisores :: Integer -> [Integer] divisores n = [x | x <- [1..n], rem n x == 0] -- (contienUno n) se verifica si n contiene el dígito 1. Por ejemplo, -- contieneUno 214 == True -- contieneUno 234 == False contieneUno :: Integer -> Bool contieneUno n = elem '1' (show n) -- 2ª definición (por recursión sin show) contieneUno2 :: Integer -> Bool contieneUno2 1 = True contieneUno2 n | n < 10 = False | n `rem` 10 == 1 = True | otherwise = contieneUno2 (n `div` 10) -- --------------------------------------------------------------------- -- Ejercicio 1.2. ¿Cuál será el próximo año en el que todos sus divisores -- contienen el dígito 1? ¿y el anterior? -- --------------------------------------------------------------------- -- El cálculo es -- ghci> head [n | n <- [2014..], divisoresConUno n] -- 2017 -- ghci> head [n | n <- [2014,2013..], divisoresConUno n] -- 2011 -- --------------------------------------------------------------------- -- Ejercicio 2.1. Un elemento de una lista es permanente si ninguno de -- los siguientes es mayor que él. -- -- Definir, por recursión, la función -- permanentesR :: [Int] -> [Int] -- tal que (permanentesR xs) es la lista de los elementos permanentes de -- xs. Por ejemplo, -- permanentesR [80,1,7,8,4] == [80,8,4] -- --------------------------------------------------------------------- -- 1ª definición: permanentesR :: [Int] -> [Int] permanentesR [] = [] permanentesR (x:xs) | x == maximum (x:xs) = x:permanentesR xs | otherwise = permanentesR xs -- 2ª definición (sin usar maximum): permanentesR2 :: [Int] -> [Int] permanentesR2 [] = [] permanentesR2 (x:xs) | and [x>=y|y<-xs] = x:permanentesR2 xs | otherwise = permanentesR2 xs -- Nota: Comparación de eficiencia -- ghci> let xs = [1..1000] in last (permanentesR (xs ++ reverse xs)) -- 1 -- (0.22 secs, 41105812 bytes) -- ghci> let xs = [1..1000] in last (permanentesR2 (xs ++ reverse xs)) -- 1 -- (0.96 secs, 31421308 bytes) -- --------------------------------------------------------------------- -- Ejercicio 2.2. Definir, por plegado, la función -- permanentesP :: [Int] -> [Int] -- tal que (permanentesP xs) es la lista de los elementos permanentes de -- xs. Por ejemplo, -- permanentesP [80,1,7,8,4] == [80,8,4] -- --------------------------------------------------------------------- -- 1ª definición: permanentesP :: [Int] -> [Int] permanentesP = foldr f [] where f x ys | x == maximum (x:ys) = x:ys | otherwise = ys -- 2ª definición: permanentesP2 :: [Int] -> [Int] permanentesP2 xs = foldl f [] (reverse xs) where f ac x | x == maximum (x:ac) = x:ac | otherwise = ac -- Nota: Comparación de eficiencia -- ghci> let xs = [1..1000] in last (permanentesP (xs ++ reverse xs)) -- 1 -- (0.22 secs, 52622056 bytes) -- ghci> let xs = [1..1000] in last (permanentesP2 (xs ++ reverse xs)) -- 1 -- (0.23 secs, 52918324 bytes) -- --------------------------------------------------------------------- -- Ejercicio 3. Definir la función -- especial :: Int -> [[Int]] -> Bool -- tal que (especial k xss) se verifica si cada uno de los diez dígitos -- 0, 1, 2,..., 9 aparece k veces entre todas las listas de xss. Por -- ejemplo, -- especial 1 [[12,40],[5,79,86,3]] == True -- especial 2 [[107,32,89],[58,76,94],[63,120,45]] == True -- especial 3 [[1329,276,996],[534,867,1200],[738,1458,405]] == True -- --------------------------------------------------------------------- -- 1ª definición (por comprensión): especial :: Int -> [[Int]] -> Bool especial k xss = sort (concat [show n | xs <- xss, n <- xs]) == concat [replicate k d | d <- ['0'..'9']] -- 2ª definición (con map) especial2 :: Int -> [[Int]] -> Bool especial2 k xss = sort (concat (concat (map (map cifras) xss))) == concat [replicate k d | d <- [0..9]] cifras:: Int -> [Int] cifras n = [read [x] | x <-show n] -- --------------------------------------------------------------------- -- Ejercicio 4. Definir la función -- primosConsecutivosConIgualFinal :: Int -> [Integer] -- tal que (primosConsecutivosConIgualFinal n) es la lista de los -- primeros n primos consecutivos que terminan en el mismo dígito. Por -- ejemplo, -- primosConsecutivosConIgualFinal 2 == [139, 149] -- primosConsecutivosConIgualFinal 3 == [1627, 1637, 1657] -- --------------------------------------------------------------------- primosConsecutivosConIgualFinal :: Int -> [Integer] primosConsecutivosConIgualFinal n = consecutivosConPropiedad p n primos where p [] = True p (x:xs) = and [r == rem y 10 | y <- xs] where r = rem x 10 -- (consecutivosConPropiedad p n xs) es la lista con los n primeros -- elementos consecutivos de zs que verifican la propiedad p. Por -- ejemplo, -- ghci> consecutivosConPropiedad (\xs -> sum xs > 20) 2 [5,2,1,17,4,25] -- [17,4] consecutivosConPropiedad :: ([a] -> Bool) -> Int -> [a] -> [a] consecutivosConPropiedad p n zs = head [xs | xs <- [take n ys | ys <- tails zs], p xs] -- primos es la lista de los números primos. Por ejemplo, -- ghci> take 20 primos -- [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71] primos :: [Integer] primos = [n | n <- 2:[3,5..], primo n] -- (primo n) se verifica si n es un número primo. Por ejemplo, -- primo 7 == True -- primo 8 == False primo :: Integer -> Bool primo n = [x | x <- [1..n], rem n x == 0] == [1,n] |
Los ejercicios y soluciones del segundo turno se muestran a continuación
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
-- --------------------------------------------------------------------- -- Ejercicio 1. El factorial de 7 es -- 7! = 1 * 2 * 3 * 4 * 5 * 6 * 7 = 5040 -- por tanto, el último dígito no nulo del factorial de 7 es 4. -- -- Definir la función -- ultimoNoNuloFactorial :: Integer -> Integer -- tal que (ultimoNoNuloFactorial n) es el último dígito no nulo del -- factorial de n. Por ejemplo, -- ultimoNoNuloFactorial 7 == 4 -- --------------------------------------------------------------------- ultimoNoNuloFactorial :: Integer -> Integer ultimoNoNuloFactorial n = ultimoNoNulo (factorial n) -- (ultimoNoNulo n) es el último dígito no nulo de n. Por ejemplo, -- ultimoNoNulo 5040 == 4 ultimoNoNulo :: Integer -> Integer ultimoNoNulo n | m /= 0 = m | otherwise = ultimoNoNulo (n `div` 10) where m = n `rem` 10 -- 2ª definición (por comprensión) ultimoNoNulo2 :: Integer -> Integer ultimoNoNulo2 n = read [head (dropWhile (=='0') (reverse (show n)))] -- (factorial n) es el factorial de n. Por ejemplo, -- factorial 7 == 5040 factorial :: Integer -> Integer factorial n = product [1..n] -- --------------------------------------------------------------------- -- Ejercicio 2.1. Una lista se puede comprimir indicando el número de -- veces consecutivas que aparece cada elemento. Por ejemplo, la lista -- comprimida de [1,1,7,7,7,5,5,7,7,7,7] es [(2,1),(3,7),(2,5),(4,7)], -- indicando que comienza con dos 1, seguido de tres 7, dos 5 y cuatro -- 7. -- -- Definir, por comprensión, la función -- expandidaC :: [(Int,a)] -> [a] -- tal que (expandidaC ps) es la lista expandida correspondiente a ps -- (es decir, es la lista xs tal que la comprimida de xs es ps). Por -- ejemplo, -- expandidaC [(2,1),(3,7),(2,5),(4,7)] == [1,1,7,7,7,5,5,7,7,7,7] -- --------------------------------------------------------------------- expandidaC :: [(Int,a)] -> [a] expandidaC ps = concat [replicate k x | (k,x) <- ps] -- --------------------------------------------------------------------- -- Ejercicio 2.2. Definir, por recursión, la función -- expandidaR :: [(Int,a)] -> [a] -- tal que (expandidaR ps) es la lista expandida correspondiente a ps -- (es decir, es la lista xs tal que la comprimida de xs es ps). Por -- ejemplo, -- expandidaR [(2,1),(3,7),(2,5),(4,7)] == [1,1,7,7,7,5,5,7,7,7,7] -- --------------------------------------------------------------------- expandidaR :: [(Int,a)] -> [a] expandidaR [] = [] expandidaR ((n,x):ps) = replicate n x ++ expandidaR ps -- --------------------------------------------------------------------- -- Ejercicio 3.1. Un número n es de Angelini si n y 2n tienen algún -- dígito común. Por ejemplo, 2014 es un número de Angelini ya que 2014 -- y su doble (4028) comparten los dígitos 4 y 0. -- -- Definir la función -- angelini :: Integer -> Bool -- tal que (angelini n) se verifica si n es un número de Angelini. Por -- ejemplo, -- angelini 2014 == True -- angelini 2067 == False -- --------------------------------------------------------------------- -- 1ª definición (con any) angelini :: Integer -> Bool angelini n = any (`elem` (show (2*n))) (show n) -- 2ª definición (por comprensión) angelini2 :: Integer -> Bool angelini2 n = not (null [x | x <- show n, x `elem` show (2*n)]) -- 3ª definición (por recursión) angelini3 :: Integer -> Bool angelini3 n = aux (show n) (show (2*n)) where aux [] _ = False aux (x:xs) ys = x `elem` ys || aux xs ys -- 4ª definición (por plegado) angelini4 :: Integer -> Bool angelini4 n = aux (show n) where aux = foldr f False f x y = x `elem` show (2*n) || y -- --------------------------------------------------------------------- -- Ejercicio 3.2. ¿Cuál es el primer año que no será de Angelini? -- --------------------------------------------------------------------- -- El cálculo es -- ghci> head [n | n <- [2014..], not (angelini n)] -- 2057 -- --------------------------------------------------------------------- -- Ejercicio 4.1. El número 37 es primo y se puede escribir como suma de -- primos menores distintos (en efecto, los números 3, 11 y 23 son -- primos y su suma es 37. -- -- Definir la función -- primoSumaDePrimos :: Integer -> Bool -- tal que (primoSumaDePrimos n) se verifica si n es primo y se puede -- escribir como suma de primos menores que n. Por ejemplo, -- primoSumaDePrimos 37 == True -- primoSumaDePrimos 39 == False -- primoSumaDePrimos 11 == False -- --------------------------------------------------------------------- primoSumaDePrimos :: Integer -> Bool primoSumaDePrimos n = primo n && esSumaDePrimos n -- (esSumaDePrimos n) se verifica si n es una suma de primos menores que -- n. Por ejemplo, -- esSumaDePrimos 37 == True -- esSumaDePrimos 11 == False esSumaDePrimos :: Integer -> Bool esSumaDePrimos n = esSuma n [x | x <- 2:[3,5..n-1], primo x] -- (primo n) se verifica si n es primo. Por ejemplo, -- primo 37 == True -- primo 38 == False primo :: Integer -> Bool primo n = [x | x <- [1..n], n `rem` x == 0] == [1,n] -- (esSuma n xs) s verifica si n es suma de elementos de xs. Por ejemplo, -- esSuma 20 [4,2,7,9] == True -- esSuma 21 [4,2,7,9] == False esSuma :: Integer -> [Integer] -> Bool esSuma 0 _ = True esSuma n [] = False esSuma n (x:xs) | n == x = True | n > x = esSuma n xs || esSuma (n-x) xs | otherwise = esSuma n xs -- --------------------------------------------------------------------- -- Ejercicio 4.2. ¿Cuál será el próximo año primo suma de primos? ¿y el -- anterior? -- --------------------------------------------------------------------- -- El cálculo es -- ghci> head [p | p <- [2014..], primoSumaDePrimos p] -- 2017 -- ghci> head [p | p <- [2014,2013..], primoSumaDePrimos p] -- 2011 |