La semana en Exercitium (del 28 de marzo al 1 de abril)
Esta semana he publicado en Exercitium las soluciones de los siguientes problemas:
- 1. Emparejamiento binario
- 2. Ampliación de matrices por columnas
- 3. Regiones determinadas por n rectas del plano
- 4. Elemento más repetido de manera consecutiva
- 5. Número de pares de elementos adyacentes iguales en una matriz
A continuación se muestran las soluciones.
1. Emparejamiento binario
Definir la función
1 |
zipBinario :: [a -> b -> c] -> [a] -> [b] -> [c] |
tal que (zipBinario fs xs ys) es la lista obtenida aplicando cada una de las operaciones binarias de fs a los correspondientes elementos de xs e ys. Por ejemplo,
1 2 3 |
zipBinario [(+), (*), (*)] [2,2,2] [4,4,4] == [6,8,8] zipBinario [(+)] [2,2,2] [4,4,4] == [6] zipBinario [(<), (==), (==)] "coloca" "lobo" == [True,True,False] |
Soluciones
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 |
import Test.QuickCheck import Test.QuickCheck.HigherOrder import Test.Hspec -- 1ª solución zipBinario1 :: [a -> b -> c] -> [a] -> [b] -> [c] zipBinario1 (f:fs) (x:xs) (y:ys) = f x y : zipBinario1 fs xs ys zipBinario1 _ _ _ = [] -- 2ª solución zipBinario2 :: [a -> b -> c] -> [a] -> [b] -> [c] zipBinario2 fs xs ys = [f x y | (f,(x,y)) <- zip fs (zip xs ys)] -- 3ª solución zipBinario3 :: [a -> b -> c] -> [a] -> [b] -> [c] zipBinario3 fs xs ys = [f x y | (f,x,y) <- zip3 fs xs ys] -- 4ª solución zipBinario4 :: [a -> b -> c] -> [a] -> [b] -> [c] zipBinario4 = zipWith3 id -- Verificación -- ============ especificacion :: ([Int -> Int -> Int] -> [Int] -> [Int] -> [Int]) -> Spec especificacion zipBinario = do it "e1" $ zipBinario [(+), (*), (*)] [2,2,2] [4,4,4] `shouldBe` [6,8,8] it "e2" $ zipBinario [(+)] [2,2,2] [4,4,4] `shouldBe` [6] verifica :: IO () verifica = hspec $ do describe "zipBinario1" $ especificacion zipBinario1 describe "zipBinario2" $ especificacion zipBinario2 describe "zipBinario3" $ especificacion zipBinario3 describe "zipBinario4" $ especificacion zipBinario4 -- La verificación es -- λ> verifica -- -- zipBinario1 -- e1 -- e2 -- zipBinario2 -- e1 -- e2 -- zipBinario3 -- e1 -- e2 -- zipBinario4 -- e1 -- e2 -- -- Finished in 0.0016 seconds -- 8 examples, 0 failures -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_zipBinario :: [Bool -> Bool -> Bool] -> [Bool] -> [Bool] -> Bool prop_zipBinario fs xs ys = all (== zipBinario1 fs xs ys) [g fs xs ys | g <- [zipBinario2, zipBinario3, zipBinario4]] -- La comprobación es -- λ> quickCheck' prop_zipBinario -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> maximum (zipBinario1 (cycle [(+), (*)]) [1..] [1..2*10^6]) -- 4000000000000 -- (2.13 secs, 965,392,072 bytes) -- λ> maximum (zipBinario2 (cycle [(+), (*)]) [1..] [1..2*10^6]) -- 4000000000000 -- (1.86 secs, 1,109,392,176 bytes) -- λ> maximum (zipBinario3 (cycle [(+), (*)]) [1..] [1..2*10^6]) -- 4000000000000 -- (1.93 secs, 981,392,128 bytes) -- λ> maximum (zipBinario4 (cycle [(+), (*)]) [1..] [1..2*10^6]) -- 4000000000000 -- (1.07 secs, 773,392,040 bytes) |
El código se encuentra en GitHub.
La elaboración de las soluciones se describe en el siguiente vídeo
2. Ampliación de matrices por columnas
Las matrices enteras se pueden representar mediante tablas con índices enteros:
1 |
type Matriz = Array (Int,Int) Int |
Definir la función
1 |
ampliaColumnas :: Matriz -> Matriz -> Matriz |
tal que (ampliaColumnas p q) es la matriz construida añadiendo las columnas de la matriz q a continuación de las de p (se supone que tienen el mismo número de filas). Por ejemplo, si p y q representa las dos primeras matrices, entonces (ampliaColumnas p q) es la tercera
1 2 |
|0 1| |4 5 6| |0 1 4 5 6| |2 3| |7 8 9| |2 3 7 8 9| |
En Haskell, se definen las dos primeras matrices se definen por
1 2 |
ej1 = listArray ((1,1),(2,2)) [0..3] ej2 = listArray ((1,1),(2,3)) [4..9] |
y el cálculo de la tercera es
1 2 3 4 5 |
λ> ampliaColumnas ej1 ej2 array ((1,1),(2,5)) [((1,1),0),((1,2),1),((1,3),4),((1,4),5),((1,5),6), ((2,1),2),((2,2),3),((2,3),7),((2,4),8),((2,5),9)] λ> elems (ampliaColumnas ej1 ej2) [0,1,4,5,6,2,3,7,8,9] |
Soluciones
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 |
import Data.Array (Array, (!), array, bounds, elems, listArray) import Data.Matrix (Matrix, (<|>), fromList, ncols, nrows, toList) import Test.QuickCheck type Matriz = Array (Int,Int) Int ej1, ej2 :: Matriz ej1 = listArray ((1,1),(2,2)) [0..3] ej2 = listArray ((1,1),(2,3)) [4..9] -- 1ª solución -- =========== ampliaColumnas1 :: Matriz -> Matriz -> Matriz ampliaColumnas1 p1 p2 = array ((1,1),(m,n1+n2)) [((i,j), f i j) | i <- [1..m], j <- [1..n1+n2]] where ((_,_),(m,n1)) = bounds p1 ((_,_),(_,n2)) = bounds p2 f i j | j <= n1 = p1!(i,j) | otherwise = p2!(i,j-n1) -- 2ª solución -- =========== ampliaColumnas2 :: Matriz -> Matriz -> Matriz ampliaColumnas2 p1 p2 = matriz (matrix p1 <|> matrix p2) -- (matrix p) es la matriz p en el formatao de Data.Matrix. Por ejemplo, -- λ> ej1 -- array ((1,1),(2,2)) [((1,1),0),((1,2),1),((2,1),2),((2,2),3)] -- λ> matrix ej1 -- ┌ ┐ -- │ 0 1 │ -- │ 2 3 │ -- └ ┘ -- λ> matrix (ampliaColumnas1 ej1 ej2) -- ┌ ┐ -- │ 0 1 4 5 6 │ -- │ 2 3 7 8 9 │ -- └ ┘ matrix :: Matriz -> Matrix Int matrix p = fromList m n (elems p) where (_,(m,n)) = bounds p -- (matriz p) es la matriz p en el formato de Data.Array. Por ejemplo, -- λ> matriz (fromList 2 3 [1..]) -- array ((1,1),(2,3)) [((1,1),1),((1,2),2),((1,3),3),((2,1),4),((2,2),5),((2,3),6)] matriz :: Matrix Int -> Matriz matriz p = listArray ((1,1),(nrows p,ncols p)) (toList p) -- Comprobación de equivalencia -- ============================ data ParMatrices = P Matriz Matriz deriving Show -- parMatricesArbitrario es un generador de pares de matrices con el -- mismo número de filas. parMatricesArbitrario :: Gen ParMatrices parMatricesArbitrario = do m <- arbitrary `suchThat` (> 0) n1 <- arbitrary `suchThat` (> 0) n2 <- arbitrary `suchThat` (> 0) xs <- vector (m * n1) ys <- vector (m * n2) return (P (listArray ((1,1),(m,n1)) xs) (listArray ((1,1),(m,n2)) ys)) -- ParMatrices es una subclase de Arbitrary instance Arbitrary ParMatrices where arbitrary = parMatricesArbitrario -- La propiedad es prop_ampliaColumna :: ParMatrices -> Bool prop_ampliaColumna (P p q) = ampliaColumnas1 p q == ampliaColumnas2 p q -- La comprobación es -- λ> quickCheck prop_ampliaColumna -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> let p = listArray ((1,1),(10^3,10^3)) [1..] in maximum (ampliaColumnas1 p p) -- 1000000 -- (2.04 secs, 1,562,652,704 bytes) -- λ> let p = listArray ((1,1),(10^3,10^3)) [1..] in maximum (ampliaColumnas2 p p) -- 1000000 -- (0.69 secs, 738,508,624 bytes) |
El código se encuentra en GitHub.
La elaboración de las soluciones se describe en el siguiente vídeo
3. Regiones determinadas por n rectas del plano
En los siguientes dibujos se observa que el número máximo de regiones en el plano generadas con 1, 2 ó 3 líneas son 2, 4 ó 7, respectivamente.
1 2 3 4 5 6 7 8 9 10 11 |
\ | \5| \| \ |\ | \ | | \ 1 1 | 3 1 | 3 \ 6 ------ ---|--- ---|----\--- 2 2 | 4 2 | 4 \ 7 | | \ |
Definir la función
1 |
regiones :: Integer -> Integer |
tal que (regiones n)
es el número máximo de regiones en el plano generadas con n
líneas. Por ejemplo,
1 2 3 4 5 6 7 8 9 10 |
regiones 1 == 2 regiones 2 == 4 regiones 3 == 7 regiones 100 == 5051 regiones 1000 == 500501 regiones 10000 == 50005001 length (show (regiones (10^(10^5)))) == 200000 length (show (regiones (10^(10^6)))) == 2000000 length (show (regiones (10^(10^6)))) == 2000000 length (show (regiones (10^(10^7)))) == 20000000 |
Soluciones
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 |
import Data.List (genericIndex) import Test.QuickCheck -- 1ª solución -- =========== regiones1 :: Integer -> Integer regiones1 0 = 1 regiones1 n = regiones1 (n-1) + n -- 2ª solución -- =========== regiones2 :: Integer -> Integer regiones2 n = 1 + sum [0..n] -- 3ª solución -- =========== regiones3 :: Integer -> Integer regiones3 n = 1 + sumas `genericIndex` n -- (sumas n) es la suma 0 + 1 + 2 +...+ n. Por ejemplo, -- take 10 sumas == [0,1,3,6,10,15,21,28,36,45] sumas :: [Integer] sumas = scanl1 (+) [0..] -- 4ª solución -- =========== regiones4 :: Integer -> Integer regiones4 n = 1 + n*(n+1) `div` 2 -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_regiones :: Positive Integer -> Bool prop_regiones (Positive n) = all (== regiones1 n) [regiones2 n, regiones3 n, regiones4 n] -- La comprobación es -- λ> quickCheck prop_regiones -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> regiones1 (4*10^6) -- 8000002000001 -- (2.20 secs, 938,105,888 bytes) -- λ> regiones2 (4*10^6) -- 8000002000001 -- (0.77 secs, 645,391,624 bytes) -- λ> regiones3 (4*10^6) -- 8000002000001 -- (1.22 secs, 1,381,375,296 bytes) -- λ> regiones4 (4*10^6) -- 8000002000001 -- (0.01 secs, 484,552 bytes) |
El código se encuentra en GitHub.
La elaboración de las soluciones se describe en el siguiente vídeo
4. Elemento más repetido de manera consecutiva
Definir la función
1 |
masRepetido :: Ord a => [a] -> (a,Int) |
tal que (masRepetido xs)
es el elemento de xs
que aparece más veces de manera consecutiva en la lista junto con el número de sus apariciones consecutivas; en caso de empate, se devuelve el mayor de dichos elementos. Por ejemplo,
1 2 3 4 5 |
masRepetido [1,1,4,4,1] == (4,2) masRepetido [4,4,1,1,5] == (4,2) masRepetido "aadda" == ('d',2) masRepetido "ddaab" == ('d',2) masRepetido (show (product [1..5*10^4])) == ('0',12499) |
Soluciones
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 |
import Data.List (group) import Data.Tuple (swap) import Control.Arrow ((&&&)) import Test.QuickCheck -- 1ª solución -- =========== masRepetido1 :: Ord a => [a] -> (a,Int) masRepetido1 [x] = (x,1) masRepetido1 (x:y:zs) | m > n = (x,m) | m == n = (max x u,m) | otherwise = (u,n) where (u,n) = masRepetido1 (y:zs) m = length (takeWhile (==x) (x:y:zs)) -- 2ª solución -- =========== masRepetido2 :: Ord a => [a] -> (a,Int) masRepetido2 (x:xs) | null xs' = (x,length (x:xs)) | m > n = (x,m) | m == n = (max x u,m) | otherwise = (u,n) where xs' = dropWhile (== x) xs m = length (takeWhile (==x) (x:xs)) (u,n) = masRepetido2 xs' -- 3ª solución -- =========== masRepetido3 :: Ord a => [a] -> (a,Int) masRepetido3 xs = (n,z) where (z,n) = maximum [(1 + length ys,y) | (y:ys) <- group xs] -- 4ª solución -- ============ masRepetido4 :: Ord a => [a] -> (a,Int) masRepetido4 xs = swap (maximum [(1 + length ys,y) | (y:ys) <- group xs]) -- 5ª solución -- ============ masRepetido5 :: Ord a => [a] -> (a,Int) masRepetido5 xs = swap (maximum (map (\ys -> (length ys, head ys)) (group xs))) -- 6ª solución -- ============ masRepetido6 :: Ord a => [a] -> (a,Int) masRepetido6 = swap . maximum . map ((,) <$> length <*> head) . group -- 7ª solución -- ============ masRepetido7 :: Ord a => [a] -> (a,Int) masRepetido7 = swap . maximum . map (length &&& head) . group -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_masRepetido :: NonEmptyList Int -> Bool prop_masRepetido (NonEmpty xs) = all (== masRepetido1 xs) [masRepetido2 xs, masRepetido3 xs, masRepetido4 xs, masRepetido5 xs, masRepetido6 xs, masRepetido7 xs] -- La comprobación es -- λ> quickCheck prop_masRepetido -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> masRepetido1 (show (product [1..3*10^4])) -- ('0',7498) -- (3.72 secs, 2,589,930,952 bytes) -- λ> masRepetido2 (show (product [1..3*10^4])) -- ('0',7498) -- (1.27 secs, 991,406,232 bytes) -- λ> masRepetido3 (show (product [1..3*10^4])) -- ('0',7498) -- (0.85 secs, 945,399,976 bytes) -- λ> masRepetido4 (show (product [1..3*10^4])) -- ('0',7498) -- (0.86 secs, 945,399,888 bytes) -- λ> masRepetido5 (show (product [1..3*10^4])) -- ('0',7498) -- (0.80 secs, 943,760,760 bytes) -- λ> masRepetido6 (show (product [1..3*10^4])) -- ('0',7498) -- (0.78 secs, 945,400,400 bytes) -- λ> masRepetido7 (show (product [1..3*10^4])) -- ('0',7498) -- (0.78 secs, 942,122,088 bytes) -- -- λ> masRepetido2 (show (product [1..5*10^4])) -- ('0',12499) -- (3.27 secs, 2,798,156,008 bytes) -- λ> masRepetido3 (show (product [1..5*10^4])) -- ('0',12499) -- (2.20 secs, 2,716,952,408 bytes) -- λ> masRepetido4 (show (product [1..5*10^4])) -- ('0',12499) -- (2.22 secs, 2,716,952,320 bytes) -- λ> masRepetido5 (show (product [1..5*10^4])) -- ('0',12499) -- (2.18 secs, 2,714,062,328 bytes) -- λ> masRepetido6 (show (product [1..5*10^4])) -- ('0',12499) -- (2.17 secs, 2,716,952,832 bytes) -- λ> masRepetido7 (show (product [1..5*10^4])) -- ('0',12499) -- (2.17 secs, 2,711,172,792 bytes) |
El código se encuentra en GitHub.
La elaboración de las soluciones se describe en el siguiente vídeo
5. Número de pares de elementos adyacentes iguales en una matriz
Una matriz se puede representar mediante una lista de listas. Por ejemplo, la matriz
1 2 |
|2 1 5| |4 3 7| |
se puede representar mediante la lista
1 |
[[2,1,5],[4,3,7]] |
Definir la función
1 |
numeroParesAdyacentesIguales :: Eq a => [[a]] -> Int |
tal que (numeroParesAdyacentesIguales xss)
es el número de pares de elementos consecutivos (en la misma fila o columna) iguales de la matriz xss
. Por ejemplo,
1 2 3 4 5 6 7 |
numeroParesAdyacentesIguales [[0,1],[0,2]] == 1 numeroParesAdyacentesIguales [[0,0],[1,2]] == 1 numeroParesAdyacentesIguales [[0,1],[0,0]] == 2 numeroParesAdyacentesIguales [[1,2],[1,4],[4,4]] == 3 numeroParesAdyacentesIguales ["ab","aa"] == 2 numeroParesAdyacentesIguales [[0,0,0],[0,0,0],[0,0,0]] == 12 numeroParesAdyacentesIguales [[0,0,0],[0,1,0],[0,0,0]] == 8 |
Soluciones
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 |
import Data.List (group,transpose) import Data.Array ((!), listArray) import Test.QuickCheck -- 1ª solución -- =========== numeroParesAdyacentesIguales1 :: Eq a => [[a]] -> Int numeroParesAdyacentesIguales1 xss = length [(i,j) | i <- [1..m-1], j <- [1..n], p!(i,j) == p!(i+1,j)] + length [(i,j) | i <- [1..m], j <- [1..n-1], p!(i,j) == p!(i,j+1)] where m = length xss n = length (head xss) p = listArray ((1,1),(m,n)) (concat xss) -- 2ª solución -- =========== numeroParesAdyacentesIguales2 :: Eq a => [[a]] -> Int numeroParesAdyacentesIguales2 xss = numeroParesAdyacentesIgualesFilas xss + numeroParesAdyacentesIgualesFilas (transpose xss) -- (numeroParesAdyacentesIgualesFilas xss) es el número de pares de -- elementos consecutivos (en la misma fila) iguales de la matriz -- xss. Por ejemplo, -- λ> numeroParesAdyacentesIgualesFilas [[0,0,1,0],[0,1,1,0],[0,1,0,1]] -- 2 -- λ> numeroParesAdyacentesIgualesFilas ["0010","0110","0101"] -- 2 numeroParesAdyacentesIgualesFilas :: Eq a => [[a]] -> Int numeroParesAdyacentesIgualesFilas xss = sum [numeroParesAdyacentesIgualesFila xs | xs <- xss] -- La función anterior se puede definir con map numeroParesAdyacentesIgualesFilas2 :: Eq a => [[a]] -> Int numeroParesAdyacentesIgualesFilas2 xss = sum (map numeroParesAdyacentesIgualesFila xss) -- y también se puede definir sin argumentos: numeroParesAdyacentesIgualesFilas3 :: Eq a => [[a]] -> Int numeroParesAdyacentesIgualesFilas3 = sum . map numeroParesAdyacentesIgualesFila -- (numeroParesAdyacentesIgualesFila xs) es el número de pares de -- elementos consecutivos de la lista xs. Por ejemplo, -- numeroParesAdyacentesIgualesFila [5,5,5,2,5] == 2 numeroParesAdyacentesIgualesFila :: Eq a => [a] -> Int numeroParesAdyacentesIgualesFila xs = length [(x,y) | (x,y) <- zip xs (tail xs), x == y] -- 3ª solución -- =========== numeroParesAdyacentesIguales3 :: Eq a => [[a]] -> Int numeroParesAdyacentesIguales3 xss = length (concatMap tail (concatMap group (xss ++ transpose xss))) -- 4ª solución -- =========== numeroParesAdyacentesIguales4 :: Eq a => [[a]] -> Int numeroParesAdyacentesIguales4 = length . (tail =<<) . (group =<<) . ((++) =<< transpose) -- Comprobación de equivalencia -- ============================ newtype Matriz = M [[Int]] deriving Show -- Generador de matrices arbitrarias. Por ejemplo, -- λ> generate matrizArbitraria -- M [[-3,0],[8,-6],[-13,-13],[10,8],[14,29]] -- λ> generate matrizArbitraria -- M [[11,9,4,-25,-29,30,-18],[13,8,-2,-22,29,-3,-13]] matrizArbitraria :: Gen Matriz matrizArbitraria = do m <- chooseInt (1,10) n <- chooseInt (1,10) xss <- vectorOf m (vectorOf n arbitrary) return (M xss) -- Matriz es una subclase de Arbitrary. instance Arbitrary Matriz where arbitrary = matrizArbitraria -- La propiedad es prop_numeroParesAdyacentesIguales :: Matriz -> Bool prop_numeroParesAdyacentesIguales (M xss) = all (== numeroParesAdyacentesIguales1 xss) [numeroParesAdyacentesIguales2 xss, numeroParesAdyacentesIguales3 xss, numeroParesAdyacentesIguales4 xss] -- La comprobación es -- λ> quickCheck prop_numeroParesAdyacentesIguales -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> numeroParesAdyacentesIguales1 (replicate (3*10^3) (replicate (10^3) 0)) -- 5996000 -- (5.51 secs, 4,751,249,472 bytes) -- λ> numeroParesAdyacentesIguales2 (replicate (3*10^3) (replicate (10^3) 0)) -- 5996000 -- (2.62 secs, 1,681,379,960 bytes) -- λ> numeroParesAdyacentesIguales3 (replicate (3*10^3) (replicate (10^3) 0)) -- 5996000 -- (0.48 secs, 1,393,672,616 bytes) -- λ> numeroParesAdyacentesIguales4 (replicate (3*10^3) (replicate (10^3) 0)) -- 5996000 -- (0.38 secs, 1,393,560,848 bytes) |
El código se encuentra en GitHub.
La elaboración de las soluciones se describe en el siguiente vídeo