Ir al contenido principal

Suma de divisores

Definir las funciones

divisores     :: Integer -> [Integer]
sumaDivisores :: Integer -> Integer

tales que

  • (divisores x) es la lista de los divisores de x. Por ejemplo,
divisores 12  ==  [1,2,3,4,6,12]
divisores 25  ==  [1,5,25]
length (divisores (product [1..12]))  ==  792
length (divisores 131535436245601)    ==  32
  • (sumaDivisores x) es la suma de los divisores de x. Por ejemplo,
sumaDivisores 12  ==  28
sumaDivisores 25  ==  31
sumaDivisores (product [1..12])  ==  2217441408
sumaDivisores 131535436245601    ==  132534784471040

Soluciones

import Data.List (group, inits, nub, sort, subsequences)
import Data.Numbers.Primes (primeFactors)

-- 1ª definición de divisores
divisores1 :: Integer -> [Integer]
divisores1 x =
  [y | y <- [1..x]
     , x `mod` y == 0]

-- 2ª definición de divisores
divisores2 :: Integer -> [Integer]
divisores2 n =
  filter ((== 0) . mod n) [1..n]

-- 3ª definición de divisores
divisores3 :: Integer -> [Integer]
divisores3 =
  nub . sort . map product . subsequences . primeFactors

-- 4ª definición
-- =============

divisores4 :: Integer -> [Integer]
divisores4 =
  sort
  . map (product . concat)
  . productoCartesiano
  . map inits
  . group
  . primeFactors

-- (producto xss) es el producto cartesiano de los conjuntos xss. Por
-- ejemplo,
--    λ> producto [[1,3],[2,5],[6,4]]
--    [[1,2,6],[1,2,4],[1,5,6],[1,5,4],[3,2,6],[3,2,4],[3,5,6],[3,5,4]]
productoCartesiano :: [[a]] -> [[a]]
productoCartesiano []       = [[]]
productoCartesiano (xs:xss) =
  [x:ys | x <- xs, ys <- productoCartesiano xss]

-- Comparación de eficiencia de divisores
--    λ> length (divisores1 (product [1..10]))
--    270
--    (5.32 secs, 725,922,264 bytes)
--    λ> length (divisores2 (product [1..10]))
--    270
--    (1.75 secs, 435,611,632 bytes)
--    λ> length (divisores3 (product [1..10]))
--    270
--    (0.09 secs, 51,079,512 bytes)
--    λ> length (divisores4 (product [1..10]))
--    270
--    (0.02 secs, 863,768 bytes)
--
--    λ> length (divisores3 13082761331670030)
--    16384
--    (49.89 secs, 25,296,488 bytes)
--    λ> length (divisores4 13082761331670030)
--    16384
--    (0.25 secs, 78,694,200 bytes)

-- En lo que sigue usamos la 4ª definición de divisores
divisores :: Integer -> [Integer]
divisores = divisores4

-- 1ª definición de sumaDivisores
sumaDivisores :: Integer -> Integer
sumaDivisores =
  sum . divisores

-- 2ª definición de sumaDivisores
-- ==============================

-- Si la descomposición de x en factores primos es
--    x = p(1)^e(1) . p(2)^e(2) . .... . p(n)^e(n)
-- entonces la suma de los divisores de x es
--    p(1)^(e(1)+1) - 1     p(2)^(e(2)+1) - 1       p(n)^(e(2)+1) - 1
--   ------------------- . ------------------- ... -------------------
--        p(1)-1                p(2)-1                  p(n)-1
-- Ver la demostración en http://bit.ly/2zUXZPc

sumaDivisores2 :: Integer -> Integer
sumaDivisores2 x =
  product [(p^(e+1)-1) `div` (p-1) | (p,e) <- basesYexponentes x]

-- (basesYexponentes x) son las bases y exponentes de la descomposición
-- prima de x. Por ejemplo,
--    basesYexponentes 18000 == [(2,4),(3,2),(5,3)]
basesYexponentes :: Integer -> [(Integer,Integer)]
basesYexponentes x =
  map primeroYlongitud (group (primeFactors x))

-- (primeroYlongitus) 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,Integer)
primeroYlongitud (x:xs) =
  (x, 1 + genericLength xs)

-- Comparación de eficiencia de sumaDivisores
-- ==========================================

--    λ> sumaDivisores 251888923423315469521109880000000
--    1471072204661054993275791673480320
--    (10.43 secs, 10,658,208,360 bytes)
--    λ> sumaDivisores2 251888923423315469521109880000000
--    1471072204661054993275791673480320
--    (0.00 secs, 223,488 bytes)