import Data.List ((\\))
import Data.Char (digitToInt)
import Graphics.Gnuplot.Simple (plotList, Attribute (Key, Title, XRange, PNG))
-- 1ª definición de sucLoomis
-- ==========================
sucLoomis :: Integer -> [Integer]
sucLoomis x = map (loomis x) [0..]
loomis :: Integer -> Integer -> Integer
loomis x 0 = x
loomis x n = y + productoDigitosNoNulos y
where y = loomis x (n-1)
productoDigitosNoNulos :: Integer -> Integer
productoDigitosNoNulos = product . digitosNoNulos
digitosNoNulos :: Integer -> [Integer]
digitosNoNulos x =
[read [c] | c <- show x, c /= '0']
-- 2ª definición de sucLoomis
-- ==========================
sucLoomis2 :: Integer -> [Integer]
sucLoomis2 = iterate siguienteLoomis
siguienteLoomis :: Integer -> Integer
siguienteLoomis y = y + productoDigitosNoNulos y
-- 3ª definición de sucLoomis
-- ==========================
sucLoomis3 :: Integer -> [Integer]
sucLoomis3 =
iterate ((+) <*> product .
map (toInteger . digitToInt) .
filter (/= '0') . show)
-- Comparación de eficiencia
-- =========================
-- λ> sucLoomis 1 !! 30000
-- 6571272766
-- (2.45 secs, 987,955,944 bytes)
-- λ> sucLoomis2 1 !! 30000
-- 6571272766
-- (2.26 secs, 979,543,328 bytes)
-- λ> sucLoomis3 1 !! 30000
-- 6571272766
-- (0.31 secs, 88,323,832 bytes)
-- 1ª definición de convergencia
-- =============================
convergencia1 :: Integer -> Integer
convergencia1 x =
head (dropWhile noEnSucLoomisDe1 (sucLoomis x))
noEnSucLoomisDe1 :: Integer -> Bool
noEnSucLoomisDe1 x = not (pertenece x sucLoomisDe1)
sucLoomisDe1 :: [Integer]
sucLoomisDe1 = sucLoomis 1
pertenece :: Integer -> [Integer] -> Bool
pertenece x ys =
x == head (dropWhile (<x) ys)
-- 2ª definición de convergencia
-- =============================
convergencia2 :: Integer -> Integer
convergencia2 = aux (sucLoomis3 1) . sucLoomis3
where aux as@(x:xs) bs@(y:ys) | x == y = x
| x < y = aux xs bs
| otherwise = aux as ys
-- 3ª definición de convergencia
-- =============================
convergencia3 :: Integer -> Integer
convergencia3 = head . interseccion (sucLoomis3 1) . sucLoomis3
-- (interseccion xs ys) es la intersección entre las listas ordenadas xs
-- e ys. Por ejemplo,
-- λ> take 10 (interseccion (sucLoomis3 1) (sucLoomis3 2))
-- [2,4,8,16,22,26,38,62,74,102]
interseccion :: Ord a => [a] -> [a] -> [a]
interseccion = aux
where aux as@(x:xs) bs@(y:ys) = case compare x y of
LT -> aux xs bs
EQ -> x : aux xs ys
GT -> aux as ys
aux _ _ = []
-- 4ª definición de convergencia
-- =============================
convergencia4 :: Integer -> Integer
convergencia4 x = perteneceA (sucLoomis3 x) 1
where perteneceA (y:ys) n | y == c = y
| otherwise = perteneceA ys c
where c = head $ dropWhile (< y) $ sucLoomis3 n
-- Comparación de eficiencia
-- =========================
-- λ> convergencia1 (10^4)
-- 150056
-- (2.94 secs, 1,260,809,808 bytes)
-- λ> convergencia2 (10^4)
-- 150056
-- (0.03 secs, 700,240 bytes)
-- λ> convergencia3 (10^4)
-- 150056
-- (0.03 secs, 1,165,496 bytes)
-- λ> convergencia4 (10^4)
-- 150056
-- (0.02 secs, 1,119,648 bytes)
--
-- λ> convergencia2 (10^12)
-- 1000101125092
-- (1.81 secs, 714,901,080 bytes)
-- λ> convergencia3 (10^12)
-- 1000101125092
-- (1.92 secs, 744,932,184 bytes)
-- λ> convergencia4 (10^12)
-- 1000101125092
-- (1.82 secs, 941,053,328 bytes)
-- Definición de graficaConvergencia
-- ==================================
graficaConvergencia :: [Integer] -> IO ()
graficaConvergencia xs =
plotList [ Key Nothing
, Title "Convergencia de sucesiones de Loomis"
, XRange (fromIntegral (minimum xs),fromIntegral (maximum xs))
, PNG "Las_sucesiones_de_Loomis_2.png"
]
[(x,convergencia2 x) | x <- xs]