PFH: La semana en Exercitium (11 de junio de 2022)
Esta semana he publicado en Exercitium las soluciones de los siguientes problemas:
- 1. Diccionario de frecuencias
- 2. Primos circulares
- 3. Codificación de Gödel
- 4. Representación matricial de relaciones binarias
- 5. Distancia esperada entre dos puntos de un cuadrado unitario
A continuación se muestran las soluciones.
1. Diccionario de frecuencias
Definir la función
1 |
frecuencias :: Ord a => [a] -> Map a Int |
tal que (frecuencias xs)
es el diccionario formado por los elementos de xs
junto con el número de veces que aparecen en xs
. Por ejemplo,
1 2 3 4 5 6 7 8 |
λ> frecuencias "sosos" fromList [('o',2),('s',3)] λ> frecuencias (show (10^100)) fromList [('0',100),('1',1)] λ> frecuencias (take (10^6) (cycle "abc")) fromList [('a',333334),('b',333333),('c',333333)] λ> size (frecuencias (take (10^6) (cycle [1..10^6]))) 1000000 |
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 |
import Data.List (foldl') import Data.Map (Map, empty, insertWith, fromList, fromListWith, size) import Test.QuickCheck -- 1ª solución -- =========== frecuencias1 :: Ord a => [a] -> Map a Int frecuencias1 [] = empty frecuencias1 (x:xs) = insertWith (+) x 1 (frecuencias1 xs) -- 2ª solución -- =========== frecuencias2 :: Ord a => [a] -> Map a Int frecuencias2 = foldl' (\d x-> insertWith (+) x 1 d) empty -- 3ª solución -- =========== frecuencias3 :: Ord a => [a] -> Map a Int frecuencias3 xs = fromListWith (+) (zip xs (repeat 1)) -- Equivalencia de las definiciones -- ================================ -- La propiedad es prop_frecuencias :: [Int] -> Bool prop_frecuencias xs = all (== frecuencias1 xs) [ frecuencias2 xs , frecuencias3 xs] -- La comprobación es -- λ> quickCheck prop_frecuencias -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> frecuencias1 (take (10^6) (cycle "abc")) -- fromList [('a',333334),('b',333333),('c',333333)] -- (0.89 secs, 453,842,448 bytes) -- λ> frecuencias2 (take (10^6) (cycle "abc")) -- fromList [('a',333334),('b',333333),('c',333333)] -- (0.54 secs, 274,181,128 bytes) -- λ> frecuencias3 (take (10^6) (cycle "abc")) -- fromList [('a',333334),('b',333333),('c',333333)] -- (0.29 secs, 313,787,976 bytes) -- λ> size (frecuencias1 (take (10^6) (cycle [1..10^6]))) -- 1000000 -- (3.76 secs, 2,651,926,024 bytes) -- λ> size (frecuencias2 (take (10^6) (cycle [1..10^6]))) -- 1000000 -- (1.03 secs, 1,640,678,448 bytes) -- λ> size (frecuencias3 (take (10^6) (cycle [1..10^6]))) -- 1000000 -- (0.88 secs, 1,672,678,536 bytes) |
El código se encuentra en GitHub.
2. Primos circulares
Un primo circular es un número tal que todas las rotaciones de dígitos producen números primos. Por ejemplo, 195 es un primo circular ya que las rotaciones de sus dígitos son 197, 971 y 719 y los tres números son primos.
Definir la lista
1 |
circulares :: [Integer] |
cuyo valor es la lista de los números primos circulares. Por ejemplo,
1 2 |
take 16 circulares == [2,3,5,7,11,13,17,31,37,71,73,79,97,113,131,197] circulares !! 50 == 933199 |
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 |
import Data.Numbers.Primes (isPrime, primes) import Test.QuickCheck (Positive (Positive), quickCheck) -- 1ª solución -- =========== circulares1 :: [Integer] circulares1 = filter esCircular1 primes -- (esCircular1 n) se verifica si n es un número circular. Por ejemplo, -- esCircular1 197 == True -- esCircular1 157 == False esCircular1 :: Integer -> Bool esCircular1 = all (isPrime . read) . rotaciones1 . show -- (rotaciones1 xs) es la lista de las rotaciones obtenidas desplazando -- el primer elemento xs al final. Por ejemplo, -- rotaciones1 [2,3,5] == [[2,3,5],[3,5,2],[5,2,3]] rotaciones1 :: [a] -> [[a]] rotaciones1 xs = reverse (aux (length xs) [xs]) where aux 1 yss = yss aux n (ys:yss) = aux (n-1) (rota ys : ys :yss) -- 2ª solución -- =========== circulares2 :: [Integer] circulares2 = filter esCircular2 primes esCircular2 :: Integer -> Bool esCircular2 = all (isPrime . read) . rotaciones2 . show rotaciones2 :: [a] -> [[a]] rotaciones2 xs = take (length xs) (iterate rota xs) -- (rota xs) es la lista añadiendo el primer elemento de xs al -- final. Por ejemplo, -- rota [3,2,5,7] == [2,5,7,3] rota :: [a] -> [a] rota (x:xs) = xs ++ [x] -- 3ª solución -- =========== circulares3 :: [Integer] circulares3 = filter (all isPrime . rotaciones3) primes rotaciones3 :: Integer -> [Integer] rotaciones3 n = [read (take m (drop i (cycle s))) | i <- [1..m]] where s = show n m = length s -- 4ª definición -- ============= -- Nota. La 4ª definición es una mejora observando que para que n sea un -- número primo circular es necesario que todos los dígitos de n sean -- impares, salvo para n = 2. circulares4 :: [Integer] circulares4 = 2 : filter esCircular4 primes -- (esCircular4 n) se verifica si n es un número circular. Por ejemplo, -- esCircular4 197 == True -- esCircular4 157 == False esCircular4 :: Integer -> Bool esCircular4 n = digitosImpares n && all (isPrime . read) (rotaciones2 (show n)) -- (digitosImpares n) se verifica si todos los dígitos de n son -- impares. Por ejemplo, -- digitosImpares 7351 == True -- digitosImpares 7341 == False digitosImpares :: Integer -> Bool digitosImpares = all (`elem` "135679") . show -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_circulares :: Positive Int -> Bool prop_circulares (Positive n) = all (== circulares1 !! n) [circulares2 !! n, circulares3 !! n, circulares4 !! n] -- La comprobación es -- λ> quickCheckWith (stdArgs {maxSize=50}) prop_circulares -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> circulares1 !! 46 -- 331999 -- (2.08 secs, 7,229,208,200 bytes) -- λ> circulares2 !! 46 -- 331999 -- (1.93 secs, 7,165,043,992 bytes) -- λ> circulares3 !! 46 -- 331999 -- (0.74 secs, 2,469,098,648 bytes) -- λ> circulares4 !! 46 -- 331999 -- (0.28 secs, 917,501,600 bytes) |
El código se encuentra en GitHub.
3. Codificación de Gödel
Dada una lista de números naturales xs, codificación de Gödel de xs se obtiene multiplicando las potencias de los primos sucesivos, siendo los exponentes los sucesores de los elementos de xs. Por ejemplo, si xs = [6,0,4], la codificación de xs es
1 |
2^7 * 3^1 * 5^5 = 1200000 |
Definir las funciones
1 2 |
codificaG :: [Integer] -> Integer decodificaG :: Integer -> [Integer] |
tales que
(codificaG xs)
es la codificación de Gödel dexs
. Por ejemplo,
1 2 3 4 |
codificaG [6,0,4] == 1200000 codificaG [3,1,1] == 3600 codificaG [3,1,0,0,0,0,0,1] == 4423058640 codificaG [1..6] == 126111168580452537982500 |
(decodificaG n)
es la lista xs cuya codificación esn
. Por ejemplo,
1 2 3 4 |
decodificaG 1200000 == [6,0,4] decodificaG 3600 == [3,1,1] decodificaG 4423058640 == [3,1,0,0,0,0,0,1] decodificaG 126111168580452537982500 == [1,2,3,4,5,6] |
Comprobar con QuickCheck que ambas funciones son inversas; es decir,
1 2 |
decodificaG (codificaG xs) = xs codificaG (decodificaG n) = n |
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 |
import Data.List (genericLength, group) import Data.Numbers.Primes (primes, primeFactors) import Test.QuickCheck (NonNegative (getNonNegative), NonEmptyList (NonEmpty), Positive (Positive, getPositive), quickCheck) -- 1ª definición de codificaG -- ========================== codificaG1 :: [Integer] -> Integer codificaG1 = codificaG1' . codificaAux codificaAux :: [Integer] -> [Integer] codificaAux = map succ codificaG1' :: [Integer] -> Integer codificaG1' = aux primes where aux _ [] = 1 aux (p:ps) (x:xs) = p^x * aux ps xs -- 2ª definición de codificaG -- ========================== codificaG2 :: [Integer] -> Integer codificaG2 = codificaG2' . codificaAux codificaG2' :: [Integer] -> Integer codificaG2' xs = product [p^x | (p, x) <- zip primes xs] -- 3ª definición de codificaG -- ========================== codificaG3 :: [Integer] -> Integer codificaG3 = codificaG3' . codificaAux codificaG3' :: [Integer] -> Integer codificaG3' xs = product (zipWith (^) primes xs) -- 4ª definición de codificaG -- ========================== codificaG4 :: [Integer] -> Integer codificaG4 = codificaG4' . codificaAux codificaG4' :: [Integer] -> Integer codificaG4' = product . zipWith (^) primes -- Comprobación de equivalencia de codificaG -- ========================================= -- La propiedad es prop_codificaG :: [NonNegative Integer] -> Bool prop_codificaG xs = all (== codificaG1 ys) [codificaG2 ys, codificaG3 ys, codificaG4 ys] where ys = map getNonNegative xs -- La comprobación es -- λ> quickCheck prop_codificaG -- +++ OK, passed 100 tests. -- Comparación de eficiencia de codificaG -- ====================================== -- La comparación es -- λ> length (show (codificaG1 (replicate (4*10^4) 1))) -- 208100 -- (1.73 secs, 2,312,100,136 bytes) -- λ> length (show (codificaG2 (replicate (4*10^4) 1))) -- 208100 -- (1.63 secs, 2,327,676,832 bytes) -- λ> length (show (codificaG3 (replicate (4*10^4) 1))) -- 208100 -- (1.62 secs, 2,323,836,832 bytes) -- λ> length (show (codificaG4 (replicate (4*10^4) 1))) -- 208100 -- (1.54 secs, 2,147,635,680 bytes) -- Definición de codificaG -- ======================= -- Usaremos la 4ª codificaG :: [Integer] -> Integer codificaG = codificaG4 -- Definición de decodificaG -- ========================= decodificaG :: Integer -> [Integer] decodificaG = decodificaAux . decodificaG' decodificaAux :: [Integer] -> [Integer] decodificaAux = map pred decodificaG' :: Integer -> [Integer] decodificaG' 1 = [0] decodificaG' n = aux primes (group (primeFactors n)) where aux _ [] = [] aux (x:xs) ((y:ys):yss) | x == y = 1 + genericLength ys : aux xs yss | otherwise = 0 : aux xs ((y:ys):yss) -- Comprobación de propiedades -- =========================== -- La primera propiedad es prop_decodificaG_codificaG :: NonEmptyList (NonNegative Integer) -> Bool prop_decodificaG_codificaG (NonEmpty xs) = decodificaG (codificaG ys) == ys where ys = map getNonNegative xs -- La comprobación es -- λ> quickCheck prop_decodificaG_codificaG -- +++ OK, passed 100 tests. -- La 2ª propiedad es prop_codificaG_decodificaG :: Positive Integer -> Bool prop_codificaG_decodificaG (Positive n) = codificaG (decodificaG n) == n -- la comprobación es -- λ> quickCheck prop_codificaG_decodificaG -- +++ OK, passed 100 tests. |
El código se encuentra en GitHub.
4. Representación matricial de relaciones binarias
Dada una relación r
sobre un conjunto de números enteros, la matriz asociada a r
es una matriz booleana p
(cuyos elementos son True
o False
), tal que p(i,j) = True
si y sólo si i
está relacionado con j
mediante la relación r
.
Las relaciones binarias homogéneas y las matrices booleanas se pueden representar por
1 2 |
type Relacion = ([Int],[(Int,Int)]) type Matriz = Array (Int,Int) Bool |
Definir la función
1 |
matrizRB:: Relacion -> Matriz |
tal que (matrizRB r)
es la matriz booleana asociada a r
. Por ejemplo,
1 2 3 4 5 6 7 8 9 10 |
λ> matrizRB ([1..3],[(1,1), (1,3), (3,1), (3,3)]) array ((1,1),(3,3)) [((1,1),True) ,((1,2),False),((1,3),True), ((2,1),False),((2,2),False),((2,3),False), ((3,1),True) ,((3,2),False),((3,3),True)] λ> matrizRB ([1..3],[(1,3), (3,1)]) array ((1,1),(3,3)) [((1,1),False),((1,2),False),((1,3),True), ((2,1),False),((2,2),False),((2,3),False), ((3,1),True) ,((3,2),False),((3,3),False)] λ> let n = 10^4 in matrizRB3 ([1..n],[(1,n),(n,1)]) ! (n,n) 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 |
import Data.Array (Array, accumArray, array, listArray) import Test.QuickCheck (Arbitrary, Gen, arbitrary, sublistOf, suchThat, quickCheck) type Relacion = ([Int],[(Int,Int)]) type Matriz = Array (Int,Int) Bool -- 1ª solución -- =========== matrizRB1 :: Relacion -> Matriz matrizRB1 r = array ((1,1),(n,n)) [((a,b), (a,b) `elem` grafo r) | a <- [1..n], b <- [1..n]] where n = maximum (universo r) universo (us,_) = us grafo (_,ps) = ps -- 2ª solución -- =========== matrizRB2 :: Relacion -> Matriz matrizRB2 r = listArray ((1,1),(n,n)) [(a,b) `elem` snd r | a <- [1..n], b <- [1..n]] where n = maximum (fst r) -- 3ª solución -- =========== matrizRB3 :: Relacion -> Matriz matrizRB3 r = accumArray (||) False ((1,1),(n,n)) (zip (snd r) (repeat True)) where n = maximum (fst r) -- Comprobación de equivalencia -- ============================ -- Tipo de relaciones binarias newtype RB = RB Relacion deriving Show -- relacionArbitraria genera una relación arbitraria. Por ejemplo, -- λ> generate relacionArbitraria -- RB ([1,2,3,4,5],[(1,4),(1,5),(2,3),(2,4),(4,2),(4,3),(4,4),(5,1),(5,2),(5,3),(5,4)]) relacionArbitraria :: Gen RB relacionArbitraria = do n <- arbitrary `suchThat` (> 1) xs <- sublistOf [(x,y) | x <- [1..n], y <- [1..n]] return (RB ([1..n], xs)) -- RB es una subclase de Arbitrary instance Arbitrary RB where arbitrary = relacionArbitraria -- La propiedad es prop_matrizRB :: RB -> Bool prop_matrizRB (RB r) = all (== matrizRB1 r) [matrizRB2 r, matrizRB3 r] -- La comprobación es -- λ> quickCheck prop_matrixzB -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> let n = 2000 in matrizRB1 ([1..n],[(1,n),(n,1)]) ! (n,n) -- False -- (2.02 secs, 1,505,248,912 bytes) -- λ> let n = 2000 in matrizRB2 ([1..n],[(1,n),(n,1)]) ! (n,n) -- False -- (1.92 secs, 833,232,360 bytes) -- λ> let n = 2000 in matrizRB3 ([1..n],[(1,n),(n,1)]) ! (n,n) -- False -- (0.05 secs, 32,848,696 bytes) |
El código se encuentra en GitHub.
5. Distancia esperada entre dos puntos de un cuadrado unitario
Definir, por simulación, la función
1 |
distanciaEsperada :: Int -> IO Double |
tal que (distanciaEsperada n)
es la distancia esperada entre n
puntos del cuadrado unitario de vértices opuestos (0,0) y (1,1), elegidos aleatoriamente. Por ejemplo,
1 2 3 4 5 6 |
distanciaEsperada 10 == 0.43903617921423593 distanciaEsperada 10 == 0.6342350621260004 distanciaEsperada 100 == 0.5180418995364429 distanciaEsperada 100 == 0.5288261085653962 distanciaEsperada 1000 == 0.5143804432569616 distanciaEsperada 10000 == 0.5208360147922616 |
El valor exacto de la distancia esperada es
1 |
ve = (sqrt(2) + 2 + 5*log(1+sqrt(2)))/15 = 0.5214054331647207 |
Definir la función
1 |
graficaDistanciaEsperada :: [Int] -> IO () |
tal que (graficaDistanciaEsperada ns)
dibuja las gráficas de los pares (n, distanciaEsperada n)
para n
en la lista creciente ns
junto con la recta y = ve
, donde v
e es el valor exacto. Por ejemplo, (graficaDistanciaEsperada [10,30..4000])
dibuja
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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import Data.List (genericLength) import System.Random (newStdGen, randomRIO, randomRs) import Control.Monad (replicateM) import Graphics.Gnuplot.Simple -- 1ª solución -- =========== -- Un punto es un par de números reales. type Punto = (Double, Double) -- (puntosDelCuadrado n) es una lista de n puntos del cuadrado -- unitario de vértices opuestos (0,0) y (1,1). Por ejemplo, -- λ> puntosDelCuadrado 3 -- [(0.6067427807212623,0.24785843546479303), -- (0.9579158098726746,8.047408846191773e-2), -- (0.856758357789639,0.9814972717003113)] -- λ> puntosDelCuadrado 3 -- [(1.9785720974027532e-2,0.6343219201012211), -- (0.21903717179861604,0.20947986189590784), -- (0.4739903340716357,1.2262474491489095e-2)] puntosDelCuadrado :: Int -> IO [Punto] puntosDelCuadrado n = do gen <- newStdGen let xs = randomRs (0,1) gen (as, ys) = splitAt n xs (bs, _) = splitAt n ys return (zip as bs) -- (distancia p1 p2) es la distancia entre los puntos p1 y p2. Por -- ejemplo, -- distancia (0,0) (3,4) == 5.0 distancia :: Punto -> Punto -> Double distancia (x1,y1) (x2,y2) = sqrt ((x1-x2)^2+(y1-y2)^2) -- (distancias ps) es la lista de las distancias entre los elementos 1º -- y 2º, 3º y 4º, ... de ps. Por ejemplo, -- distancias [(0,0),(3,4),(1,1),(7,9)] == [5.0,10.0] distancias :: [Punto] -> [Double] distancias [] = [] distancias (p1:p2:ps) = distancia p1 p2 : distancias ps -- (media xs) es la media aritmética de los elementos de xs. Por ejemplo, -- media [1,7,1] == 3.0 media :: [Double] -> Double media xs = sum xs / genericLength xs -- (distanciaEsperada n) es la distancia esperada entre n puntos -- aleatorios en el cuadrado unitario. Por ejemplo, -- λ> distanciaEsperada 100 -- 0.4712858421448363 -- λ> distanciaEsperada 100 -- 0.4947745206856711 distanciaEsperada :: Int -> IO Double distanciaEsperada n = do ps <- puntosDelCuadrado (2*n) return (media (distancias ps)) -- 2ª solución -- =========== distanciaEsperada2 :: Int -> IO Double distanciaEsperada2 n = do ps <- puntosDelCuadrado2 (2*n) return (media (distancias ps)) -- (puntosDelCuadrado2 n) es una lista de n puntos del cuadrado -- unitario de vértices opuestos (0,0) y (1,1). Por ejemplo, -- λ> puntosDelCuadrado2 3 -- [(0.9836699352638695,0.5143414844876929), -- (0.8715237339877027,0.9905157772823782), -- (0.29502946161912935,0.16889248111565192)] -- λ> puntosDelCuadrado2 3 -- [(0.20405570457106392,0.47574116941605116), -- (0.7128182811364226,3.201419787777959e-2), -- (0.5576891231675457,0.9994474730919443)] puntosDelCuadrado2 :: Int -> IO [Punto] puntosDelCuadrado2 n = replicateM n puntoDelCuadrado2 -- (puntoDelCuadrado2 n) es un punto del cuadrado unitario de vértices -- opuestos (0,0) y (1,1). Por ejemplo, -- λ> puntoDelCuadrado2 -- (0.7512991739803923,0.966436016138578) -- λ> puntoDelCuadrado2 -- (0.7306826194847795,0.8984574498515252) puntoDelCuadrado2 :: IO Punto puntoDelCuadrado2 = do x <- randomRIO (0, 1.0) y <- randomRIO (0, 1.0) return (x, y) -- 3ª solución -- =========== distanciaEsperada3 :: Int -> IO Double distanciaEsperada3 n = do ds <- distanciasAleatorias n return (media ds) -- (distanciasAleatorias n) es la lista de las distancias aleatorias -- entre n pares de puntos del cuadrado unitario. Por ejemplo, -- λ> distanciasAleatorias 3 -- [0.8325589110989705,0.6803336613847881,0.1690051224111662] -- λ> distanciasAleatorias 3 -- [0.3470124940889039,0.459002678562019,0.7665623634969365] distanciasAleatorias :: Int -> IO [Double] distanciasAleatorias n = replicateM n distanciaAleatoria -- distanciaAleatoria es la distancia de un par de punto del cuadrado -- unitario elegidos aleatoriamente. Por ejemplo, -- λ> distanciaAleatoria -- 0.8982361685460913 -- λ> distanciaAleatoria -- 0.9777207485571939 -- λ> distanciaAleatoria -- 0.6042223512347842 distanciaAleatoria :: IO Double distanciaAleatoria = do p1 <- puntoDelCuadrado2 distancia p1 <$> puntoDelCuadrado2 -- 4ª solución -- =========== distanciaEsperada4 :: Int -> IO Double distanciaEsperada4 n = media <$> distanciasAleatorias n -- Gráfica -- ======= graficaDistanciaEsperada :: [Int] -> IO () graficaDistanciaEsperada ns = do ys <- mapM distanciaEsperada ns let e = (sqrt 2 + 2 + 5 * log (1 + sqrt 2)) / 15 plotLists [ Key Nothing -- , PNG "Distancia_esperada_entre_dos_puntos.png" ] [ zip ns ys , zip ns (repeat e)] |
El código se encuentra en GitHub.