Densidades de números abundantes, perfectos y deficientes
La n-ésima densidad de un tipo de número es el cociente entre la cantidad de los números entre 1 y n que son del tipo considerado y n. Por ejemplo, la 7-ésima densidad de los múltiplos de 3 es 2/7 ya que entre los 7 primeros números sólo 2 son múltiplos de 3.
Definir las funciones
1 2 |
densidades :: Int -> (Double,Double,Double) graficas :: Int -> IO () |
tales que
(densidades n)
es la terna formada por la n-ésima densidad- de los números abundantes (es decir, para los que la suma de sus divisores propios es mayor que el número),
- de los números perfectos (es decir, para los que la suma de sus divisores propios es mayor que el número) y
- de los números deficientes (es decir, para los que la suma de sus divisores propios es menor que el número).
Por ejemplo,
1 2 3 4 5 |
densidades 100 == (0.22, 2.0e-2, 0.76) densidades 1000 == (0.246, 3.0e-3, 0.751) densidades 10000 == (0.2488, 4.0e-4, 0.7508) densidades 100000 == (0.24795, 4.0e-5, 0.75201) densidades 1000000 == (0.247545,4.0e-6, 0.752451) |
(graficas n)
dibuja las gráficas de las k-ésimas densidades (para k entre 1 y n) de los números abundantes, de los números perfectos y de los números deficientes. Por ejemplo,(graficas 100)
dibuja
y(graficas 400)
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 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 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
import Data.List (genericLength, group, partition) import Data.Array (accumArray, assocs) import Data.Numbers.Primes (primeFactors) import Graphics.Gnuplot.Simple (plotLists, Attribute (Key)) import Test.QuickCheck (Positive (Positive), quickCheck) -- 1ª solución -- =========== densidades1 :: Int -> (Double,Double,Double) densidades1 n = (f a, f p, f d) where a = nAbundantes n p = nPerfectos n d = n - a - p f x = fromIntegral x / fromIntegral n -- (nAbundantes n) es la cantidad de números abundantes desde 1 hasta -- n. Por ejemplo, -- nAbundantes 100 == 22 nAbundantes :: Int -> Int nAbundantes n = length (filter esAbundante [1..n]) -- (esAbundante n) se verifica si n es un número abundante. Por ejemplo, -- esAbundante 12 == True -- esAbundante 22 == False esAbundante :: Int -> Bool esAbundante n = sumaDivisores n > n -- (sumaDivisores n) es la suma de los divisores propios de n. Por -- ejemplo, -- sumaDivisores 12 == 16 -- sumaDivisores 22 == 14 sumaDivisores :: Int -> Int sumaDivisores = sum . divisores -- (divisores n) es la lista de los divisores propios de n. Por ejemplo, -- divisores 12== [1,2,3,4,6] -- divisores 22== [1,2,11] divisores :: Int -> [Int] divisores n = [x | x <- [1..n-1], n `mod` x == 0] -- (nPerfectos n) es la cantidad de números perfectos desde 1 hasta -- n. Por ejemplo, -- nPerfectos 100 == 2 nPerfectos :: Int -> Int nPerfectos n = length (filter esPerfecto [1..n]) -- (esPerfecto n) se verifica si n es un número perfecto. Por ejemplo, -- esPerfecto 28 == True -- esPerfecto 38 == False esPerfecto :: Int -> Bool esPerfecto n = sumaDivisores n == n -- 2ª solución -- =========== densidades2 :: Int -> (Double,Double,Double) densidades2 n = (f as, f ps, f ds) where (as,pds) = partition esAbundante [1..n] (ps,ds) = partition esPerfecto pds f xs = genericLength xs / fromIntegral n -- 3ª solución -- =========== densidades3 :: Int -> (Double,Double,Double) densidades3 n = (f as, f ps, f ds) where cs = map clasificacion [1..n] (as,pds) = partition (== Abundante) cs (ps,ds) = partition (== Perfecto) pds f xs = genericLength xs / fromIntegral n data Clase = Abundante | Perfecto | Deficiente deriving (Eq, Show) -- (clasificacion n) es la clase de número de n. Por ejemplo, -- clasificacion 12 == Abundante -- clasificacion 22 == Deficiente -- clasificacion 28 == Perfecto clasificacion :: Int -> Clase clasificacion n | sd > n = Abundante | sd < n = Deficiente | otherwise = Perfecto where sd = sumaDivisores n -- 4ª solución -- =========== densidades4 :: Int -> (Double,Double,Double) densidades4 n = (f as, f ps, f ds) where cs = map clasificacion2 [1..n] (as,pds) = partition (== Abundante) cs (ps,ds) = partition (== Perfecto) pds f xs = genericLength xs / fromIntegral n -- 2ª definición de clasificacion clasificacion2 :: Int -> Clase clasificacion2 n | sd > n = Abundante | sd < n = Deficiente | otherwise = Perfecto where sd = sumaDivisores2 n -- 2ª definición de sumaDivisores sumaDivisores2 :: Int -> Int sumaDivisores2 x = product [(p^(e+1)-1) `div` (p-1) | (p,e) <- factorizacion x] - x -- (factorizacion x) es la lista de las bases y exponentes de la -- descomposición prima de x. Por ejemplo, -- factorizacion 600 == [(2,3),(3,1),(5,2)] factorizacion :: Int -> [(Int,Int)] factorizacion = map primeroYlongitud . group . primeFactors -- (primeroYlongitud xs) es el par formado por el primer elemento de xs -- y la longitud de xs. Por ejemplo, -- primeroYlongitud [3,2,5,7] == (3,4) primeroYlongitud :: [a] -> (a,Int) primeroYlongitud (x:xs) = (x, 1 + length xs) -- 5ª solución -- =========== densidades5 :: Int -> (Double,Double,Double) densidades5 n = (f a, f p, f d) where (a,p,d) = distribucion n f x = fromIntegral x / fromIntegral n -- (distribucion n) es la terna (a,p,d) donde a es la cantidad de -- números abundantes de 1 a n, p la de los perfectos y d la de los -- deficientes. Por ejemplo, -- distribucion 100 == (22,2,76) distribucion :: Int -> (Int,Int,Int) distribucion n = aux (0,0,0) (sumaDivisoresHasta n) where aux (a,p,d) [] = (a,p,d) aux (a,p,d) ((x,y):xys) | x < y = aux (1+a,p,d) xys | x > y = aux (a,p,1+d) xys | otherwise = aux (a,1+p,d) xys -- (sumaDivisoresHasta n) es la lista de los pares (a,b) tales que a -- varía entre 1 y n y b es la suma de los divisores propios de a. Por -- ejemplo, -- λ> sumaDivisoresHasta 12 -- [(1,0),(2,1),(3,1),(4,3),(5,1),(6,6),(7,1),(8,7),(9,4),(10,8),(11,1),(12,16)] sumaDivisoresHasta :: Int -> [(Int,Int)] sumaDivisoresHasta n = assocs (accumArray (+) 0 (1,n) (divisoresHasta n)) -- (divisoresHasta n) es la lista de los pares (a,b) tales que a está -- entre 2 y n y b es un divisor propio e x. Por ejemplo, -- λ> divisoresHasta 6 -- [(2,1),(3,1),(4,1),(5,1),(6,1),(4,2),(6,2),(6,3)] -- λ> divisoresHasta 8 -- [(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(4,2),(6,2),(8,2),(6,3),(8,4)] divisoresHasta :: Int -> [(Int,Int)] divisoresHasta n = [(a,b) | b <- [1..n `div` 2], a <- [b*2, b*3..n]] -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_densidades :: Positive Int -> Bool prop_densidades (Positive n) = all (== densidades1 n) [ densidades2 n , densidades3 n , densidades4 n , densidades5 n ] -- La comprobación es -- λ> quickCheck prop_densidades -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> densidades1 2000 -- (0.2465,1.5e-3,0.752) -- (1.59 secs, 804,883,768 bytes) -- λ> densidades2 2000 -- (0.2465,1.5e-3,0.752) -- (1.39 secs, 704,749,552 bytes) -- λ> densidades3 2000 -- (0.2465,1.5e-3,0.752) -- (0.81 secs, 403,783,312 bytes) -- λ> densidades4 2000 -- (0.2465,1.5e-3,0.752) -- (0.04 secs, 34,364,960 bytes) -- λ> densidades5 2000 -- (0.2465,1.5e-3,0.752) -- (0.02 secs, 4,716,720 bytes) -- -- λ> densidades4 100000 -- (0.24795,4.0e-5,0.75201) -- (1.61 secs, 4,826,971,624 bytes) -- λ> densidades5 100000 -- (0.24795,4.0e-5,0.75201) -- (0.43 secs, 291,989,272 bytes) -- Gráfica -- ======= graficas :: Int -> IO () graficas n = plotLists [Key Nothing] [ [x | (x,_,_) <- ts] , [y | (_,y,_) <- ts] , [z | (_,_,z) <- ts]] where ts = [densidades5 k | k <- [1..n]] |
El código se encuentra en GitHub.