Ir al contenido principal

Suma de todos los anteriores


Definir la función

sumaAnteriores :: [Integer] -> Bool

tal que (sumaAnteriores xs) se verifica si cada elemento de la lista xs (excepto el primero) es la suma de sus anteriores elementos en la lista. Por ejemplo,

sumaAnteriores [3,3,6,12]  ==  True
sumaAnteriores [3,3,7,10]  ==  False
sumaAnteriores [3]         ==  True
sumaAnteriores []          ==  True

Soluciones

import Data.List (isPrefixOf)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

-- 1ª solución
-- ===========

sumaAnteriores1 :: [Integer] -> Bool
sumaAnteriores1 xs = aux (reverse xs)
  where aux []     = True
        aux [_]    = True
        aux (y:ys) = y == sum ys && aux ys

-- 2ª solución
-- ===========

sumaAnteriores2 :: [Integer] -> Bool
sumaAnteriores2 (x:y:zs) =
  x == y && and [b == 2*a | (a,b) <- adyacentes (y:zs)]
sumaAnteriores2 _ = True

-- (adyacentes xs) es la lista de los elementos adyacentes de xs. Por
-- ejemplo,
--    adyacentes [3,3,6,12]  ==  [(3,3),(3,6),(6,12)]
adyacentes :: [a] -> [(a,a)]
adyacentes xs = zip xs (tail xs)

-- 3ª solución
-- ===========

sumaAnteriores3 :: [Integer] -> Bool
sumaAnteriores3 (x:xs) =
  xs == [x*2^n | n <- [0.. length xs - 1]]
sumaAnteriores3 _ = True

-- 4ª solución
-- ===========

sumaAnteriores4 :: [Integer] -> Bool
sumaAnteriores4 (x:xs) =
  xs `isPrefixOf` [x*2^n | n <- [0..]]
sumaAnteriores4 _ = True

-- 5ª solución
-- ===========

sumaAnteriores5 :: [Integer] -> Bool
sumaAnteriores5 [] = True
sumaAnteriores5 [_] = True
sumaAnteriores5 (x:xs) = aux x xs
  where
    aux _ [] = True
    aux z (y:ys) = y == z && aux (z + y) ys

-- Verificación
-- ============

verifica :: IO ()
verifica = hspec spec

specG :: ([Integer] -> Bool) -> Spec
specG sumaAnteriores = do
  it "e1" $
    sumaAnteriores [3,3,6,12]  `shouldBe`  True
  it "e2" $
    sumaAnteriores [3,3,7,10]  `shouldBe`  False
  it "e3" $
    sumaAnteriores [3]         `shouldBe`  True
  it "e4" $
    sumaAnteriores []          `shouldBe`  True

spec :: Spec
spec = do
  describe "def. 1" $ specG sumaAnteriores1
  describe "def. 2" $ specG sumaAnteriores2
  describe "def. 3" $ specG sumaAnteriores3
  describe "def. 4" $ specG sumaAnteriores4
  describe "def. 5" $ specG sumaAnteriores5

-- La verificación es
--    λ> verifica
--    20 examples, 0 failures

-- Comprobación de equivalencia
-- ============================

-- La propiedad es
prop_sumaAnteriores :: [Integer] -> Bool
prop_sumaAnteriores xs =
  all (== sumaAnteriores1 xs)
      [sumaAnteriores2 xs,
       sumaAnteriores3 xs,
       sumaAnteriores4 xs,
       sumaAnteriores5 xs]

-- La comprobación es
--    λ> quickCheck prop_sumaAnteriores
--    +++ OK, passed 100 tests.

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

-- La comparación es
--    λ> sumaAnteriores1 (3:[3*2^n | n <- [0..10000]])
--    True
--    (5.17 secs, 43,954,899,000 bytes)
--    λ> sumaAnteriores2 (3:[3*2^n | n <- [0..10000]])
--    True
--    (0.11 secs, 78,722,256 bytes)
--    λ> sumaAnteriores3 (3:[3*2^n | n <- [0..10000]])
--    True
--    (0.10 secs, 143,635,224 bytes)
--    λ> sumaAnteriores4 (3:[3*2^n | n <- [0..10000]])
--    True
--    (0.13 secs, 136,506,888 bytes)
--    λ> sumaAnteriores5 (3:[3*2^n | n <- [0..10000]])
--    True
--    (0.09 secs, 77,922,168 bytes)
--
--    λ> sumaAnteriores2 (3:[3*2^n | n <- [0..60000]])
--    True
--    (2.22 secs, 1,429,071,808 bytes)
--    λ> sumaAnteriores3 (3:[3*2^n | n <- [0..60000]])
--    True
--    (4.46 secs, 2,411,893,032 bytes)
--    λ> sumaAnteriores4 (3:[3*2^n | n <- [0..60000]])
--    True
--    (4.31 secs, 2,360,493,480 bytes)
--    λ> sumaAnteriores5 (3:[3*2^n | n <- [0..60000]])
--    True
--    (2.30 secs, 1,424,271,736 bytes)