Ir al contenido principal

Máxima suma de los segmentos


Un segmento de una lista xs es una sublista de xs formada por elementos consecutivos en la lista. El problema de la máxima suma de segmentos consiste en dada una lista de números enteros calcular el máximo de las sumas de todos los segmentos de la lista. Por ejemplo, para la lista [-1,2,-3,5,-2,1,3,-2,-2,-3,6] la máxima suma de segmentos es 7 que es la suma del segmento [5,-2,1,3] y para la lista [-1,-2,-3] es 0 que es la suma de la lista vacía.

Definir la función

mss :: [Integer] -> Integer

tal que (mss xs) es la máxima suma de los segmentos de xs. Por ejemplo,

mss [-1,2,-3,5,-2,1,3,-2,-2,-3,6]  ==  7
mss [-1,-2,-3]                     ==  0
mss [1..500]                       ==  125250
mss [1..1000]                      ==  500500
mss [-500..3]                      ==  6
mss [-1000..3]                     ==  6
mss [1..10^2]                      ==  5050
mss [1..10^3]                      ==  500500
mss [1..10^4]                      ==  50005000
mss [1..10^5]                      ==  5000050000
mss [1..10^6]                      ==  500000500000
mss [1..10^7]                      ==  50000005000000

Soluciones

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

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

mss1 :: [Integer] -> Integer
mss1 = maximum . map sum . segmentos

-- (segmentos xs) es la lista de los segmentos de xs. Por ejemplo,
--    λ> segmentos "abc"
--    ["","a","ab","abc","","b","bc","","c",""]
segmentos :: [a] -> [[a]]
segmentos = concatMap inits . tails

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

mss2 :: [Integer] -> Integer
mss2 = maximum . map sum . concatMap tails . inits

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

mss3 :: [Integer] -> Integer
mss3 = maximum . map (maximum . scanl (+) 0) . tails

-- 4ª soución
-- ==========

mss4 :: [Integer] -> Integer
mss4  = fst . foldr (\x (b,a) -> (max (a+x) b, max 0 (a+x))) (0,0)

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

mss5 :: [Integer] -> Integer
mss5 = maximum . scanl (\a x -> max 0 a + x) 0

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

verifica :: IO ()
verifica = hspec spec

specG :: ([Integer] -> Integer) -> Spec
specG mss = do
  it "e1" $
    mss [-1,2,-3,5,-2,1,3,-2,-2,-3,6]  `shouldBe`  7
  it "e2" $
    mss [-1,-2,-3]                     `shouldBe`  0

spec :: Spec
spec = do
  describe "def. 1" $ specG mss1
  describe "def. 2" $ specG mss2
  describe "def. 3" $ specG mss3
  describe "def. 4" $ specG mss4
  describe "def. 5" $ specG mss5

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

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

-- La propiedad es
prop_equivalencia :: [Integer] -> Bool
prop_equivalencia xs =
  all (== mss1 xs)
      [mss2 xs,
       mss3 xs,
       mss4 xs,
       mss5 xs]

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

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

-- La comparación es
--    λ> :set +s
--    λ> mss1 [1..10^3]
--    500500
--    (6.30 secs, 10,101,462,472 bytes)
--    λ> mss2 [1..10^3]
--    500500
--    (1.47 secs, 2,785,948,600 bytes)
--    λ> mss3 [1..10^3]
--    500500
--    (0.05 secs, 60,972,432 bytes)
--    λ> mss4 [1..10^3]
--    500500
--    (0.01 secs, 1,162,816 bytes)
--    λ> mss5 [1..10^3]
--    500500
--    (0.01 secs, 847,912 bytes)
--
--    λ> mss3 [1..2*10^4]
--    200010000
--    (6.63 secs, 24,008,043,152 bytes)
--    λ> mss4 [1..2*10^4]
--    200010000
--    (0.05 secs, 13,207,656 bytes)
--    λ> mss5 [1..2*10^4]
--    200010000
--    (0.04 secs, 5,562,912 bytes)
--
--    λ> mss4 [1..2*10^6]
--    2000001000000
--    (3.44 secs, 1,268,405,872 bytes)
--    λ> mss5 [1..2*10^6]
--    2000001000000
--    (0.64 secs, 496,605,992 bytes)