Algoritmo divide y vencerás
La técnica divide y vencerás consta de los siguientes pasos:
- Dividir el problema en subproblemas menores.
- Resolver por separado cada uno de los subproblemas:
- si los subproblemas son complejos, usar la misma técnica recursivamente;
- si son simples, resolverlos directamente.
- Combinar todas las soluciones de los subproblemas en una solución simple.
Definir la función
divideVenceras :: (p -> Bool) -> (p -> s) -> (p -> [p]) -> (p -> [s] -> s) -> p -> s
tal que divideVenceras ind resuelve divide combina pbInicial
resuelve el problema pbInicial
mediante la técnica de divide y vencerás, donde
-
ind pb
se verifica si el problemapb
es indivisible -
resuelve pb
es la solución del problema indivisiblepb
-
divide pb
es la lista de subproblemas depb
-
combina pb ss
es la combinación de las solucionesss
de los subproblemas del problemapb
. -
pbInicial
es el problema inicial
Usando la función DivideVenceras, definir las funciones
ordenaPorMezcla :: Ord a => [a] -> [a] ordenaRapida :: Ord a => [a] -> [a]
tales que
-
ordenaPorMezcla xs
es la lista obtenida ordenandoxs
por el procedimiento de ordenación por mezcla. Por ejemplo,
λ> ordenaPorMezcla [3,1,4,1,5,9,2,8] [1,1,2,3,4,5,8,9]
-
ordenaRapida xs
es la lista obtenida ordenandoxs
por el procedimiento de ordenación rápida. Por ejemplo,
λ> ordenaRapida [3,1,4,1,5,9,2,8] [1,1,2,3,4,5,8,9]
Soluciones
A continuación se muestran las soluciones en Haskell y las soluciones en Python.
Soluciones en Haskell
module DivideVenceras (divideVenceras) where import Test.Hspec (Spec, hspec, it, shouldBe) divideVenceras :: (p -> Bool) -> (p -> s) -> (p -> [p]) -> (p -> [s] -> s) -> p -> s divideVenceras ind resuelve divide combina = dv' where dv' pb | ind pb = resuelve pb | otherwise = combina pb [dv' sp | sp <- divide pb] ordenaPorMezcla :: Ord a => [a] -> [a] ordenaPorMezcla = divideVenceras ind id divide combina where ind xs = length xs <= 1 divide xs = [take n xs, drop n xs] where n = length xs `div` 2 combina _ [l1,l2] = mezcla l1 l2 -- (mezcla xs ys) es la lista obtenida mezclando xs e ys. Por ejemplo, -- mezcla [1,3] [2,4,6] == [1,2,3,4,6] mezcla :: Ord a => [a] -> [a] -> [a] mezcla [] b = b mezcla a [] = a mezcla a@(x:xs) b@(y:ys) | x <= y = x : mezcla xs b | otherwise = y : mezcla a ys ordenaRapida :: Ord a => [a] -> [a] ordenaRapida = divideVenceras ind id divide combina where ind xs = length xs <= 1 divide (x:xs) = [[ y | y <- xs, y <= x], [ y | y <- xs, y > x]] combina (x:_) [l1,l2] = l1 ++ [x] ++ l2 -- Verificación -- ============ verifica :: IO () verifica = hspec spec spec :: Spec spec = do it "e1" $ ordenaPorMezcla [3,1,4,1,5,9,2,8] `shouldBe` [1,1,2,3,4,5,8,9] it "e2" $ ordenaRapida [3,1,4,1,5,9,2,8] `shouldBe` [1,1,2,3,4,5,8,9] -- La verificación es -- λ> verifica -- -- e1 -- e2 -- -- Finished in 0.0004 seconds -- 2 examples, 0 failures
Soluciones en Python
from typing import Callable, TypeVar P = TypeVar('P') S = TypeVar('S') def divideVenceras(ind: Callable[[P], bool], resuelve: Callable[[P], S], divide: Callable[[P], list[P]], combina: Callable[[P, list[S]], S], p: P) -> S: def dv(pb: P) -> S: if ind(pb): return resuelve(pb) return combina(pb, [dv(sp) for sp in divide(pb)]) return dv(p) def ordenaPorMezcla(xs: list[int]) -> list[int]: def ind(xs: list[int]) -> bool: return len(xs) <= 1 def divide(xs: list[int]) -> list[list[int]]: n = len(xs) // 2 return [xs[:n], xs[n:]] def combina(_: list[int], xs: list[list[int]]) -> list[int]: return mezcla(xs[0], xs[1]) return divideVenceras(ind, lambda x: x, divide, combina, xs) # (mezcla xs ys) es la lista obtenida mezclando xs e ys. Por ejemplo, # mezcla([1,3], [2,4,6]) == [1,2,3,4,6] def mezcla(a: list[int], b: list[int]) -> list[int]: if not a: return b if not b: return a if a[0] <= b[0]: return [a[0]] + mezcla(a[1:], b) return [b[0]] + mezcla(a, b[1:]) def ordenaRapida(xs: list[int]) -> list[int]: def ind(xs: list[int]) -> bool: return len(xs) <= 1 def divide(xs: list[int]) -> list[list[int]]: x, *xs = xs return [[y for y in xs if y <= x], [y for y in xs if y > x]] def combina(xs: list[int], ys: list[list[int]]) -> list[int]: x = xs[0] return ys[0] + [x] + ys[1] return divideVenceras(ind, lambda x: x, divide, combina, xs) # Verificación # ============ def test_divideVenceras() -> None: assert ordenaPorMezcla([3,1,4,1,5,9,2,8]) == [1,1,2,3,4,5,8,9] assert ordenaRapida([3,1,4,1,5,9,2,8]) == [1,1,2,3,4,5,8,9] print("Verificado") # La verificación es # >>> test_divideVenceras() # Verificado