Ir al contenido principal

Posiciones de las diagonales principales

Las posiciones de una matriz con 3 filas y 4 columnas son

   (1,1) (1,2) (1,3) (1,4)
   (2,1) (2,2) (2,3) (2,4)
   (3,1) (3,2) (3,3) (3,4)

La posiciones de sus 6 diagonales principales son

  [(3,1)]
  [(2,1),(3,2)]
  [(1,1),(2,2),(3,3)]
  [(1,2),(2,3),(3,4)]
  [(1,3),(2,4)]
  [(1,4)]

Definir la función

   posicionesDiagonalesPrincipales :: Int -> Int -> [[(Int, Int)]]

tal que posicionesdiagonalesprincipales m n es la lista de las posiciones de las diagonales principales de una matriz con m filas y n columnas. Por ejemplo,

  λ> mapM_ print (posicionesDiagonalesPrincipales 3 4)
  [(3,1)]
  [(2,1),(3,2)]
  [(1,1),(2,2),(3,3)]
  [(1,2),(2,3),(3,4)]
  [(1,3),(2,4)]
  [(1,4)]
  λ> mapM_ print (posicionesDiagonalesPrincipales 4 4)
  [(4,1)]
  [(3,1),(4,2)]
  [(2,1),(3,2),(4,3)]
  [(1,1),(2,2),(3,3),(4,4)]
  [(1,2),(2,3),(3,4)]
  [(1,3),(2,4)]
  [(1,4)]
  λ> mapM_ print (posicionesDiagonalesPrincipales 4 3)
  [(4,1)]
  [(3,1),(4,2)]
  [(2,1),(3,2),(4,3)]
  [(1,1),(2,2),(3,3)]
  [(1,2),(2,3)]
  [(1,3)]

1. Soluciones en Haskell

import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

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

posicionesDiagonalesPrincipales1 :: Int -> Int -> [[(Int, Int)]]
posicionesDiagonalesPrincipales1 m n =
  [extension ij | ij <- iniciales]
  where iniciales = [(i,1) | i <- [m,m-1..2]] ++ [(1,j) | j <- [1..n]]
        extension (i,j) = [(i+k,j+k) | k <- [0..min (m-i) (n-j)]]

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

posicionesDiagonalesPrincipales2 :: Int -> Int -> [[(Int, Int)]]
posicionesDiagonalesPrincipales2 m n =
  [zip [i..m] [1..n] | i <- [m,m-1..1]] ++
  [zip [1..m] [j..n] | j <- [2..n]]

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

verifica :: IO ()
verifica = hspec spec

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

spec :: Spec
spec = do
  describe "def. 1" $ specG posicionesDiagonalesPrincipales1
  describe "def. 2" $ specG posicionesDiagonalesPrincipales2

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

-- Equivalencia de las definiciones
-- ================================

-- La propiedad es
prop_posicionesDiagonalesPrincipales2 :: Positive Int -> Positive Int -> Bool
prop_posicionesDiagonalesPrincipales2 (Positive m) (Positive n) =
  posicionesDiagonalesPrincipales1 m n ==
  posicionesDiagonalesPrincipales2 m n

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

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

-- La comparación es
--   λ> length (posicionesDiagonalesPrincipales1 (10^7) (10^6))
--   10999999
--   (6.14 secs, 3,984,469,440 bytes)
--   λ> length (posicionesDiagonalesPrincipales2 (10^7) (10^6))
--   10999999
--   (3.07 secs, 2,840,469,440 bytes)

2. Soluciones en Python

from timeit import Timer, default_timer

from hypothesis import given
from hypothesis import strategies as st

# 1ª solución
# ===========

matriz = list[list[tuple[int, int]]]

def posicionesDiagonalesPrincipales1(m: int, n: int) -> matriz:
    def iniciales() -> list[tuple[int, int]]:
        return [(i,1) for i in range(m,1,-1)] + [(1,j) for j in range(1, n+1)]
    def extension(p: tuple[int, int]) -> list[tuple[int, int]]:
        (i,j) = p
        return [(i+k,j+k) for k in range(0, 1+min(m-i, n-j))]
    return [extension(ij) for ij in iniciales()]

# 2ª solución
# ===========

def posicionesDiagonalesPrincipales2(m: int, n: int) -> matriz:
    return [list(zip(range(i,m+1), range(1,n+1))) for i in range(m,0,-1)] + \
           [list(zip(range(1,m+1), range(j,n+1))) for j in range(2,n+1)]

# Verificación
# ============

def test_posicionesDiagonalesPrincipales() -> None:
    for posicionesDiagonalesPrincipales in [posicionesDiagonalesPrincipales1,
                                            posicionesDiagonalesPrincipales2]:
        assert posicionesDiagonalesPrincipales(3, 4) == \
            [[(3,1)],
             [(2,1),(3,2)],
             [(1,1),(2,2),(3,3)],
             [(1,2),(2,3),(3,4)],
             [(1,3),(2,4)],
             [(1,4)]]
        assert posicionesDiagonalesPrincipales(4, 4) == \
            [[(4,1)],
             [(3,1),(4,2)],
             [(2,1),(3,2),(4,3)],
             [(1,1),(2,2),(3,3),(4,4)],
             [(1,2),(2,3),(3,4)],
             [(1,3),(2,4)],
             [(1,4)]]
        assert posicionesDiagonalesPrincipales(4, 3) == \
            [[(4,1)],
             [(3,1),(4,2)],
             [(2,1),(3,2),(4,3)],
             [(1,1),(2,2),(3,3)],
             [(1,2),(2,3)],
             [(1,3)]]
    print("Verificado")

# La verificación es
#    >>> test_posicionesDiagonalesPrincipales()
#    Verificado

# Equivalencia de las definiciones
# ================================

# La propiedad es
@given(st.integers(min_value=1, max_value=100),
       st.integers(min_value=1, max_value=100))
def test_posicionesDiagonalesPrincipales_equiv(m: int, n: int) -> None:
    assert posicionesDiagonalesPrincipales1(m, n) == \
           posicionesDiagonalesPrincipales2(m, n)

# La comprobación es
#    >>> test_posicionesDiagonalesPrincipales_equiv()
#    >>>

# Comparación de eficiencia
# =========================

def tiempo(e: str) -> None:
    """Tiempo (en segundos) de evaluar la expresión e."""
    t = Timer(e, "", default_timer, globals()).timeit(1)
    print(f"{t:0.2f} segundos")

# La comparación es
#    >>> tiempo('posicionesDiagonalesPrincipales1(10**4, 2*10**3)')
#    3.32 segundos
#    >>> tiempo('posicionesDiagonalesPrincipales2(10**4, 2*10**3)')
#    2.16 segundos