Ir al contenido principal

Número de inversiones


Se dice que en una sucesión de números x(1), x(2), ..., x(n) hay una inversión cuando existe un par de números x(i) > x(j), siendo i < j. Por ejemplo, en la permutación 2, 1, 4, 3 hay dos inversiones (2 antes que 1 y 4 antes que 3) y en la permutación 4, 3, 1, 2 hay cinco inversiones (4 antes 3, 4 antes 1, 4 antes 2, 3 antes 1, 3 antes 2).

Definir la función

numeroInversiones :: Ord a => [a] -> Int

tal que (numeroInversiones xs) es el número de inversiones de xs. Por ejemplo,

numeroInversiones [2,1,4,3]  ==  2
numeroInversiones [4,3,1,2]  ==  5

Soluciones

import Test.QuickCheck (quickCheck)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Data.Array ((!), listArray)

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

numeroInversiones1 :: Ord a => [a] -> Int
numeroInversiones1 = length . indicesInversiones

-- (indicesInversiones xs) es la lista de los índices de las inversiones
-- de xs. Por ejemplo,
--    indicesInversiones [2,1,4,3]  ==  [(0,1),(2,3)]
--    indicesInversiones [4,3,1,2]  ==  [(0,1),(0,2),(0,3),(1,2),(1,3)]
indicesInversiones :: Ord a => [a] -> [(Int,Int)]
indicesInversiones xs = [(i,j) | i <- [0..n-2],
                                 j <- [i+1..n-1],
                                 xs!!i > xs!!j]
  where n = length xs

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

numeroInversiones2 :: Ord a => [a] -> Int
numeroInversiones2 = length . indicesInversiones2

indicesInversiones2 :: Ord a => [a] -> [(Int,Int)]
indicesInversiones2 xs = [(i,j) | i <- [0..n-2],
                                  j <- [i+1..n-1],
                                  v!i > v!j]
  where n = length xs
        v = listArray (0,n-1) xs

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

numeroInversiones3 :: Ord a => [a] -> Int
numeroInversiones3 = length . inversiones

-- (inversiones xs) es la lista de las inversiones  de xs. Por ejemplo,
--    Inversiones [2,1,4,3]  ==  [(2,1),(4,3)]
--    Inversiones [4,3,1,2]  ==  [(4,3),(4,1),(4,2),(3,1),(3,2)]
inversiones :: Ord a => [a] -> [(a,a)]
inversiones []     = []
inversiones (x:xs) = [(x,y) | y <- xs, y < x] ++ inversiones xs

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

numeroInversiones4 :: Ord a => [a] -> Int
numeroInversiones4 []     = 0
numeroInversiones4 (x:xs) = length (filter (x>) xs) + numeroInversiones4 xs

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

numeroInversiones5 :: Ord a => [a] -> Int
numeroInversiones5 xs = snd (ordenadaConInversiones xs)

ordenadaConInversiones :: Ord a => [a] -> ([a], Int)
ordenadaConInversiones []  = ([], 0)
ordenadaConInversiones [x] = ([x], 0)
ordenadaConInversiones xs  =
  (mezcla, izqInversiones + dchaInversiones + mezclaInversiones)
  where
  (izq, dcha) = splitAt (length xs `div` 2) xs
  (izqOrdenada, izqInversiones)  = ordenadaConInversiones izq
  (dchaOrdenada, dchaInversiones) = ordenadaConInversiones dcha
  (mezcla, mezclaInversiones) = mezclaYcuenta izqOrdenada dchaOrdenada

mezclaYcuenta :: Ord a => [a] -> [a] -> ([a], Int)
mezclaYcuenta [] ys = (ys, 0)
mezclaYcuenta xs [] = (xs, 0)
mezclaYcuenta (x:xs) (y:ys)
  | x <= y = let (zs, n) = mezclaYcuenta xs (y:ys) in
      (x : zs, n)
  | otherwise = let (zs, n) = mezclaYcuenta (x:xs) ys in
      (y : zs, length (x:xs) + n)

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

verifica :: IO ()
verifica = hspec spec

specG :: ([Int] -> Int) -> Spec
specG numeroInversiones = do
  it "e1" $
    numeroInversiones [2,1,4,3]  `shouldBe`  2
  it "e2" $
    numeroInversiones [4,3,1,2]  `shouldBe`  5

spec :: Spec
spec = do
  describe "def. 1" $ specG numeroInversiones1
  describe "def. 2" $ specG numeroInversiones2
  describe "def. 3" $ specG numeroInversiones3
  describe "def. 4" $ specG numeroInversiones4
  describe "def. 5" $ specG numeroInversiones5

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

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

-- La propiedad es
prop_numeroInversiones :: [Int] -> Bool
prop_numeroInversiones xs =
  all (== numeroInversiones1 xs)
      [numeroInversiones2 xs,
       numeroInversiones3 xs,
       numeroInversiones4 xs,
       numeroInversiones5 xs]

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

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

-- La comparación es
--    λ> numeroInversiones1 [1200,1199..1]
--    719400
--    (2.15 secs, 260,102,328 bytes)
--    λ> numeroInversiones2 [1200,1199..1]
--    719400
--    (0.36 secs, 294,624,048 bytes)
--    λ> numeroInversiones3 [1200,1199..1]
--    719400
--    (0.21 secs, 150,647,848 bytes)
--    λ> numeroInversiones4 [1200,1199..1]
--    719400
--    (0.06 secs, 41,504,368 bytes)
--    λ> numeroInversiones5 [1200,1199..1]
--    719400
--    (0.06 secs, 6,825,296 bytes)
--    λ> numeroInversiones3 [3000,2999..1]
--    4498500
--    (1.03 secs, 937,320,624 bytes)
--    λ> numeroInversiones4 [3000,2999..1]
--    4498500
--    (0.40 secs, 254,111,416 bytes)
--    λ> numeroInversiones5 [3000,2999..1]
--    4498500
--    (0.08 secs, 17,849,880 bytes)