Parejas de números y divisores
Definir la función
divisoresHasta :: Int -> [(Int,Int)]
tal que (divisoresHasta n) es la lista de los pares (a,b) tales que a es un número entre 2 y n y b es un divisor propio de a. Por ejemplo,
λ> divisoresHasta 6 [(2,1),(3,1),(4,1),(4,2),(5,1),(6,1),(6,2),(6,3)] λ> divisoresHasta 8 [(2,1),(3,1),(4,1),(4,2),(5,1),(6,1),(6,2),(6,3),(7,1),(8,1),(8,2),(8,4)] λ> length (divisoresHasta 1234567) 16272448
Soluciones
import Data.List (sort) import Data.Array (accumArray, (!)) import Test.Hspec (Spec, describe, hspec, it, shouldBe) import Test.QuickCheck -- 1ª solución: Enfoque directo por comprensión -- ============================================ divisoresHasta1 :: Int -> [(Int,Int)] divisoresHasta1 n = [(a,b) | a <- [2..n], b <- [1..a `div` 2], a `mod` b == 0] -- 2ª solución: Enfoque funcional usando concatMap -- =============================================== divisoresHasta2 :: Int -> [(Int,Int)] divisoresHasta2 n = concatMap (\a -> [(a,b) | b <- [1..a `div` 2], a `mod` b == 0]) [2..n] -- 3ª solución: Enfoque de criba (generación de múltiplos) y ordenación -- ==================================================================== divisoresHasta3 :: Int -> [(Int,Int)] divisoresHasta3 n = sort [(a,b) | b <- [1..n `div` 2], a <- [2*b, 3*b..n]] -- 4ª solución: Criba optimizada con Array -- ======================================= divisoresHasta4 :: Int -> [(Int,Int)] divisoresHasta4 n = [(a, b) | a <- [2..n], b <- tabla ! a] where tabla = accumArray (flip (:)) [] (2, n) [(k*b, b) | b <- [n `div` 2, n `div` 2 - 1 .. 1], k <- [2 .. n `div` b]] -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (Int -> [(Int,Int)]) -> Spec specG divisoresHasta = do it "e1" $ divisoresHasta 6 `shouldBe` [(2,1),(3,1),(4,1),(4,2),(5,1),(6,1),(6,2),(6,3)] it "e2" $ divisoresHasta 8 `shouldBe` [(2,1),(3,1),(4,1),(4,2),(5,1),(6,1),(6,2),(6,3),(7,1),(8,1),(8,2),(8,4)] spec :: Spec spec = do describe "def. 1" $ specG divisoresHasta1 describe "def. 2" $ specG divisoresHasta2 describe "def. 3" $ specG divisoresHasta3 describe "def. 4" $ specG divisoresHasta4 -- La verificación es -- λ> verifica -- 4 examples, 0 failures -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_equivalencia :: Positive Int -> Bool prop_equivalencia (Positive n) = all (== divisoresHasta1 n) [ divisoresHasta2 n , divisoresHasta3 n , divisoresHasta4 n ] -- La comprobación es -- λ> quickCheck prop_equivalencia -- +++ OK, passed 100 tests. -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> last (divisoresHasta1 4000) -- (4000,2000) -- (1.59 secs, 804,026,232 bytes) -- λ> last (divisoresHasta2 4000) -- (4000,2000) -- (1.63 secs, 805,823,288 bytes) -- λ> last (divisoresHasta3 4000) -- (4000,2000) -- (0.05 secs, 31,032,920 bytes) -- λ> last (divisoresHasta4 4000) -- (4000,2000) -- (0.05 secs, 13,010,816 bytes) -- -- λ> last (divisoresHasta3 100000) -- (100000,50000) -- (1.81 secs, 1,462,358,336 bytes) -- λ> last (divisoresHasta4 100000) -- (100000,50000) -- (0.99 secs, 428,785,568 bytes)