Ir al contenido principal

Mínimos locales


Un mínimo local de una lista es un elemento de la lista que es menor que su predecesor y que su sucesor en la lista. Por ejemplo, 1 es un mínimo local de [3,2,1,3,7,7,1,0,2] ya que es menor que 2 (su predecesor) y que 3 (su sucesor).

Definir la función

minimosLocales :: Ord a => [a] -> [a]

tal que (minimosLocales xs) es la lista de los mínimos locales de la lista xs. Por ejemplo,

minimosLocales [3,2,1,3,7,7,9,6,8]  ==  [1,6]
minimosLocales [1..100]             ==  []
minimosLocales "mqexvzat"           ==  "eva"

Soluciones

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

-- 1ª solución
-- ===========
minimosLocales1 :: Ord a => [a] -> [a]
minimosLocales1 (x:y:z:xs)
  | y < x && y < z = y : minimosLocales1 (z:xs)
  | otherwise      = minimosLocales1 (y:z:xs)
minimosLocales1 _  = []

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

minimosLocales2 :: Ord a => [a] -> [a]
minimosLocales2 xs =
  [y | (x,y,z) <- ternas xs, y < x, y < z]

-- (ternas xs) es la lista de las ternas de xs. Por ejemplo,
--    λ> ternas [3,2,1,3,7,7,9,6,8]
--    [(3,2,1),(2,1,3),(1,3,7),(3,7,7),(7,7,9),(7,9,6),(9,6,8)]
ternas :: [a] -> [(a,a,a)]
ternas (x:y:z:ts) = (x,y,z) : ternas (y:z:ts)
ternas _          = []

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

minimosLocales3 :: Ord a => [a] -> [a]
minimosLocales3 xs =
  [y | (x,y,z) <- ternas2 xs, y < x, y < z]

ternas2 :: [a] -> [(a,a,a)]
ternas2 xs =
  zip3 xs (tail xs) (drop 2 xs)

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

minimosLocales4 :: Ord a => [a] -> [a]
minimosLocales4 xs =
  [y | (x,y,z) <- zip3 xs (tail xs) (drop 2 xs), y < x, y < z]

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

minimosLocales5 :: Ord a => [a] -> [a]
minimosLocales5 xs =
  concatMap (\(x, y, z) -> [y | y < x, y < z])
            (zip3 xs (tail xs) (drop 2 xs))

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

verifica :: IO ()
verifica = hspec spec

specG :: ([Int] -> [Int]) -> Spec
specG minimosLocales = do
  it "e1" $
    minimosLocales [3,2,1,3,7,7,9,6,8]  `shouldBe`  [1,6]
  it "e2" $
    minimosLocales [1,2,3]              `shouldBe`  []

spec :: Spec
spec = do
  describe "def. 1" $ specG minimosLocales1
  describe "def. 2" $ specG minimosLocales2
  describe "def. 3" $ specG minimosLocales3
  describe "def. 4" $ specG minimosLocales4
  describe "def. 5" $ specG minimosLocales5

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

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

-- La propiedad es
prop_equivalencia :: [Int] -> Bool
prop_equivalencia xs =
  all (== minimosLocales1 xs)
      [minimosLocales2 xs,
       minimosLocales3 xs,
       minimosLocales4 xs,
       minimosLocales5 xs]

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

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

-- La comparación es
--    λ> length (minimosLocales1 (take (5*10^6) (cycle [2,1,3])))
--    1666666
--    (1.99 secs, 1,587,267,920 bytes)
--    λ> length (minimosLocales2 (take (5*10^6) (cycle [2,1,3])))
--    1666666
--    (2.68 secs, 2,507,267,648 bytes)
--    λ> length (minimosLocales3 (take (5*10^6) (cycle [2,1,3])))
--    1666666
--    (1.66 secs, 1,347,268,160 bytes)
--    λ> length (minimosLocales4 (take (5*10^6) (cycle [2,1,3])))
--    1666666
--    (1.64 secs, 1,347,268,248 bytes)
--    λ> length (minimosLocales5 (take (5*10^6) (cycle [2,1,3])))
--    1666666
--    (1.59 secs, 1,920,601,368 bytes)