Ir al contenido principal

Ordenación por el máximo


Definir la función

ordenadosPorMaximo :: Ord a => [[a]] -> [[a]]

tal que (ordenadosPorMaximo xss) es la lista de los elementos de xss ordenada por sus máximos (se supone que los elementos de xss son listas no vacía) y cuando tiene el mismo máximo se conserva el orden original. Por ejemplo,

λ> ordenadosPorMaximo [[0,8],[9],[8,1],[6,3],[8,2],[6,1],[6,2]]
[[6,3],[6,1],[6,2],[0,8],[8,1],[8,2],[9]]
λ> ordenadosPorMaximo ["este","es","el","primero"]
["el","primero","es","este"]

Nota: Escribir las soluciones en Haskell, en Python y en Common Lisp.


Soluciones

A continuación se muestran

1. Soluciones en Haskell

module Ordenados_por_maximo where

import Data.List (sort, sortBy)
import GHC.Exts (sortWith)
import Data.Map (elems, fromList)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
import Test.QuickCheck

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

ordenadosPorMaximo1 :: Ord a => [[a]] -> [[a]]
ordenadosPorMaximo1 xss =
  map snd (sort [((maximum xs,k),xs) | (k,xs) <- zip [0..] xss])

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

ordenadosPorMaximo2 :: Ord a => [[a]] -> [[a]]
ordenadosPorMaximo2 xss =
  [xs | (_,xs) <- sort [((maximum xs,k),xs) | (k,xs) <- zip [0..] xss]]

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

ordenadosPorMaximo3 :: Ord a => [[a]] -> [[a]]
ordenadosPorMaximo3 =
  sortBy (\xs ys -> compare (maximum xs) (maximum ys))

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

ordenadosPorMaximo4 :: Ord a => [[a]] -> [[a]]
ordenadosPorMaximo4 = sortWith maximum

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

ordenadosPorMaximo5 :: Ord a => [[a]] -> [[a]]
ordenadosPorMaximo5 xss =
  elems (fromList [((maximum xs, k), xs) | (k, xs) <- zip [0..] xss])

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

verifica :: IO ()
verifica = hspec spec

specG :: ([[Int]] -> [[Int]]) -> Spec
specG ordenadosPorMaximo = do
  it "e1" $
    ordenadosPorMaximo [[0,8],[9],[8,1],[6,3],[8,2],[6,1],[6,2]] `shouldBe`
      [[6,3],[6,1],[6,2],[0,8],[8,1],[8,2],[9]]

spec :: Spec
spec = do
  describe "def. 1" $ specG ordenadosPorMaximo1
  describe "def. 2" $ specG ordenadosPorMaximo2
  describe "def. 3" $ specG ordenadosPorMaximo3
  describe "def. 4" $ specG ordenadosPorMaximo4
  describe "def. 5" $ specG ordenadosPorMaximo5

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

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

-- La propiedad es
prop_ordenadosPorMaximo :: [[Int]] -> Bool
prop_ordenadosPorMaximo xss =
  all (== ordenadosPorMaximo1 yss)
      [ordenadosPorMaximo2 yss,
       ordenadosPorMaximo3 yss,
       ordenadosPorMaximo4 yss,
       ordenadosPorMaximo5 yss]
  where yss = filter (not . null) xss

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

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

-- La comparación es
--    λ> length (ordenadosPorMaximo1 [[1..k] | k <- [1..10^4]])
--    10000
--    (6.00 secs, 8,763,714,848 bytes)
--    λ> length (ordenadosPorMaximo2 [[1..k] | k <- [1..10^4]])
--    10000
--    (6.15 secs, 8,764,177,472 bytes)
--    λ> length (ordenadosPorMaximo3 [[1..k] | k <- [1..10^4]])
--    10000
--    (8.16 secs, 13,914,503,672 bytes)
--    λ> length (ordenadosPorMaximo4 [[1..k] | k <- [1..10^4]])
--    10000
--    (7.77 secs, 13,914,183,776 bytes)
--    λ> length (ordenadosPorMaximo5 [[1..k] | k <- [1..10^4]])
--    10000
--    (6.71 secs, 3,607,840,248 bytes)

2. Soluciones en Python

from timeit import Timer, default_timer

from hypothesis import given
from hypothesis import strategies as st

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

def ordenadosPorMaximo1(xss: list[list[int]]) -> list[list[int]]:
    return [xs for _, _, xs in sorted(((max(xs), k, xs) for k, xs in enumerate(xss)))]

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

def ordenadosPorMaximo2(xss: list[list[int]]) -> list[list[int]]:
    return sorted(xss, key=max)

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

def test_ordenadosPorMaximo() -> None:
    for ordenadosPorMaximo in [ordenadosPorMaximo1, ordenadosPorMaximo2]:
        assert ordenadosPorMaximo([[0,8],[9],[8,1],[6,3],[8,2],[6,1],[6,2]]) ==\
            [[6,3],[6,1],[6,2],[0,8],[8,1],[8,2],[9]]
        print(f"Verificado {ordenadosPorMaximo.__name__}")

# La verificación es
#    >>> test_ordenadosPorMaximo()
#    Verificado ordenadosPorMaximo1
#    Verificado ordenadosPorMaximo2

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

