Ir al contenido principal

Sucesión de suma de cuadrados de los dígitos

Definir la función

sucSumaCuadradosDigitos :: Integer -> [Integer]

tal que (sucSumaCuadradosDigitos n) es la sucesión cuyo primer término es n y los restantes se obtienen sumando los cuadrados de los dígitos de su término anterior. Por ejemplo,

λ> take 20 (sucSumaCuadradosDigitos 2016)
[2016,41,17,50,25,29,85,89,145,42,20,4,16,37,58,89,145,42,20,4]
λ> take 20 (sucSumaCuadradosDigitos 1976)
[1976,167,86,100,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
λ> sucSumaCuadradosDigitos 2016 !! (10^8)
145

Soluciones

-- 1ª definición
-- =============

sucSumaCuadradosDigitos1 :: Integer -> [Integer]
sucSumaCuadradosDigitos1 n = iterate sumaCuadradosDigitos n

-- ---------------------------------------------------------------------
-- (sumaCuadradosDigitos n) es la suma de los cuadrados de los dígitos
-- de n. Por ejemplo,
--    sumaCuadradosDigitos 2016  ==  41
sumaCuadradosDigitos :: Integer -> Integer
sumaCuadradosDigitos n = sum (map (^2) (digitos n))

-- (digitos n) es la lista de los dígitos de n. Por ejemplo,
--    digitos 325  ==  [3,2,5]
digitos :: Integer -> [Integer]
digitos n = [read [d] | d <- show n]

-- 2ª definición
-- =============

sucSumaCuadradosDigitos2 :: Integer -> [Integer]
sucSumaCuadradosDigitos2 n =
    n : [sumaCuadradosDigitos x | x <- sucSumaCuadradosDigitos2 n]

-- 3ª definición
-- =============

-- A partir de los cálculos con las definiciones anteriores, se observa
-- que para todo n (sucSumaCuadradosDigitos n) tiene una parte pura y
-- otra periódica. Por ejemplo, para n = 2016,
--      λ> take 20 (sucSumaCuadradosDigitos 2016)
--      [2016,41,17,50,25,29,85,89,145,42,20,4,16,37,58,89,145,42,20,4]
-- la parte pura es
--      [2016,41,17,50,25,29,85]
-- y la parte periódica es
--      [89,145,42,20,4,16,37,58])

sucSumaCuadradosDigitos3 :: Integer -> [Integer]
sucSumaCuadradosDigitos3 n = xs ++ cycle ys
    where (xs,ys) = sucCompactaSumaCuadradosDigitos n

-- (sucCompactaSumaCuadradosDigitos n) es el par formado por la parte
-- pura y la periódica de (sucSumaCuadradosDigitos n). Por ejemplo,
--    λ> sucCompactaSumaCuadradosDigitos 2016
--    ([2016,41,17,50,25,29,85],[89,145,42,20,4,16,37,58])
--    λ> sucCompactaSumaCuadradosDigitos 1976
--    ([1976,167,86,100],[1])
sucCompactaSumaCuadradosDigitos :: Integer -> ([Integer],[Integer])
sucCompactaSumaCuadradosDigitos =
    partePuraPeriodica . sucSumaCuadradosDigitos1

-- (partePuraPeriodica xs) es el par formado por la parte pura y la
-- periódica de xs. Por ejemplo,
--    λ> partePuraPeriodica (sucSumaCuadradosDigitos 2016)
--    ([2016,41,17,50,25,29,85],[89,145,42,20,4,16,37,58])
--    λ> partePuraPeriodica (sucSumaCuadradosDigitos 1976)
--    ([1976,167,86,100],[1])
partePuraPeriodica :: [Integer] -> ([Integer],[Integer])
partePuraPeriodica xs = aux [] xs
    where aux as (b:bs) | elem b as = span (/=b) (reverse as)
                        | otherwise = aux (b:as) bs

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

--    λ> sucSumaCuadradosDigitos1 2016 !! (10^5)
--    145
--    (4.13 secs, 1,568,615,040 bytes)
--    λ> sucSumaCuadradosDigitos2 2016 !! (10^5)
--    145
--    (0.04 secs, 16,620,344 bytes)
--    λ> sucSumaCuadradosDigitos3 2016 !! (10^5)
--    145
--    (0.01 secs, 0 bytes)
--
--    λ> sucSumaCuadradosDigitos2 2016 !! (10^8)
--    145
--    (24.54 secs, 15,998,659,480 bytes)
--    λ> sucSumaCuadradosDigitos3 2016 !! (10^8)
--    145
--    (0.01 secs, 0 bytes)