Decidir si existe un subconjunto con suma dada
Sea S un conjunto finito de números naturales y m un número natural. El problema consiste en determinar si existe un subconjunto de S cuya suma es m. Por ejemplo, si S = [3,34,4,12,5,2] y m = 9, existe un subconjunto de S, [4,5], cuya suma es 9. En cambio, no hay ningún subconjunto de S que sume 13.
Definir la función
existeSubSuma :: [Int] -> Int -> Bool
tal que (existeSubSuma xs m) se verifica si existe algún subconjunto de xs que sume m. Por ejemplo,
existeSubSuma [3,34,4,12,5,2] 9 == True existeSubSuma [3,34,4,12,5,2] 13 == False existeSubSuma ([3,34,4,12,5,2]++[20..400]) 13 == False existeSubSuma ([3,34,4,12,5,2]++[20..400]) 654 == True existeSubSuma [1..200] (sum [1..200]) == True
Soluciones
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 = not (null (filter (\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 || existeSubSuma2 xs (n-x) -- 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) || q ! (i-1,j-v!i) -- 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 = False | otherwise = aux ys n || aux ys (n-y) -- 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 = False | otherwise = q ! (i-1,j) || q ! (i-1,j-v!i) -- 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 -- (1.97 secs, 3,624,481,984 bytes) -- λ> let xs = [1..22] in existeSubSuma2 xs (sum xs) -- True -- (2.51 secs, 1,275,670,128 bytes) -- λ> let xs = [1..22] in existeSubSuma3 xs (sum xs) -- True -- (0.02 secs, 6,572,608 bytes) -- λ> let xs = [1..22] in existeSubSuma4 xs (sum xs) -- True -- (3.97 secs, 2,114,534,176 bytes) -- λ> let xs = [1..22] in existeSubSuma5 xs (sum xs) -- True -- (0.01 secs, 5,341,048 bytes) -- -- λ> let xs = [1..200] in existeSubSuma3 xs (sum xs) -- True -- (6.51 secs, 4,751,888,416 bytes) -- λ> let xs = [1..200] in existeSubSuma5 xs (sum xs) -- True -- (4.46 secs, 3,369,603,064 bytes)