import qualified Data.MemoCombinators as Memo (integral)
import Data.List (genericLength, genericTake, maximumBy)
import Test.QuickCheck (Positive(..), quickCheck)
-- 1ª solución
-- ===========
mayoresGeneradores :: Integer -> [Integer]
mayoresGeneradores n =
[x | (x,y) <- ps, y == m]
where ps = genericTake n longitudesOrbitas
m = maximum (map snd ps)
-- longitudesOrbita es la lista de los números junto a las longitudes de
-- las órbitas de Collatz que generan. Por ejemplo,
-- λ> take 10 longitudesOrbitas
-- [(1,1),(2,2),(3,8),(4,3),(5,6),(6,9),(7,17),(8,4),(9,20),(10,7)]
longitudesOrbitas :: [(Integer, Integer)]
longitudesOrbitas =
[(n, genericLength (collatz n)) | n <- [1..]]
-- (siguiente n) es el siguiente de n en la sucesión de Collatz. Por
-- ejemplo,
-- siguiente 13 == 40
-- siguiente 40 == 20
siguiente :: Integer -> Integer
siguiente n | even n = n `div` 2
| otherwise = 3*n+1
-- (collatz1 n) es la órbita de Collatz de n hasta alcanzar el
-- 1. Por ejemplo,
-- collatz 13 == [13,40,20,10,5,16,8,4,2,1]
-- 1ª definición de collatz
collatz1 :: Integer -> [Integer]
collatz1 1 = [1]
collatz1 n = n : collatz1 (siguiente n)
-- 2ª definición de collatz
collatz2 :: Integer -> [Integer]
collatz2 n = takeWhile (/=1) (iterate siguiente n) ++ [1]
-- Usaremos la 2ª definición de collatz
collatz :: Integer -> [Integer]
collatz = collatz2
-- 2ª solución
-- ===========
mayoresGeneradores2 :: Integer -> [Integer]
mayoresGeneradores2 n =
[x | (x,y) <- ps, y == m]
where ps = [(x, longitudOrbita x) | x <- [1..n]]
m = maximum (map snd ps)
-- (longitudOrbita x) es la longitud de la órbita de x. Por ejemplo,
-- longitudOrbita 13 == 10
longitudOrbita :: Integer -> Integer
longitudOrbita 1 = 1
longitudOrbita x = 1 + longitudOrbita (siguiente x)
-- 3ª solución
-- ===========
mayoresGeneradores3 :: Integer -> [Integer]
mayoresGeneradores3 n =
[x | (x,y) <- ps, y == m]
where ps = [(x, longitudOrbita2 x) | x <- [1..n]]
m = maximum (map snd ps)
longitudOrbita2 :: Integer -> Integer
longitudOrbita2 = Memo.integral longitudOrbita2'
where
longitudOrbita2' 1 = 1
longitudOrbita2' x = 1 + longitudOrbita2 (siguiente x)
-- Equivalencia de definiciones
-- ============================
-- La propiedad es
prop_mayoresGeneradores :: (Positive Integer) -> Bool
prop_mayoresGeneradores (Positive n) =
all (== (mayoresGeneradores n))
[mayoresGeneradores2 n,
mayoresGeneradores3 n]
-- La comprobación es
-- λ> quickCheck prop_mayoresGeneradores
-- +++ OK, passed 100 tests.
-- Comprobación de eficiencia
-- ==========================
-- La comprobación es
-- λ> mayoresGeneradores (10^5)
-- [77031]
-- (5.43 secs, 6,232,320,064 bytes)
-- λ> mayoresGeneradores2 (10^5)
-- [77031]
-- (7.68 secs, 5,238,991,616 bytes)
-- λ> mayoresGeneradores3 (10^5)
-- [77031]
-- (0.88 secs, 571,788,736 bytes)