import Data.List (subsequences, sort)
import Data.Array (Array, array, listArray, (!))
-- 1ª definición (Calculando todos los subconjuntos)
-- =================================================
existeSubSuma1 :: [Int] -> Int -> Bool
existeSubSuma1 xs n =
any (\ys -> sum ys == n) (subsequences xs)
-- 2ª definición (por recursión)
-- =============================
existeSubSuma2 :: [Int] -> Int -> Bool
existeSubSuma2 _ 0 = True
existeSubSuma2 [] _ = False
existeSubSuma2 (x:xs) n
| n < x = existeSubSuma2 xs n
| otherwise = existeSubSuma2 xs (n-x) || existeSubSuma2 xs n
-- 3ª definición (por programación dinámica)
-- =========================================
existeSubSuma3 :: [Int] -> Int -> Bool
existeSubSuma3 xs n =
matrizExisteSubSuma3 xs n ! (length xs,n)
-- (matrizExisteSubSuma3 xs m) es la matriz q tal que q(i,j) se verifica
-- si existe algún subconjunto de (take i xs) que sume j. Por ejemplo,
-- λ> elems (matrizExisteSubSuma3 [1,3,5] 9)
-- [True,False,False,False,False,False,False,False,False,False,
-- True,True, False,False,False,False,False,False,False,False,
-- True,True, False,True, True, False,False,False,False,False,
-- True,True, False,True, True, True, True, False,True, True]
-- Con las cabeceras,
-- 0 1 2 3 4 5 6 7 8 9
-- [] [True,False,False,False,False,False,False,False,False,False,
-- [1] True,True, False,False,False,False,False,False,False,False,
-- [1,3] True,True, False,True, True, False,False,False,False,False,
-- [1,3,5] True,True, False,True, True, True, True, False,True, True]
matrizExisteSubSuma3 :: [Int] -> Int -> Array (Int,Int) Bool
matrizExisteSubSuma3 xs n = q
where m = length xs
v = listArray (1,m) xs
q = array ((0,0),(m,n)) [((i,j), f i j) | i <- [0..m],
j <- [0..n]]
f _ 0 = True
f 0 _ = False
f i j | j < v ! i = q ! (i-1,j)
| otherwise = q ! (i-1,j-v!i) || q ! (i-1,j)
-- 4ª definición (ordenando y por recursión)
-- =========================================
existeSubSuma4 :: [Int] -> Int -> Bool
existeSubSuma4 xs = aux (sort xs)
where aux _ 0 = True
aux [] _ = False
aux (y:ys) n
| y <= n = aux ys (n-y) || aux ys n
| otherwise = False
-- 5ª definición (ordenando y dinámica)
-- ====================================
existeSubSuma5 :: [Int] -> Int -> Bool
existeSubSuma5 xs n =
matrizExisteSubSuma5 (reverse (sort xs)) n ! (length xs,n)
matrizExisteSubSuma5 :: [Int] -> Int -> Array (Int,Int) Bool
matrizExisteSubSuma5 xs n = q
where m = length xs
v = listArray (1,m) xs
q = array ((0,0),(m,n)) [((i,j), f i j) | i <- [0..m],
j <- [0..n]]
f _ 0 = True
f 0 _ = False
f i j | v ! i <= j = q ! (i-1,j-v!i) || q ! (i-1,j)
| otherwise = False
-- Equivalencia
-- ============
prop_existeSubSuma :: [Int] -> Int -> Bool
prop_existeSubSuma xs n =
all (== existeSubSuma2 ys m) [ existeSubSuma3 ys m
, existeSubSuma4 ys m
, existeSubSuma5 ys m ]
where ys = map abs xs
m = abs n
-- La comprobación es
-- λ> quickCheck prop_existeSubSuma
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia:
-- ==========================
-- λ> let xs = [1..22] in existeSubSuma1 xs (sum xs)
-- True
-- (7.76 secs, 3,892,403,928 bytes)
-- λ> let xs = [1..22] in existeSubSuma2 xs (sum xs)
-- True
-- (0.02 secs, 95,968 bytes)
-- λ> let xs = [1..22] in existeSubSuma3 xs (sum xs)
-- True
-- (0.03 secs, 6,055,200 bytes)
-- λ> let xs = [1..22] in existeSubSuma4 xs (sum xs)
-- True
-- (0.01 secs, 98,880 bytes)
-- λ> let xs = [1..22] in existeSubSuma5 xs (sum xs)
-- True
-- (0.02 secs, 2,827,560 bytes)
-- λ> let xs = [1..200] in existeSubSuma2 xs (sum xs)
-- True
-- (0.01 secs, 182,280 bytes)
-- λ> let xs = [1..200] in existeSubSuma3 xs (sum xs)
-- True
-- (8.89 secs, 1,875,071,968 bytes)
-- λ> let xs = [1..200] in existeSubSuma4 xs (sum xs)
-- True
-- (0.02 secs, 217,128 bytes)
-- λ> let xs = [1..200] in existeSubSuma5 xs (sum xs)
-- True
-- (8.66 secs, 1,875,087,976 bytes)
--
-- λ> and [existeSubSuma2 [1..20] n | n <- [1..sum [1..20]]]
-- True
-- (2.82 secs, 323,372,512 bytes)
-- λ> and [existeSubSuma3 [1..20] n | n <- [1..sum [1..20]]]
-- True
-- (0.65 secs, 221,806,376 bytes)
-- λ> and [existeSubSuma4 [1..20] n | n <- [1..sum [1..20]]]
-- True
-- (4.12 secs, 535,153,152 bytes)
-- λ> and [existeSubSuma5 [1..20] n | n <- [1..sum [1..20]]]
-- True
-- (0.73 secs, 238,579,696 bytes) |