Ir al contenido principal

Diagonales secundarias de una matriz


Definir la función

diagonalesSecundarias :: Array (Int,Int) a -> [[a]]

tal que (diagonalesSecundarias p) es la lista de las diagonales secundarias de p. Por ejemplo, para la matriz

1  2  3  4
5  6  7  8
9 10 11 12

la lista de sus diagonales secundarias es

[[1],[2,5],[3,6,9],[4,7,10],[8,11],[12]]

En Haskell,

λ> diagonalesSecundarias (listArray ((1,1),(3,4)) [1..12])
[[1],[2,5],[3,6,9],[4,7,10],[8,11],[12]]

Soluciones

import Data.Array (Array, (!), bounds, listArray)
import Data.List (transpose)
import Data.Maybe (catMaybes)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

type Matriz a = Array (Int,Int) a

-- 1ª solución: Basada en la propiedad de la suma de índices
-- =========================================================

diagonalesSecundarias1 :: Matriz a -> [[a]]
diagonalesSecundarias1 p =
  [[ p ! (i, s - i)
   | i <- [1..m],
     let j = s - i,
     j >= 1, j <= n ]
  | s <- [2..m+n] ]
  where
    (_, (m, n)) = bounds p

-- 2ª solución: Enfoque geométrico
-- ===============================

diagonalesSecundarias2 :: Matriz a -> [[a]]
diagonalesSecundarias2 p =
  [[p!ij1 | ij1 <- extension ij] | ij <- iniciales]
  where (_,(m,n)) = bounds p
        iniciales = [(1,j) | j <- [1..n]] ++ [(i,n) | i <- [2..m]]
        extension (i,j) = [(i+k,j-k) | k <- [0..min (j-1) (m-i)]]

-- 3ª solución: Evolución optimizada de la primera
-- ===============================================

diagonalesSecundarias3 :: Array (Int, Int) a -> [[a]]
diagonalesSecundarias3 p =
    [ [ p ! (i, s - i) | i <- [max 1 (s - n) .. min m (s - 1)] ]
    | s <- [2 .. m+n] ]
  where
    (_, (m, n)) = bounds p

-- 4ª solución: Enfoque estructural (estilo funcional puro)
-- ========================================================

diagonalesSecundarias4 :: Matriz a -> [[a]]
diagonalesSecundarias4 p = map catMaybes $ transpose filasDesplazadas
  where
    (_, (m, n)) = bounds p
    filas = [[Just (p ! (i, j)) | j <- [1..n]] | i <- [1..m]]
    filasDesplazadas = zipWith (\k fila -> replicate k Nothing ++ fila) [0..] filas

-- 5ª solución: Enfoque algebraico denso
-- =====================================

diagonalesSecundarias5 :: Matriz a -> [[a]]
diagonalesSecundarias5 v =
  [[v!(1+x+y, 1-y) | y <- [-min (n-1) x .. -max 0 (x-m+1)]]
  | x <- [0 .. m-1+n-1]]
  where (_, (m, n)) = bounds v

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

verifica :: IO ()
verifica = hspec spec

specG :: (Matriz Int -> [[Int]]) -> Spec
specG diagonalesSecundarias = do
  it "e1" $
    diagonalesSecundarias (listArray ((1,1),(3,4)) [1..12])
    `shouldBe` [[1],[2,5],[3,6,9],[4,7,10],[8,11],[12]]

spec :: Spec
spec = do
  describe "def. 1" $ specG diagonalesSecundarias1
  describe "def. 2" $ specG diagonalesSecundarias2
  describe "def. 3" $ specG diagonalesSecundarias3
  describe "def. 4" $ specG diagonalesSecundarias4
  describe "def. 5" $ specG diagonalesSecundarias5

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

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

newtype Matriz2 = M (Array (Int,Int) Int)
  deriving Show

-- Generador de matrices arbitrarias. Por ejemplo,
--    λ> generate matrizArbitraria
--    M (array ((1,1),(3,4))
--             [((1,1),18),((1,2),6), ((1,3),-23),((1,4),-13),
--              ((2,1),-2),((2,2),22),((2,3),-25),((2,4),-5),
--              ((3,1),2), ((3,2),16),((3,3),-15),((3,4),7)])
matrizArbitraria :: Gen Matriz2
matrizArbitraria = do
  m  <- chooseInt (1,10)
  n  <- chooseInt (1,10)
  xs <- vectorOf (m*n) arbitrary
  return (M (listArray ((1,1),(m,n)) xs))

-- Matriz es una subclase de Arbitrary.
instance Arbitrary Matriz2 where
  arbitrary = matrizArbitraria

-- La propiedad es
prop_equivalencia :: Matriz2 -> Bool
prop_equivalencia (M p) =
  all (== diagonalesSecundarias1 p)
      [ diagonalesSecundarias2 p
      , diagonalesSecundarias3 p
      , diagonalesSecundarias4 p
      , diagonalesSecundarias5 p
      ]

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

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

-- La comparación es
--    λ> ejemplo = listArray ((1,1),(2000,2000)) [1..]
--    λ> sum (map sum (diagonalesSecundarias1 ejemplo))
--    8000002000000
--    (3.19 secs, 2,596,283,624 bytes)
--    λ> sum (map sum (diagonalesSecundarias2 ejemplo))
--    8000002000000
--    (3.37 secs, 2,660,139,488 bytes)
--    λ> sum (map sum (diagonalesSecundarias3 ejemplo))
--    8000002000000
--    (5.67 secs, 3,617,915,784 bytes)
--    λ> sum (map sum (diagonalesSecundarias4 ejemplo))
--    8000002000000
--    (2.46 secs, 2,083,307,544 bytes)
--    λ> sum (map sum (diagonalesSecundarias5 ejemplo))
--    8000002000000
--    (3.31 secs, 3,490,252,592 bytes)