# La propiedad es
@given(st.lists(st.lists(st.integers(), min_size=1)))
def test_ordenadosPorMaximo_equiv(xss: list[list[int]]) -> None:
    assert ordenadosPorMaximo1(xss) == ordenadosPorMaximo2(xss)

# La comprobación es
#    >>> test_ordenadosPorMaximo_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('ordenadosPorMaximo1([[i for i in range(k)] for k in range(1, 10**4)])')
#    2.87 segundos
#    >>> tiempo('ordenadosPorMaximo2([[i for i in range(k)] for k in range(1, 10**4)])')
#    2.86 segundos

2. Soluciones en Common Lisp

(ql:quickload "fiveam" :silent t)

(defpackage :ordenados-por-maximo
  (:use :cl
        :fiveam))

(in-package :ordenados-por-maximo)

;;; 1ª solución
;;; ===========

(defun ordenados-por-maximo1 (xss)
  (mapcar #'rest
          (sort (loop for xs in xss
                      for k from 0
                      collect (cons (cons (apply #'max xs) k) xs))
                #'<
                :key #'(lambda (pair) (first (first pair))))))

;;; 2ª solución
;;; ===========

;;; (maximo xs) el máximo elemento de la lista no vacía xs. Por ejemplo,
;;;    > (maximo '(3 7 2))
;;;    7
(defun maximo (xs)
  (reduce #'max xs))

(defun ordenados-por-maximo2 (xss)
  (stable-sort (copy-seq xss)
               #'(lambda (xs ys) (< (maximo xs) (maximo ys)))))

;;; 3ª solución
;;; ===========

(defun ordenados-por-maximo3 (xss)
  (stable-sort (copy-seq xss)
               #'< :key (lambda (xs) (reduce #'max xs))))

;;; 4ª solución
;;; ===========

(defun ordenados-por-maximo4 (xss)
  (let* ((con-maximos (mapcar (lambda (xs) (cons xs (maximo xs))) xss))
         (ordenados (sort con-maximos #'< :key #'cdr)))
    (mapcar #'car ordenados)))

;;; Verificación
;;; ============

(test ordenados-por-maximo
  (is (equal (ordenados-por-maximo1 '((0 8) (9) (8 1) (6 3) (8 2) (6 1) (6 2)))
             '((6 3) (6 1) (6 2) (0 8) (8 1) (8 2) (9))))
  (is (equal (ordenados-por-maximo2 '((0 8) (9) (8 1) (6 3) (8 2) (6 1) (6 2)))
             '((6 3) (6 1) (6 2) (0 8) (8 1) (8 2) (9))))
  (is (equal (ordenados-por-maximo3 '((0 8) (9) (8 1) (6 3) (8 2) (6 1) (6 2)))
             '((6 3) (6 1) (6 2) (0 8) (8 1) (8 2) (9))))
  (is (equal (ordenados-por-maximo4 '((0 8) (9) (8 1) (6 3) (8 2) (6 1) (6 2)))
             '((6 3) (6 1) (6 2) (0 8) (8 1) (8 2) (9))))
  )

(defun verifica ()
  (run 'ordenados-por-maximo))

;;; La verificación es
;;;    CL-USER> (ordenados-por-maximo::verifica)
;;;
;;;    Running test ORDENADOS-POR-MAXIMO ....

;;; Equivalencia de las definiciones
;;; ================================

;;; La propiedad es
(test ordenados-por-maximo-equiv
  (for-all ((xss (gen-list
                 :elements (gen-list
                            :elements (gen-integer)))))
    (let* ((yss (remove-if #'null xss))
           (r (ordenados-por-maximo1 yss)))
      (is (equal r (ordenados-por-maximo2 yss)))
      (is (equal r (ordenados-por-maximo2 yss)))
      (is (equal r (ordenados-por-maximo3 yss)))
      )))

(defun comprueba ()
  (run 'ordenados-por-maximo-equiv))

;;; La comprobación es
;;;    > (ordenados-por-maximo::comprueba)
;;;
;;;    Running test ORDENADOS-POR-MAXIMO-EQUIV ...
;;;    (#<IT.BESE.FIVEAM::FOR-ALL-TEST-PASSED {1003EDAF13}>)

;;; Comparación de eficiencia
;;; =========================

;;; (ejemplo n) es una lista con n elementos tal que su k-ésimo
;;; elementos son los números del 1 al k. Por ejemplo,
;;;    > (ejemplo 5)
;;;    ((1) (1 2) (1 2 3) (1 2 3 4) (1 2 3 4 5))
(defun ejemplo (n)
  (loop for k from 1 to n
        collect (loop for i from 1 to k
                      collect i)))

;;; La comparación es
;;;    > (time (length (ordenados-por-maximo1 (ejemplo 6000))))
;;;    0.807 seconds of real time
;;;    > (time (length (ordenados-por-maximo2 (ejemplo 6000))))
;;;    0.739 seconds of real time
;;;    > (time (length (ordenados-por-maximo3 (ejemplo 6000))))
;;;    0.821 seconds of real time
;;;    > (time (length (ordenados-por-maximo4 (ejemplo 6000))))
;;;    0.738 seconds of real time