Piensa_en_Haskell_y_en_Python
Piensa_en_Haskell_y_en_Python
Se permite:
Al reutilizar o distribuir la obra, tiene que dejar bien claro los términos
de la licencia de esta obra.
Esto es un resumen del texto legal (la licencia completa). Para ver una co-
pia de esta licencia, visite http://creativecommons.org/licenses/by-nc-sa/2.
5/es/ o envie una carta a Creative Commons, 559 Nathan Abbott Way, Stan-
ford, California 94305, USA.
Índice general
3
4 Ejercicios de programación con Python
II Algorítmica 441
6. El tipo abstracto de datos de las pilas 443
6.1. El tipo abstracto de datos de las pilas . . . . . . . . . . . . . . .443
6.2. El tipo de datos de las pilas mediante listas . . . . . . . . . . .445
6.3. El tipo de datos de las pilas con librerías . . . . . . . . . . . . .453
6.4. Transformación entre pilas y listas . . . . . . . . . . . . . . . . .460
6.5. Filtrado de pilas según una propiedad . . . . . . . . . . . . . . .467
6.6. Aplicación de una función a los elementos de una pila . . . . .471
6.7. Pertenencia a una pila . . . . . . . . . . . . . . . . . . . . . . . .475
6.8. Inclusión de pilas . . . . . . . . . . . . . . . . . . . . . . . . . . .478
6.9. Reconocimiento de prefijos de pilas . . . . . . . . . . . . . . . .483
6.10. Reconocimiento de subpilas . . . . . . . . . . . . . . . . . . . . .487
6.11. Reconocimiento de ordenación de pilas . . . . . . . . . . . . . .492
6.12. Ordenación de pilas por inserción . . . . . . . . . . . . . . . . .496
6.13. Eliminación de repeticiones en una pila . . . . . . . . . . . . . .501
6.14. Máximo elemento de una pila . . . . . . . . . . . . . . . . . . . .505
IV Miscelánea 1207
16. Miscelánea 1209
16.1. Números de Pentanacci . . . . . . . . . . . . . . . . . . . . . . 1212 .
16.2. El teorema de Navidad de Fermat . . . . . . . . . . . . . . . . 1219 .
16.3. Números primos de Hilbert . . . . . . . . . . . . . . . . . . . . 1231 .
16.4. Factorizaciones de números de Hilbert . . . . . . . . . . . . . 1237 .
16.5. Sumas de dos primos . . . . . . . . . . . . . . . . . . . . . . . . 1245.
16.6. Representaciones de un número como suma de dos cuadrados 1252
16.7. La serie de Thue-Morse . . . . . . . . . . . . . . . . . . . . . . 1261 .
16.8. La sucesión de Thue-Morse . . . . . . . . . . . . . . . . . . . . 1266 .
16.9. Huecos maximales entre primos . . . . . . . . . . . . . . . . . 1275 .
16.10. La función indicatriz de Euler . . . . . . . . . . . . . . . . . . . 1282
.
16.11. Ceros finales del factorial . . . . . . . . . . . . . . . . . . . . . 1289
.
16.12. Primos cubanos . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294
.
Índice general 13
Bibliografía 1463
14 Ejercicios de programación con Python
Introducción
1
https://www.glc.us.es/ jalonso/exercitium
2
https://jaalonso.github.io/materias/PFconHaskell/temas.html
3
https://github.com/jaalonso/Exercitium
4
https://github.com/jaalonso/Exercitium-Python
5
https://docs.haskellstack.org/en/stable
6
https://python-poetry.org
7
http://mypy-lang.org
8
https://hackage.haskell.org/package/QuickCheck
9
https://hypothesis.readthedocs.io/en/latest
10
https://jaalonso.github.io/materias/PFconHaskell
15
16 Ejercicios de programación con Python
Parte I
Introducción a la programación
con Python
17
Capítulo 1
Definiciones elementales de
funciones
Contenido
1.1. Media aritmética de tres números . . . . . . . . . . . . . . . 9
1.2. Suma de monedas . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3. Volumen de la esfera . . . . . . . . . . . . . . . . . . . . . . . 11
1.4. Área de la corona circular . . . . . . . . . . . . . . . . . . . . 12
1.5. Último dígito . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.6. Máximo de tres números . . . . . . . . . . . . . . . . . . . . . 14
1.7. El primero al final . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.8. Los primeros al final . . . . . . . . . . . . . . . . . . . . . . . . 17
1.9. Rango de una lista . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.10. Reconocimiento de palindromos . . . . . . . . . . . . . . . . 19
1.11. Interior de una lista . . . . . . . . . . . . . . . . . . . . . . . . 20
1.12. Elementos finales . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.13. Segmento de una lista . . . . . . . . . . . . . . . . . . . . . . 23
1.14. Primeros y últimos elementos . . . . . . . . . . . . . . . . . . 25
1
https://jaalonso.github.io/materias/PFconHaskell/temas.html
19
20 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# media3 : (float, float, float) -> float
# tal que (media3 x y z) es la media aritmética de los números x, y y
# z. Por ejemplo,
# media3(1, 3, 8) == 4.0
# media3(-1, 0, 7) == 2.0
# media3(-3, 0, 3) == 0.0
# ---------------------------------------------------------------------
sumaMonedas :: Int -> Int -> Int -> Int -> Int -> Int
sumaMonedas a b c d e = 1*a+2*b+5*c+10*d+20*e
En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaMonedas : (int, int, int, int, int) -> int
22 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# volumenEsfera : (float) -> float
# tal que volumenEsfera(r) es el volumen de la esfera de radio r. Por
# ejemplo,
# volumenEsfera(10) == 4188.790204786391
# ---------------------------------------------------------------------
Capítulo 1. Definiciones elementales de funciones 23
En Python
# ---------------------------------------------------------------------
# Definir la función
# areaDeCoronaCircular : (float, float) -> float
# tal que areaDeCoronaCircular(r1, r2) es el área de una corona
# circular de radio interior r1 y radio exterior r2. Por ejemplo,
# areaDeCoronaCircular(1, 2) == 9.42477796076938
# areaDeCoronaCircular(2, 5) == 65.97344572538566
# areaDeCoronaCircular(3, 5) == 50.26548245743669
# ---------------------------------------------------------------------
24 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# ultimoDigito : (int) -> int
# tal que ultimoDigito(x) es el último dígito del número x. Por
# ejemplo,
# ultimoDigito(325) == 5
# ---------------------------------------------------------------------
En Python
# ---------------------------------------------------------------------
# Definir la función
# maxTres : (int, int, int) -> int
# tal que maxTres(x, y, z) es el máximo de x, y y z. Por ejemplo,
# maxTres(6, 2, 4) == 6
# maxTres(6, 7, 4) == 7
# maxTres(6, 7, 9) == 9
# ---------------------------------------------------------------------
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_rota1 :: [Int] -> Bool
prop_rota1 xs =
rota1a xs == rota1b xs
-- La comprobación es
-- λ> quickCheck prop_rota1
-- +++ OK, passed 100 tests.
Capítulo 1. Definiciones elementales de funciones 27
En Python
# ---------------------------------------------------------------------
# Definir la función
# rota1 : (List[A]) -> List[A]
# tal que rota1(xs) es la lista obtenida poniendo el primer elemento de
# xs al final de la lista. Por ejemplo,
# rota1([3, 2, 5, 7]) == [2, 5, 7, 3]
# rota1(['a', 'b', 'c']) == ['b', 'c', 'a']
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
def rota1a(xs: list[A]) -> list[A]:
if xs == []:
return []
return xs[1:] + [xs[0]]
# 2ª solución
def rota1b(xs: list[A]) -> list[A]:
if xs == []:
return []
ys = xs[1:]
ys.append(xs[0])
return ys
# 3ª solución
def rota1c(xs: list[A]) -> list[A]:
if xs == []:
return []
y, *ys = xs
return ys + [y]
# La comprobación es
# src> poetry run pytest -q el_primero_al_final.py
# 1 passed in 0.20s
En Python
# ---------------------------------------------------------------------
# Definir la función
# rota : (int, List[A]) -> List[A]
# tal que rota(n, xs) es la lista obtenida poniendo los n primeros
# elementos de xs al final de la lista. Por ejemplo,
# rota(1, [3, 2, 5, 7]) == [2, 5, 7, 3]
# rota(2, [3, 2, 5, 7]) == [5, 7, 3, 2]
# rota(3, [3, 2, 5, 7]) == [7, 3, 2, 5]
# ---------------------------------------------------------------------
A = TypeVar('A')
En Python
# ---------------------------------------------------------------------
# Definir la función
# rango : (List[int]) -> List[int]
# tal que rango(xs) es la lista formada por el menor y mayor elemento
# de xs.
# rango([3, 2, 7, 5]) == [2, 7]
# ---------------------------------------------------------------------
En Python
# ---------------------------------------------------------------------
# Definir la función
# palindromo : (List[A]) -> bool
# tal que palindromo(xs) se verifica si xs es un palíndromo; es decir,
# es lo mismo leer xs de izquierda a derecha que de derecha a
# izquierda. Por ejemplo,
# palindromo([3, 2, 5, 2, 3]) == True
# palindromo([3, 2, 5, 6, 2, 3]) == False
# ---------------------------------------------------------------------
A = TypeVar('A')
En Python
# ---------------------------------------------------------------------
# Definir la función
# interior : (list[A]) -> list[A]
# tal que interior(xs) es la lista obtenida eliminando los extremos de
# la lista xs. Por ejemplo,
# interior([2, 5, 3, 7, 3]) == [5, 3, 7]
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
def interior1(xs: list[A]) -> list[A]:
return xs[1:][:-1]
# 2ª solución
32 Ejercicios de programación con Python
# La propiedad de equivalencia es
@given(st.lists(st.integers()))
def test_interior(xs: list[int]) -> None:
assert interior1(xs) == interior2(xs)
# La comprobación es
# src> poetry run pytest -q interior_de_una_lista.py
# 1 passed in 0.21s
-- 1ª definición
finales1 :: Int -> [a] -> [a]
finales1 n xs = drop (length xs - n) xs
-- 2ª definición
finales2 :: Int -> [a] -> [a]
finales2 n xs = reverse (take n (reverse xs))
-- Comprobación de equivalencia
-- ============================
Capítulo 1. Definiciones elementales de funciones 33
-- La propiedad es
prop_finales :: Int -> [Int] -> Bool
prop_finales n xs =
finales1 n xs == finales2 n xs
-- La comprobación es
-- λ> quickCheck prop_finales
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# finales : (int, list[A]) -> list[A]
# tal que finales(n, xs) es la lista formada por los n finales
# elementos de xs. Por ejemplo,
# finales(3, [2, 5, 4, 7, 9, 6]) == [7, 9, 6]
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª definición
def finales1(n: int, xs: list[A]) -> list[A]:
if len(xs) <= n:
return xs
return xs[len(xs) - n:]
# 2ª definición
def finales2(n: int, xs: list[A]) -> list[A]:
if n == 0:
return []
return xs[-n:]
# 3ª definición
def finales3(n: int, xs: list[A]) -> list[A]:
34 Ejercicios de programación con Python
ys = list(reversed(xs))
return list(reversed(ys[:n]))
# La propiedad de equivalencia es
@given(st.integers(min_value=0), st.lists(st.integers()))
def test_equiv_finales(n: int, xs: list[int]) -> None:
assert finales1(n, xs) == finales2(n, xs) == finales3(n, xs)
# La comprobación es
# src> poetry run pytest -q elementos_finales.py
# 1 passed in 0.18s
En Python
# ---------------------------------------------------------------------
# Definir la función
# segmento : (int, int, list[A]) -> list[A]
# tal que segmento(m, n, xs) es la lista de los elementos de xs
# comprendidos entre las posiciones m y n. Por ejemplo,
# segmento(3, 4, [3, 4, 1, 2, 7, 9, 0]) == [1, 2]
Capítulo 1. Definiciones elementales de funciones 35
A = TypeVar('A')
# 1ª definición
def segmento1(m: int, n: int, xs: list[A]) -> list[A]:
ys = xs[:n]
if m == 0:
return ys
return ys[m - 1:]
# 2ª definición
def segmento2(m: int, n: int, xs: list[A]) -> list[A]:
if m == 0:
return xs[:n]
return xs[m-1:n]
# La propiedad de equivalencia es
@given(st.integers(min_value=0),
st.integers(min_value=0),
st.lists(st.integers()))
def test_equiv_segmento(m: int, n: int, xs: list[int]) -> None:
assert segmento1(m, n, xs) == segmento2(m, n, xs)
# La comprobación es
# src> poetry run pytest -q segmento_de_una_lista.py
# 1 passed in 0.19s
36 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# extremos : (int, list[A]) -> list[A]
# tal que extremos(n, xs) es la lista formada por los n primeros
# elementos de xs y los n finales elementos de xs. Por ejemplo,
# extremos(3, [2, 6, 7, 1, 2, 4, 5, 8, 9, 2, 3]) == [2, 6, 7, 9, 2, 3]
# ---------------------------------------------------------------------
A = TypeVar('A')
En Python
# ---------------------------------------------------------------------
# Definir la función
# mediano : (int, int, int) -> int
# tal que mediano(x, y, z) es el número mediano de los tres números x, y
# y z. Por ejemplo,
# mediano(3, 2, 5) == 3
# mediano(2, 4, 5) == 4
# mediano(2, 6, 5) == 5
# mediano(2, 6, 6) == 6
# ---------------------------------------------------------------------
En Python
# ---------------------------------------------------------------------
# Definir la función
# tresIguales : (int, int, int) -> bool
# tal que tresIguales(x, y, z) se verifica si los elementos x, y y z son
# iguales. Por ejemplo,
# tresIguales(4, 4, 4) == True
# tresIguales(4, 3, 4) == False
# ---------------------------------------------------------------------
# 1ª definición
def tresIguales1(x: int, y: int, z: int) -> bool:
return x == y and y == z
# 2ª definición
def tresIguales2(x: int, y: int, z: int) -> bool:
return x == y == z
Capítulo 1. Definiciones elementales de funciones 39
# La propiedad de equivalencia es
@given(st.integers(), st.integers(), st.integers())
def test_equiv_tresIguales(x: int, y: int, z: int) -> None:
assert tresIguales1(x, y, z) == tresIguales2(x, y, z)
# La comprobación es
# src> poetry run pytest -q tres_iguales.py
# 1 passed in 0.16s
En Python
# ---------------------------------------------------------------------
# Definir la función
# tresDiferentes : (int, int, int) -> bool
# tal que tresDiferentes(x, y, z) se verifica si los elementos x, y y z
# son distintos. Por ejemplo,
# tresDiferentes(3, 5, 2) == True
# tresDiferentes(3, 5, 3) == False
# ---------------------------------------------------------------------
40 Ejercicios de programación con Python
import Test.QuickCheck
-- 1ª definición
divisionSegura1 :: Double -> Double -> Double
divisionSegura1 x y =
if y == 0 then 9999 else x/y
-- 2ª definición
divisionSegura2 :: Double -> Double -> Double
divisionSegura2 _ 0 = 9999
divisionSegura2 x y = x/y
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_divisionSegura :: Double -> Double -> Bool
prop_divisionSegura x y =
divisionSegura1 x y == divisionSegura2 x y
Capítulo 1. Definiciones elementales de funciones 41
-- La comprobación es
-- λ> quickCheck prop_divisionSegura
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# divisionSegura : (float, float) -> float
# tal que divisionSegura(x, y) es x/y si y no es cero y 9999 en caso
# contrario. Por ejemplo,
# divisionSegura(7, 2) == 3.5
# divisionSegura(7, 0) == 9999.0
# ---------------------------------------------------------------------
# 1ª definición
def divisionSegura1(x: float, y: float) -> float:
if y == 0:
return 9999.0
return x/y
# 2ª definición
def divisionSegura2(x: float, y: float) -> float:
match y:
case 0:
return 9999.0
case _:
return x/y
# La propiedad de equivalencia es
@given(st.floats(allow_nan=False, allow_infinity=False),
st.floats(allow_nan=False, allow_infinity=False))
def test_equiv_divisionSegura(x: float, y: float) -> None:
assert divisionSegura1(x, y) == divisionSegura2(x, y)
# La comprobación es
42 Ejercicios de programación con Python
import Test.QuickCheck
-- 1ª solución
xor1 :: Bool -> Bool -> Bool
xor1 True True = False
xor1 True False = True
xor1 False True = True
xor1 False False = False
-- 2ª solución
Capítulo 1. Definiciones elementales de funciones 43
-- 3ª solución:
xor3 :: Bool -> Bool -> Bool
xor3 x y = (x || y) && not (x && y)
-- 4ª solución:
xor4 :: Bool -> Bool -> Bool
xor4 x y = (x && not y) || (y && not x)
-- 5ª solución:
xor5 :: Bool -> Bool -> Bool
xor5 x y = x /= y
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_xor :: Bool -> Bool -> Bool
prop_xor x y =
all (== xor1 x y)
[xor2 x y,
xor3 x y,
xor4 x y,
xor5 x y]
-- La comprobación es
-- λ> quickCheck prop_xor
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# La disyunción excluyente de dos fórmulas se verifica si una es
# verdadera y la otra es falsa. Su tabla de verdad es
# x | y | xor x y
# ------+-------+---------
# True | True | False
44 Ejercicios de programación con Python
# 1ª solución
def xor1(x: bool, y: bool) -> Any:
match x, y:
case True, True: return False
case True, False: return True
case False, True: return True
case False, False: return False
# 2ª solución
def xor2(x: bool, y: bool) -> bool:
if x:
return not y
return y
# 3ª solución
def xor3(x: bool, y: bool) -> bool:
return (x or y) and not(x and y)
# 4ª solución
def xor4(x: bool, y: bool) -> bool:
return (x and not y) or (y and not x)
Capítulo 1. Definiciones elementales de funciones 45
# 5ª solución
def xor5(x: bool, y: bool) -> bool:
return x != y
# La propiedad de equivalencia es
@given(st.booleans(), st.booleans())
def test_equiv_xor(x: bool, y: bool) -> None:
assert xor1(x, y) == xor2(x, y) == xor3(x, y) == xor4(x, y) == xor5(x, y)
# La comprobación es
# src> poetry run pytest -q disyuncion_excluyente.py
# 1 passed in 0.11s
En Python
# ---------------------------------------------------------------------
# Las dimensiones de los rectángulos puede representarse por pares; por
# ejemplo, (5,3) representa a un rectángulo de base 5 y altura 3.
#
# Definir la función
# mayorRectangulo : (tuple[float, float], tuple[float, float])
# -> tuple[float, float]
# tal que mayorRectangulo(r1, r2) es el rectángulo de mayor área entre
# r1 y r2. Por ejemplo,
# mayorRectangulo((4, 6), (3, 7)) == (4, 6)
# mayorRectangulo((4, 6), (3, 8)) == (4, 6)
# mayorRectangulo((4, 6), (3, 9)) == (3, 9)
# ---------------------------------------------------------------------
import Test.QuickCheck
-- La propiedad es
prop_intercambia :: (Int,Int) -> Bool
prop_intercambia p = intercambia (intercambia p) == p
-- La comprobación es
-- λ> quickCheck prop_intercambia
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# intercambia : (tuple[A, B]) -> tuple[B, A]
# tal que intercambia(p) es el punto obtenido intercambiando las
# coordenadas del punto p. Por ejemplo,
# intercambia((2,5)) == (5,2)
# intercambia((5,2)) == (2,5)
#
# Comprobar con Hypothesis que la función intercambia es
# idempotente; es decir, si se aplica dos veces es lo mismo que no
# aplicarla ninguna.
# ---------------------------------------------------------------------
A = TypeVar('A')
B = TypeVar('B')
48 Ejercicios de programación con Python
# La propiedad de es
@given(st.tuples(st.integers(), st.integers()))
def test_equiv_intercambia(p: tuple[int, int]) -> None:
assert intercambia(intercambia(p)) == p
# La comprobación es
# src> poetry run pytest -q intercambio_de_componentes_de_un_par.py
# 1 passed in 0.15s
import Test.QuickCheck
-- La propiedad es
prop_triangular :: (Double,Double) -> (Double,Double) -> (Double,Double)
-> Property
prop_triangular p1 p2 p3 =
all acotado [p1, p2, p3] ==>
distancia p1 p3 <= distancia p1 p2 + distancia p2 p3
where acotado (x, y) = abs x < cota && abs y < cota
cota = 2^30
-- La comprobación es
-- ghci> quickCheck prop_triangular
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# distancia : (tuple[float, float], tuple[float, float]) -> float
# tal que distancia(p1, p2) es la distancia entre los puntos p1 y
# p2. Por ejemplo,
# distancia((1, 2), (4, 6)) == 5.0
#
# Comprobar con Hypothesis que se verifica la propiedad triangular de
# la distancia; es decir, dados tres puntos p1, p2 y p3, la distancia
# de p1 a p3 es menor o igual que la suma de la distancia de p1 a p2 y
# la de p2 a p3.
# ---------------------------------------------------------------------
50 Ejercicios de programación con Python
# La propiedad es
cota = 2 ** 30
@given(st.tuples(st.integers(min_value=0, max_value=cota),
st.integers(min_value=0, max_value=cota)),
st.tuples(st.integers(min_value=0, max_value=cota),
st.integers(min_value=0, max_value=cota)),
st.tuples(st.integers(min_value=0, max_value=cota),
st.integers(min_value=0, max_value=cota)))
def test_triangular(p1: tuple[int, int],
p2: tuple[int, int],
p3: tuple[int, int]) -> None:
assert distancia(p1, p3) <= distancia(p1, p2) + distancia(p2, p3)
# La comprobación es
# src> poetry run pytest -q distancia_entre_dos_puntos.py
# 1 passed in 0.38s
import Test.QuickCheck
-- La propiedad es
prop_ciclo :: [Int] -> Bool
prop_ciclo xs = length (ciclo xs) == length xs
-- La comprobación es
-- λ> quickCheck prop_ciclo
-- +++ OK, passed 100 tests.
52 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir una función
# ciclo : (list[A]) -> list[A]
# tal que ciclo(xs) es la lista obtenida permutando cíclicamente los
# elementos de la lista xs, pasando el último elemento al principio de
# la lista. Por ejemplo,
# ciclo([2, 5, 7, 9]) == [9, 2, 5, 7]
# ciclo([]) == []
# ciclo([2]) == [2]
#
# Comprobar que la longitud es un invariante de la función ciclo; es
# decir, la longitud de (ciclo xs) es la misma que la de xs.
# ---------------------------------------------------------------------
A = TypeVar('A')
# La propiedad de es
@given(st.lists(st.integers()))
def test_equiv_ciclo(xs: list[int]) -> None:
assert len(ciclo(xs)) == len(xs)
# La comprobación es
# src> poetry run pytest -q permutacion_ciclica.py
# 1 passed in 0.39s
Capítulo 1. Definiciones elementales de funciones 53
-- 1ª definición:
numeroMayor1 :: Int -> Int -> Int
numeroMayor1 x y = 10 * max x y + min x y
-- 2ª definición:
numeroMayor2 :: Int -> Int -> Int
numeroMayor2 x y | x > y = 10*x+y
| otherwise = 10*y+x
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_numeroMayor :: Bool
prop_numeroMayor =
and [numeroMayor1 x y == numeroMayor2 x y | x <- [0..9], y <- [0..9]]
-- La comprobación es
-- λ> prop_numeroMayor
-- True
En Python
# ---------------------------------------------------------------------
# Definir la función
54 Ejercicios de programación con Python
# 1ª definición
def numeroMayor1(x: int, y: int) -> int:
return 10 * max(x, y) + min(x, y)
# 2ª definición
def numeroMayor2(x: int, y: int) -> int:
if x > y:
return 10 * x + y
return 10 * y + x
# La comprobación es
# >>> test_equiv_numeroMayor()
# True
-- numeroDeRaices 5 23 12 == 2
-- ---------------------------------------------------------------------
En Python
# ---------------------------------------------------------------------
# Definir la función
# numeroDeRaices : (float, float, float) -> float
# tal que numeroDeRaices(a, b, c) es el número de raíces reales de la
# ecuación a*x^2 + b*x + c = 0. Por ejemplo,
# numeroDeRaices(2, 0, 3) == 0
# numeroDeRaices(4, 4, 1) == 1
# numeroDeRaices(5, 23, 12) == 2
# ---------------------------------------------------------------------
import Test.QuickCheck
-- La propiedad es
prop_raices :: Double -> Double -> Double -> Property
prop_raices a b c =
a /= 0 && not (null xs) ==> sum xs ~= (-b/a) && product xs ~= (c/a)
Capítulo 1. Definiciones elementales de funciones 57
where xs = raices a b c
-- La comprobación es
-- λ> quickCheck prop_raices
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# raices : (float, float, float) -> list[float]
# tal que raices(a, b, c) es la lista de las raíces reales de la
# ecuación ax^2 + bx + c = 0. Por ejemplo,
# raices(1, 3, 2) == [-1.0,-2.0]
# raices(1, (-2), 1) == [1.0,1.0]
# raices(1, 0, 1) == []
#
# Comprobar con Hypothesis que la suma de las raíces de la ecuación
# ax^2 + bx + c = 0 (con a no nulo) es -b/a y su producto es c/a.
# ---------------------------------------------------------------------
# La propiedad es
@given(st.floats(min_value=-100, max_value=100),
st.floats(min_value=-100, max_value=100),
st.floats(min_value=-100, max_value=100))
def test_prop_raices(a: float, b: float, c: float) -> None:
assume(abs(a) > 0.1)
xs = raices(a, b, c)
assume(xs)
[x1, x2] = xs
assert casiIguales(x1 + x2, -b / a)
assert casiIguales(x1 * x2, c / a)
# La comprobación es
# src> poetry run pytest -q raices_de_la_ecuacion_de_segundo_grado.py
# 1 passed in 0.35s
En Python
# ---------------------------------------------------------------------
# La fórmula de Herón, descubierta por Herón de Alejandría, dice que el
# área de un triángulo cuyo lados miden a, b y c es la raíz cuadrada de
# s(s-a)(s-b)(s-c) donde s es el semiperímetro
# s = (a+b+c)/2
#
# Definir la función
# area : (float, float, float) -> float
# tal que area(a, b, c) es el área del triángulo de lados a, b y c. Por
# ejemplo,
# area(3, 4, 5) == 6.0
# ---------------------------------------------------------------------
import Test.QuickCheck
-- La propiedad es
prop_interseccion :: Int -> Int -> Int -> Int -> Property
prop_interseccion a1 b1 a2 b2 =
a1 <= b1 && a2 <= b2 ==>
interseccion [a1,b1] [a2,b2] == interseccion [a2,b2] [a1,b1]
-- La comprobación es
-- λ> quickCheck prop_interseccion
-- +++ OK, passed 100 tests; 263 discarded.
Capítulo 1. Definiciones elementales de funciones 61
En Python
# ---------------------------------------------------------------------
# Los intervalos cerrados se pueden representar mediante una lista de
# dos números (el primero es el extremo inferior del intervalo y el
# segundo el superior).
#
# Definir la función
# interseccion : (list[float], list[float]) -> list[float]
# tal que interseccion(i1, i2) es la intersección de los intervalos i1 e
# i2. Por ejemplo,
# interseccion([], [3, 5] == []
# interseccion([3, 5], []) == []
# interseccion([2, 4], [6, 9]) == []
# interseccion([2, 6], [6, 9]) == [6, 6]
# interseccion([2, 6], [0, 9]) == [2, 6]
# interseccion([2, 6], [0, 4]) == [2, 4]
# interseccion([4, 6], [0, 4]) == [4, 4]
# interseccion([5, 6], [0, 4]) == []
#
# Comprobar con Hypothesis que la intersección de intervalos es
# conmutativa.
# ---------------------------------------------------------------------
Rectangulo = list[float]
# La propiedad es
@given(st.floats(), st.floats(), st.floats(), st.floats())
def test_prop_raices(a1: float, b1: float, a2: float, b2: float) -> None:
assume(a1 <= b1 and a2 <= b2)
assert interseccion([a1, b1], [a2, b2]) == interseccion([a2, b2], [a1, b1])
# La comprobación es
# src> poetry run pytest -q interseccion_de_intervalos_cerrados.py
# 1 passed in 0.64s
import Test.QuickCheck
-- La propiedad es
prop_distributiva :: (Int,Int) -> (Int,Int) -> (Int,Int) -> Property
prop_distributiva x y z =
snd x /= 0 && snd y /= 0 && snd z /= 0 ==>
igualdadRacional (productoRacional x (sumaRacional y z))
(sumaRacional (productoRacional x y)
(productoRacional x z))
-- La comprobación es
-- λ> quickCheck prop_distributiva
-- +++ OK, passed 100 tests; 21 discarded.
64 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Los números racionales pueden representarse mediante pares de números
# enteros. Por ejemplo, el número 2/5 puede representarse mediante el
# par (2,5).
#
# El tipo de los racionales se define por
# Racional = tuple[int, int]
# Definir las funciones
# formaReducida : (Racional) -> Racional
# sumaRacional : (Racional, Racional) -> Racional
# productoRacional : (Racional, Racional) -> Racional
# igualdadRacional : (Racional, Racional) -> bool
# tales que
# + formaReducida(x) es la forma reducida del número racional x. Por
# ejemplo,
# formaReducida((4, 10)) == (2, 5)
# formaReducida((0, 5)) == (0, 1)
# + sumaRacional(x, y) es la suma de los números racionales x e y,
# expresada en forma reducida. Por ejemplo,
# sumaRacional((2, 3), (5, 6)) == (3, 2)
# sumaRacional((3, 5), (-3, 5)) == (0, 1)
# + productoRacional(x, y) es el producto de los números racionales x e
# y, expresada en forma reducida. Por ejemplo,
# productoRacional((2, 3), (5, 6)) == (5, 9)
# + igualdadRacional(x, y) se verifica si los números racionales x e y
# son iguales. Por ejemplo,
# igualdadRacional((6, 9), (10, 15)) == True
# igualdadRacional((6, 9), (11, 15)) == False
# igualdadRacional((0, 2), (0, -5)) == True
#
# Comprobar con Hypothesis la propiedad distributiva del producto
# racional respecto de la suma.
# ---------------------------------------------------------------------
# La propiedad es
@given(st.tuples(st.integers(), st.integers()),
st.tuples(st.integers(), st.integers()),
st.tuples(st.integers(), st.integers()))
def test_prop_distributiva(x: tuple[int, int],
y: tuple[int, int],
z: tuple[int, int]) -> None:
(_, x2) = x
(_, y2) = y
(_, z2) = z
assume(x2 != 0 and y2 != 0 and z2 != 0)
assert igualdadRacional(productoRacional(x, sumaRacional(y, z)),
sumaRacional(productoRacional(x, y),
66 Ejercicios de programación con Python
productoRacional(x, z)))
# La comprobación es
# src> poetry run pytest -q numeros_racionales.py
# 1 passed in 0.37s
Capítulo 2
Contenido
2.1. Reconocimiento de subconjunto . . . . . . . . . . . . . . . . . 57
2.2. Igualdad de conjuntos . . . . . . . . . . . . . . . . . . . . . . . 61
2.3. Unión conjuntista de listas . . . . . . . . . . . . . . . . . . . . 65
2.4. Intersección conjuntista de listas . . . . . . . . . . . . . . . . 70
2.5. Diferencia conjuntista de listas . . . . . . . . . . . . . . . . . 75
2.6. Divisores de un número . . . . . . . . . . . . . . . . . . . . . . 79
2.7. Divisores primos . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.8. Números libres de cuadrados . . . . . . . . . . . . . . . . . . 98
2.9. Suma de los primeros números naturales . . . . . . . . . . .105
2.10. Suma de los cuadrados de los primeros números naturales 110
2.11. Suma de cuadrados menos cuadrado de la suma . . . . . .115
2.12. Triángulo aritmético . . . . . . . . . . . . . . . . . . . . . . . .122
2.13. Suma de divisores . . . . . . . . . . . . . . . . . . . . . . . . .128
2.14. Números perfectos . . . . . . . . . . . . . . . . . . . . . . . .137
2.15. Números abundantes . . . . . . . . . . . . . . . . . . . . . . .142
2.16. Números abundantes menores o iguales que n . . . . . . . .147
1
https://jaalonso.github.io/materias/PFconHaskell/temas/tema-5.html
67
68 Ejercicios de programación con Python
-- 1ª solución
Capítulo 2. Definiciones por comprensión 69
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_subconjunto :: [Int] -> [Int] -> Bool
prop_subconjunto xs ys =
all (== subconjunto1 xs ys)
[subconjunto2 xs ys,
subconjunto3 xs ys,
subconjunto4 xs ys]
-- La comprobación es
-- λ> quickCheck prop_subconjunto
70 Ejercicios de programación con Python
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> subconjunto1 [1..2*10^4] [1..2*10^4]
-- True
-- (1.81 secs, 5,992,448 bytes)
-- λ> subconjunto2 [1..2*10^4] [1..2*10^4]
-- True
-- (1.83 secs, 6,952,200 bytes)
-- λ> subconjunto3 [1..2*10^4] [1..2*10^4]
-- True
-- (1.75 secs, 4,712,304 bytes)
-- λ> subconjunto4 [1..2*10^4] [1..2*10^4]
-- True
-- (0.04 secs, 6,312,056 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# subconjunto : (list[A], list[A]) -> bool
# tal que subconjunto(xs, ys) se verifica si xs es un subconjunto de
# ys. Por ejemplo,
# subconjunto([3, 2, 3], [2, 5, 3, 5]) == True
# subconjunto([3, 2, 3], [2, 5, 6, 5]) == False
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
def subconjunto1(xs: list[A],
ys: list[A]) -> bool:
return [x for x in xs if x in ys] == xs
# 2ª solución
def subconjunto2(xs: list[A],
ys: list[A]) -> bool:
if xs:
return xs[0] in ys and subconjunto2(xs[1:], ys)
return True
# 3ª solución
def subconjunto3(xs: list[A],
ys: list[A]) -> bool:
return all(x in ys for x in xs)
# 4ª solución
def subconjunto4(xs: list[A],
ys: list[A]) -> bool:
return set(xs) <= set(ys)
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_subconjunto(xs: list[int], ys: list[int]) -> None:
assert subconjunto1(xs, ys)\
== subconjunto2(xs, ys)\
== subconjunto3(xs, ys)\
== subconjunto4(xs, ys)
# La comprobación es
72 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> xs = list(range(20000))
# >>> tiempo('subconjunto1(xs, xs)')
# 1.27 segundos
# >>> tiempo('subconjunto2(xs, xs)')
# 1.84 segundos
# >>> tiempo('subconjunto3(xs, xs)')
# 1.19 segundos
# >>> tiempo('subconjunto4(xs, xs)')
# 0.01 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_iguales :: [Int] -> [Int] -> Bool
prop_iguales xs ys =
74 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_iguales
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> iguales1 [1..2*10^4] [1..2*10^4]
-- True
-- (4.05 secs, 8,553,104 bytes)
-- λ> iguales2 [1..2*10^4] [1..2*10^4]
-- True
-- (4.14 secs, 9,192,768 bytes)
-- λ> iguales3 [1..2*10^4] [1..2*10^4]
-- True
-- (0.01 secs, 8,552,232 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# iguales : (list[Any], list[Any]) -> bool
# tal que iguales(xs, ys) se verifica si xs e ys son iguales. Por
# ejemplo,
# iguales([3, 2, 3], [2, 3]) == True
# iguales([3, 2, 3], [2, 3, 2]) == True
# iguales([3, 2, 3], [2, 3, 4]) == False
# iguales([2, 3], [4, 5]) == False
# ---------------------------------------------------------------------
# pylint: disable=arguments-out-of-order
# 1ª solución
# ===========
# 2ª solución
# ===========
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_iguales(xs: list[int], ys: list[int]) -> None:
assert iguales1(xs, ys) == iguales2(xs, ys)
# La comprobación es
# src> poetry run pytest -q igualdad_de_conjuntos.py
# 1 passed in 0.28s
# Comparación de eficiencia
# =========================
print(f”{t:0.2f} segundos”)
# La comparación es
# >>> xs = list(range(20000))
# >>> tiempo('iguales1(xs, xs)')
# 2.71 segundos
# >>> tiempo('iguales2(xs, xs)')
# 0.01 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
Capítulo 2. Definiciones por comprensión 77
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_union :: [Int] -> [Int] -> Bool
prop_union xs ys =
all (== sort (xs' `union1` ys'))
[sort (xs' `union2` ys'),
sort (xs' `union3` ys'),
xs' `union4` ys']
where xs' = nub xs
ys' = nub ys
-- La comprobación es
-- λ> quickCheck prop_union
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
78 Ejercicios de programación con Python
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_union_conmutativa :: [Int] -> [Int] -> Bool
prop_union_conmutativa xs ys =
iguales (xs `union1` ys) (ys `union1` xs)
-- La comprobación es
-- λ> quickCheck prop_union_conmutativa
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# union : (list[A], list[A]) -> list[A]
# tal que union(xs, ys) es la unión de las listas sin elementos
Capítulo 2. Definiciones por comprensión 79
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
zs.append(x)
return zs
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
#
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_union(xs: list[int], ys: list[int]) -> None:
xs1 = list(set(xs))
ys1 = list(set(ys))
assert sorted(union1(xs1, ys1)) ==\
sorted(union2(xs1, ys1)) ==\
sorted(union3(xs1, ys1)) ==\
sorted(union4(xs1, ys1))
# La comprobación es
# src> poetry run pytest -q union_conjuntista_de_listas.py
# 1 passed in 0.36s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('union1(list(range(0,30000,2)), list(range(1,30000,2)))')
# 1.30 segundos
# >>> tiempo('union2(list(range(0,30000,2)), list(range(1,30000,2)))')
# 2.84 segundos
Capítulo 2. Definiciones por comprensión 81
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_union_conmutativa(xs: list[int], ys: list[int]) -> None:
xs1 = list(set(xs))
ys1 = list(set(ys))
assert iguales(union1(xs1, ys1), union1(ys1, xs1))
# La comprobación es
# src> poetry run pytest -q union_conjuntista_de_listas.py
# 2 passed in 0.49s
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
Capítulo 2. Definiciones por comprensión 83
-- ============================
-- La propiedad es
prop_interseccion :: [Int] -> [Int] -> Bool
prop_interseccion xs ys =
all (== sort (xs' `interseccion1` ys'))
[sort (xs' `interseccion2` ys'),
sort (xs' `interseccion3` ys'),
xs' `interseccion4` ys']
where xs' = nub xs
ys' = nub ys
-- La comprobación es
-- λ> quickCheck prop_interseccion
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (interseccion1 [0..3*10^4] [1,3..3*10^4])
-- 15000
-- (2.94 secs, 6,673,360 bytes)
-- λ> length (interseccion2 [0..3*10^4] [1,3..3*10^4])
-- 15000
-- (3.04 secs, 9,793,440 bytes)
-- λ> length (interseccion3 [0..3*10^4] [1,3..3*10^4])
-- 15000
-- (5.39 secs, 6,673,472 bytes)
-- λ> length (interseccion4 [0..3*10^4] [1,3..3*10^4])
-- 15000
-- (0.04 secs, 8,593,176 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# interseccion : (list[A], list[A]) -> list[A]
# tal que interseccion(xs, ys) es la intersección de las listas sin
# elementos repetidos xs e ys. Por ejemplo,
84 Ejercicios de programación con Python
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
#
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_interseccion(xs: list[int], ys: list[int]) -> None:
xs1 = list(set(xs))
ys1 = list(set(ys))
assert sorted(interseccion1(xs1, ys1)) ==\
sorted(interseccion2(xs1, ys1)) ==\
sorted(interseccion3(xs1, ys1)) ==\
sorted(interseccion4(xs1, ys1))
# La comprobación es
# src> poetry run pytest -q interseccion_conjuntista_de_listas.py
# 1 passed in 0.33s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('interseccion1(list(range(0,20000)), list(range(1,20000,2)))')
# 0.98 segundos
# >>> tiempo('interseccion2(list(range(0,20000)), list(range(1,20000,2)))')
# 2.13 segundos
# >>> tiempo('interseccion3(list(range(0,20000)), list(range(1,20000,2)))')
# 0.87 segundos
86 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_diferencia :: [Int] -> [Int] -> Bool
prop_diferencia xs ys =
all (== sort (xs' `diferencia1` ys'))
[sort (xs' `diferencia2` ys'),
sort (xs' `diferencia3` ys'),
xs' `diferencia4` ys']
where xs' = nub xs
ys' = nub ys
-- La comprobación es
-- λ> quickCheck prop_diferencia
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (diferencia1 [0..3*10^4] [1,3..3*10^4])
-- 15001
-- (3.39 secs, 9,553,528 bytes)
-- λ> length (diferencia2 [0..3*10^4] [1,3..3*10^4])
-- 15001
-- (2.98 secs, 9,793,528 bytes)
88 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# diferencia : (list[A], list[A]) -> list[A]
# tal que diferencia(xs, ys) es la diferencia de las listas sin
# elementos repetidos xs e ys. Por ejemplo,
# diferencia([3, 2, 5, 6], [5, 7, 3, 4]) == [2, 6]
# diferencia([3, 2, 5], [5, 7, 3, 2]) == []
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
return []
if xs[0] in ys:
return diferencia2(xs[1:], ys)
return [xs[0]] + diferencia2(xs[1:], ys)
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_diferencia(xs: list[int], ys: list[int]) -> None:
xs1 = list(set(xs))
ys1 = list(set(ys))
assert sorted(diferencia1(xs1, ys1)) ==\
sorted(diferencia2(xs1, ys1)) ==\
sorted(diferencia3(xs1, ys1)) ==\
sorted(diferencia4(xs1, ys1))
# La comprobación es
# src> poetry run pytest -q diferencia_conjuntista_de_listas.py
# 1 passed in 0.39s
# Comparación de eficiencia
90 Ejercicios de programación con Python
# =========================
# La comparación es
# >>> tiempo('diferencia1(list(range(0,20000)), list(range(1,20000,2)))')
# 0.89 segundos
# >>> tiempo('diferencia2(list(range(0,20000)), list(range(1,20000,2)))')
# 2.11 segundos
# >>> tiempo('diferencia3(list(range(0,20000)), list(range(1,20000,2)))')
# 1.06 segundos
# >>> tiempo('diferencia4(list(range(0,20000)), list(range(1,20000,2)))')
# 0.01 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- 9ª solución
-- ===========
-- 10ª solución
-- ============
-- 11ª solución
-- ============
-- Comprobación de equivalencia
-- ============================
94 Ejercicios de programación con Python
-- La propiedad es
prop_divisores :: Positive Integer -> Bool
prop_divisores (Positive n) =
all (== divisores1 n)
[ divisores2 n
, divisores3 n
, divisores4 n
, divisores5 n
, divisores6 n
, divisores7 n
, divisores8 n
, divisores9 n
, divisores10 n
, divisores11 n
]
-- La comprobación es
-- λ> quickCheck prop_divisores
-- +++ OK, passed 100 tests.
-- Comparación de la eficiencia
-- ============================
-- La comparación es
-- λ> length (divisores1 (product [1..11]))
-- 540
-- (18.55 secs, 7,983,950,592 bytes)
-- λ> length (divisores2 (product [1..11]))
-- 540
-- (18.81 secs, 7,983,950,592 bytes)
-- λ> length (divisores3 (product [1..11]))
-- 540
-- (12.79 secs, 6,067,935,544 bytes)
-- λ> length (divisores4 (product [1..11]))
-- 540
-- (12.51 secs, 6,067,935,592 bytes)
-- λ> length (divisores5 (product [1..11]))
-- 540
-- (0.03 secs, 1,890,296 bytes)
Capítulo 2. Definiciones por comprensión 95
En Python
# ---------------------------------------------------------------------
# Definir la función
# divisores : (int) -> list[int]
# tal que divisores(n) es el conjunto de divisores de n. Por
# ejemplo,
# divisores(30) == [1, 2, 3, 5, 6, 10, 15, 30]
# len(divisores1(factorial(10))) == 270
# len(divisores1(factorial(25))) == 340032
# ---------------------------------------------------------------------
# pylint: disable=unused-import
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ============
# 7ª solución
# ===========
# 8ª solución
# ============
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_divisores(n: int) -> None:
assert divisores1(n) ==\
divisores2(n) ==\
Capítulo 2. Definiciones por comprensión 99
divisores3(n) ==\
divisores4(n) ==\
divisores5(n) ==\
divisores6(n) ==\
divisores7(n) ==\
divisores8(n)
# La comprobación es
# src> poetry run pytest -q divisores_de_un_numero.py
# 1 passed in 0.84s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('divisores5(4*factorial(7))')
# 1.40 segundos
#
# >>> tiempo('divisores1(factorial(11))')
# 1.79 segundos
# >>> tiempo('divisores2(factorial(11))')
# 3.80 segundos
# >>> tiempo('divisores3(factorial(11))')
# 5.22 segundos
# >>> tiempo('divisores4(factorial(11))')
# 0.00 segundos
# >>> tiempo('divisores6(factorial(11))')
# 3.51 segundos
# >>> tiempo('divisores7(factorial(11))')
# 0.00 segundos
# >>> tiempo('divisores8(factorial(11))')
# 0.00 segundos
#
# >>> tiempo('divisores4(factorial(17))')
# 2.23 segundos
100 Ejercicios de programación con Python
# >>> tiempo('divisores7(factorial(17))')
# 3.22 segundos
# >>> tiempo('divisores8(factorial(17))')
# 0.00 segundos
#
# >>> tiempo('divisores8(factorial(27))')
# 0.28 segundos
-- 1ª solución
-- ===========
-- divisores 30 == [1,2,3,5,6,10,15,30]
divisores1 :: Integer -> [Integer]
divisores1 n = [x | x <- [1..n], n `mod` x == 0]
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_divisoresPrimos :: Integer -> Property
prop_divisoresPrimos n =
n > 1 ==>
all (== divisoresPrimos1 n)
[divisoresPrimos2 n,
divisoresPrimos3 n,
divisoresPrimos4 n,
divisoresPrimos5 n,
divisoresPrimos6 n]
-- La comprobación es
-- λ> quickCheck prop_divisoresPrimos
-- +++ OK, passed 100 tests; 108 discarded.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> divisoresPrimos1 (product [1..11])
-- [2,3,5,7,11]
-- (18.34 secs, 7,984,382,104 bytes)
-- λ> divisoresPrimos2 (product [1..11])
-- [2,3,5,7,11]
-- (0.02 secs, 2,610,976 bytes)
-- λ> divisoresPrimos3 (product [1..11])
-- [2,3,5,7,11]
-- (0.02 secs, 2,078,288 bytes)
-- λ> divisoresPrimos4 (product [1..11])
104 Ejercicios de programación con Python
-- [2,3,5,7,11]
-- (0.02 secs, 565,992 bytes)
-- λ> divisoresPrimos5 (product [1..11])
-- [2,3,5,7,11]
-- (0.01 secs, 568,000 bytes)
-- λ> divisoresPrimos6 (product [1..11])
-- [2,3,5,7,11]
-- (0.00 secs, 2,343,392 bytes)
--
-- λ> divisoresPrimos2 (product [1..16])
-- [2,3,5,7,11,13]
-- (2.32 secs, 923,142,480 bytes)
-- λ> divisoresPrimos3 (product [1..16])
-- [2,3,5,7,11,13]
-- (0.80 secs, 556,961,088 bytes)
-- λ> divisoresPrimos4 (product [1..16])
-- [2,3,5,7,11,13]
-- (0.01 secs, 572,368 bytes)
-- λ> divisoresPrimos5 (product [1..16])
-- [2,3,5,7,11,13]
-- (0.01 secs, 31,665,896 bytes)
-- λ> divisoresPrimos6 (product [1..16])
-- [2,3,5,7,11,13]
-- (0.01 secs, 18,580,584 bytes)
--
-- λ> length (divisoresPrimos4 (product [1..30]))
-- 10
-- (0.01 secs, 579,168 bytes)
-- λ> length (divisoresPrimos5 (product [1..30]))
-- 10
-- (0.01 secs, 594,976 bytes)
-- λ> length (divisoresPrimos6 (product [1..30]))
-- 10
-- (3.38 secs, 8,068,783,408 bytes)
--
-- λ> length (divisoresPrimos4 (product [1..20000]))
-- 2262
-- (1.20 secs, 1,940,069,976 bytes)
-- λ> length (divisoresPrimos5 (product [1..20000]))
-- 2262
Capítulo 2. Definiciones por comprensión 105
En Python
# ---------------------------------------------------------------------
# Definir la función
# divisoresPrimos : (int) -> list[int]
# tal que divisoresPrimos(x) es la lista de los divisores primos de x.
# Por ejemplo,
# divisoresPrimos(40) == [2, 5]
# divisoresPrimos(70) == [2, 5, 7]
# len(divisoresPrimos4(producto(list(range(1, 20001))))) == 2262
# ------------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_divisoresPrimos(n: int) -> None:
assert divisoresPrimos1(n) ==\
divisoresPrimos2(n) ==\
divisoresPrimos3(n) ==\
divisoresPrimos4(n) ==\
divisoresPrimos5(n)
# La comprobación es
# src> poetry run pytest -q divisores_primos.py
# 1 passed in 0.70s
# Comparación de eficiencia
108 Ejercicios de programación con Python
# =========================
# La comparación es
# >>> tiempo('divisoresPrimos1(producto(list(range(1, 12))))')
# 11.14 segundos
# >>> tiempo('divisoresPrimos2(producto(list(range(1, 12))))')
# 0.03 segundos
# >>> tiempo('divisoresPrimos3(producto(list(range(1, 12))))')
# 0.00 segundos
# >>> tiempo('divisoresPrimos4(producto(list(range(1, 12))))')
# 0.00 segundos
# >>> tiempo('divisoresPrimos5(producto(list(range(1, 12))))')
# 0.00 segundos
#
# >>> tiempo('divisoresPrimos2(producto(list(range(1, 17))))')
# 14.21 segundos
# >>> tiempo('divisoresPrimos3(producto(list(range(1, 17))))')
# 0.00 segundos
# >>> tiempo('divisoresPrimos4(producto(list(range(1, 17))))')
# 0.01 segundos
# >>> tiempo('divisoresPrimos5(producto(list(range(1, 17))))')
# 0.00 segundos
#
# >>> tiempo('divisoresPrimos3(producto(list(range(1, 32))))')
# 0.00 segundos
# >>> tiempo('divisoresPrimos4(producto(list(range(1, 32))))')
# 4.59 segundos
# >>> tiempo('divisoresPrimos5(producto(list(range(1, 32))))')
# 0.00 segundos
#
# >>> tiempo('divisoresPrimos3(producto(list(range(1, 10001))))')
# 3.00 segundos
Capítulo 2. Definiciones por comprensión 109
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
110 Ejercicios de programación con Python
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
Capítulo 2. Definiciones por comprensión 111
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_libreDeCuadrados :: Integer -> Property
prop_libreDeCuadrados x =
x > 1 ==>
all (== libreDeCuadrados1 x)
[libreDeCuadrados2 x,
libreDeCuadrados3 x,
libreDeCuadrados4 x,
libreDeCuadrados5 x]
-- La comprobación es
-- λ> quickCheck prop_libreDeCuadrados
-- +++ OK, passed 100 tests; 165 discarded.
-- Comparación de eficiencia
112 Ejercicios de programación con Python
-- =========================
-- La comparación es
-- λ> libreDeCuadrados1 9699690
-- True
-- (8.54 secs, 6,441,144,248 bytes)
-- λ> libreDeCuadrados2 9699690
-- True
-- (4.78 secs, 1,940,781,632 bytes)
-- λ> libreDeCuadrados3 9699690
-- True
-- (0.01 secs, 561,400 bytes)
-- λ> libreDeCuadrados4 9699690
-- True
-- (0.01 secs, 568,160 bytes)
-- λ> libreDeCuadrados5 9699690
-- True
-- (0.01 secs, 567,536 bytes)
--
-- λ> libreDeCuadrados3 (product (take 30000 primes))
-- True
-- (2.30 secs, 2,369,316,208 bytes)
-- λ> libreDeCuadrados4 (product (take 30000 primes))
-- True
-- (6.68 secs, 4,565,617,408 bytes)
-- λ> libreDeCuadrados5 (product (take 30000 primes))
-- True
-- (5.54 secs, 3,411,701,752 bytes)
En Python
# ---------------------------------------------------------------------
# Un número es libre de cuadrados si no es divisible por el cuadrado de
# ningún entero mayor que 1. Por ejemplo, 70 es libre de cuadrado
# porque sólo es divisible por 1, 2, 5, 7 y 70; en cambio, 40 no es
# libre de cuadrados porque es divisible por 2^2.
#
# Definir la función
# libreDeCuadrados : (int) -> bool
# tal que (ibreDeCuadrados(x) se verifica si x es libre de cuadrados.
Capítulo 2. Definiciones por comprensión 113
# Por ejemplo,
# libreDeCuadrados(70) == True
# libreDeCuadrados(40) == False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 2. Definiciones por comprensión 115
@given(st.integers(min_value=2, max_value=1000))
def test_libreDeCuadrados(n: int) -> None:
assert libreDeCuadrados1(n) ==\
libreDeCuadrados2(n) ==\
libreDeCuadrados3(n) ==\
libreDeCuadrados4(n)
# La comprobación es
# src> poetry run pytest -q numeros_libres_de_cuadrados.py
# 1 passed in 0.59s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('libreDeCuadrados1(9699690)')
# 2.66 segundos
# >>> tiempo('libreDeCuadrados2(9699690)')
# 2.58 segundos
# >>> tiempo('libreDeCuadrados3(9699690)')
# 0.00 segundos
# >>> tiempo('libreDeCuadrados4(9699690)')
# 0.00 segundos
#
# >>> n = producto(list(primerange(1, 25000)))
# >>> tiempo('libreDeCuadrados3(n)')
# 0.42 segundos
# >>> tiempo('libreDeCuadrados4(n)')
# 0.14 segundos
116 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_suma :: Positive Integer -> Bool
prop_suma (Positive n) =
all (== suma1 n)
[suma2 n,
suma3 n,
suma4 n,
suma5 n]
-- La comprobación es
-- λ> quickCheck prop_suma
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> suma1 (5*10^6)
-- 12500002500000
-- (1.23 secs, 806,692,792 bytes)
-- λ> suma2 (5*10^6)
-- 12500002500000
-- (0.02 secs, 559,064 bytes)
-- λ> suma3 (5*10^6)
-- 12500002500000
-- (3.06 secs, 1,214,684,352 bytes)
-- λ> suma4 (5*10^6)
-- 12500002500000
-- (1.25 secs, 806,692,848 bytes)
-- λ> suma5 (5*10^6)
118 Ejercicios de programación con Python
-- 12500002500000
-- (0.26 secs, 440,559,048 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# suma : (int) -> int
# tal suma(n) es la suma de los n primeros números. Por ejemplo,
# suma(3) == 6
# len(str(suma2(10**100))) == 200
# ---------------------------------------------------------------------
setrecursionlimit(10**8)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
return 1
return n + suma3(n - 1)
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_suma(n: int) -> None:
r = suma1(n)
assert suma2(n) == r
assert suma3(n) == r
assert suma4(n) == r
assert suma5(n) == r
assert suma6(n) == r
120 Ejercicios de programación con Python
# La comprobación es
# src> poetry run pytest -q suma_de_los_primeros_numeros_naturales.py
# 1 passed in 0.16s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('suma1(20000)')
# 0.00 segundos
# >>> tiempo('suma2(20000)')
# 0.00 segundos
# >>> tiempo('suma3(20000)')
# 0.02 segundos
# >>> tiempo('suma4(20000)')
# 0.00 segundos
# >>> tiempo('suma5(20000)')
# 0.01 segundos
# >>> tiempo('suma6(20000)')
# 0.00 segundos
#
# >>> tiempo('suma1(10**8)')
# 1.55 segundos
# >>> tiempo('suma2(10**8)')
# 0.00 segundos
# >>> tiempo('suma4(10**8)')
# 3.69 segundos
# >>> tiempo('suma5(10**8)')
# 7.04 segundos
# >>> tiempo('suma6(10**8)')
# 4.23 segundos
Capítulo 2. Definiciones por comprensión 121
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_sumaDeCuadrados :: Positive Integer -> Bool
prop_sumaDeCuadrados (Positive n) =
all (== sumaDeCuadrados1 n)
[sumaDeCuadrados2 n,
sumaDeCuadrados3 n,
sumaDeCuadrados4 n,
sumaDeCuadrados5 n]
-- La comprobación es
-- λ> quickCheck prop_sumaDeCuadrados
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sumaDeCuadrados1 (2*10^6)
-- 2666668666667000000
-- (1.90 secs, 1,395,835,576 bytes)
-- λ> sumaDeCuadrados2 (2*10^6)
-- 2666668666667000000
-- (0.01 secs, 563,168 bytes)
-- λ> sumaDeCuadrados3 (2*10^6)
Capítulo 2. Definiciones por comprensión 123
-- 2666668666667000000
-- (2.37 secs, 1,414,199,400 bytes)
-- λ> sumaDeCuadrados4 (2*10^6)
-- 2666668666667000000
-- (1.33 secs, 1,315,836,128 bytes)
-- λ> sumaDeCuadrados5 (2*10^6)
-- 2666668666667000000
-- (0.71 secs, 1,168,563,384 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaDeCuadrados : (int) -> int
# tal sumaDeCuadrados(n) es la suma de los xuadrados de los n primeros
# números naturales. Por ejemplo,
# sumaDeCuadrados(3) == 14
# sumaDeCuadrados(100) == 338350
# len(str(sumaDeCuadrados(10**100))) == 300
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
124 Ejercicios de programación con Python
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 2. Definiciones por comprensión 125
@given(st.integers(min_value=1, max_value=1000))
def test_sumaDeCuadrados(n: int) -> None:
r = sumaDeCuadrados1(n)
assert sumaDeCuadrados2(n) == r
assert sumaDeCuadrados3(n) == r
assert sumaDeCuadrados4(n) == r
assert sumaDeCuadrados5(n) == r
assert sumaDeCuadrados6(n) == r
# La comprobación es
# src> poetry run pytest -q suma_de_los_cuadrados_de_los_primeros_numeros_natu
# 1 passed in 0.19s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('sumaDeCuadrados1(20000)')
# 0.01 segundos
# >>> tiempo('sumaDeCuadrados2(20000)')
# 0.00 segundos
# >>> tiempo('sumaDeCuadrados3(20000)')
# 0.02 segundos
# >>> tiempo('sumaDeCuadrados4(20000)')
# 0.02 segundos
# >>> tiempo('sumaDeCuadrados5(20000)')
# 0.02 segundos
# >>> tiempo('sumaDeCuadrados6(20000)')
# 0.02 segundos
#
# >>> tiempo('sumaDeCuadrados1(10**7)')
# 2.19 segundos
# >>> tiempo('sumaDeCuadrados2(10**7)')
# 0.00 segundos
# >>> tiempo('sumaDeCuadrados4(10**7)')
126 Ejercicios de programación con Python
# 2.48 segundos
# >>> tiempo('sumaDeCuadrados5(10**7)')
# 2.53 segundos
# >>> tiempo('sumaDeCuadrados6(10**7)')
# 2.22 segundos
import Suma_de_los_cuadrados_de_los_primeros_numeros_naturales
(sumaDeCuadrados1, sumaDeCuadrados2, sumaDeCuadrados3, sumaDeCuadrados4, sumaDe
import Data.List (foldl')
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_euler6 :: Positive Integer -> Bool
prop_euler6 (Positive n) =
all (== euler6a n)
[euler6b n,
euler6c n,
euler6d n,
euler6e n]
-- La comprobación es
-- λ> quickCheck prop_euler6
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> euler6a (3*10^6)
-- 20250004499997749999500000
-- (3.32 secs, 2,577,174,640 bytes)
-- λ> euler6b (3*10^6)
-- 20250004499997749999500000
-- (0.01 secs, 569,288 bytes)
-- λ> euler6c (3*10^6)
-- 20250004499997749999500000
-- (5.60 secs, 2,849,479,288 bytes)
-- λ> euler6d (3*10^6)
-- 20250004499997749999500000
-- (2.52 secs, 2,457,175,248 bytes)
-- λ> euler6e (3*10^6)
-- 20250004499997749999500000
-- (1.08 secs, 2,016,569,472 bytes)
--
-- λ> euler6a (10^7)
-- 2500000166666641666665000000
Capítulo 2. Definiciones por comprensión 129
En Python
# ---------------------------------------------------------------------
# Definir la función
# euler6 : (int) -> int
# tal que euler6(n) es la diferencia entre el cuadrado de la suma
# de los n primeros números y la suma de los cuadrados de los n
# primeros números. Por ejemplo,
# euler6(10) == 2640
# euler6(10^10) == 2500000000166666666641666666665000000000
#
# Nota: Este ejercicio está basado en el problema 6 del proyecto Euler
# https://www.projecteuler.net/problem=6
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_euler6(n: int) -> None:
r = euler6a(n)
assert euler6b(n) == r
assert euler6c(n) == r
assert euler6d(n) == r
assert euler6e(n) == r
assert euler6f(n) == r
# La comprobación es
132 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('euler6a(20000)')
# 0.02 segundos
# >>> tiempo('euler6b(20000)')
# 0.00 segundos
# >>> tiempo('euler6c(20000)')
# 0.02 segundos
# >>> tiempo('euler6d(20000)')
# 0.01 segundos
# >>> tiempo('euler6e(20000)')
# 0.01 segundos
# >>> tiempo('euler6f(20000)')
# 0.01 segundos
#
# >>> tiempo('euler6a(10**7)')
# 2.26 segundos
# >>> tiempo('euler6b(10**7)')
# 0.00 segundos
# >>> tiempo('euler6d(10**7)')
# 2.58 segundos
# >>> tiempo('euler6e(10**7)')
# 2.89 segundos
# >>> tiempo('euler6f(10**7)')
# 2.45 segundos
Capítulo 2. Definiciones por comprensión 133
import Test.QuickCheck
-- 1ª definición de línea
-- ======================
-- 2ª definición de línea
-- ======================
-- 3ª definición de línea
-- ======================
-- La propiedad es
prop_linea :: Positive Integer -> Bool
prop_linea (Positive n) =
all (== linea1 n)
[linea2 n,
linea3 n]
-- La comprobación es
-- λ> quickCheck prop_linea
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> last (linea1 (10^7))
-- 50000005000000
-- (5.10 secs, 3,945,159,856 bytes)
-- λ> last (linea2 (10^7))
Capítulo 2. Definiciones por comprensión 135
-- 50000005000000
-- (3.11 secs, 2,332,859,512 bytes)
-- λ> last (linea3 (10^7))
-- 50000005000000
-- (0.16 secs, 720,559,384 bytes)
-- 1ª definición de triangulo
-- ==========================
-- 2ª definición de triangulo
-- ==========================
-- 3ª definición de triangulo
-- ==========================
-- La propiedad es
prop_triangulo :: Positive Integer -> Bool
prop_triangulo (Positive n) =
all (== triangulo1 n)
[triangulo2 n,
triangulo3 n]
-- La comprobación es
-- λ> quickCheck prop_triangulo
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> last (last (triangulo1 (3*10^6)))
-- 4500001500000
-- (2.25 secs, 1,735,919,184 bytes)
-- λ> last (last (triangulo2 (3*10^6)))
-- 4500001500000
-- (1.62 secs, 1,252,238,872 bytes)
-- λ> last (last (triangulo3 (3*10^6)))
-- 4500001500000
-- (0.79 secs, 768,558,776 bytes)
En Python
# ---------------------------------------------------------------------
# Los triángulos aritméticos se forman como sigue
# 1
# 2 3
# 4 5 6
# 7 8 9 10
# 11 12 13 14 15
# 16 17 18 19 20 21
#
# Definir las funciones
# linea : (int) -> list[int]
# triangulo : (int) -> list[list[int]]
# tales que
# + linea(n) es la línea n-ésima de los triángulos aritméticos. Por
# ejemplo,
# linea(4) == [7, 8, 9, 10]
# linea(5) == [11, 12, 13, 14, 15]
# linea(10**8)[0] == 4999999950000001
# + triangulo(n) es el triángulo aritmético de altura n. Por ejemplo,
# triangulo(3) == [[1], [2, 3], [4, 5, 6]]
# triangulo(4) == [[1], [2, 3], [4, 5, 6], [7, 8, 9, 10]]
# ---------------------------------------------------------------------
# 1ª definición de línea
# ======================
# 2ª definición de línea
# ======================
# 3ª definición de línea
# ======================
@given(st.integers(min_value=1, max_value=1000))
def test_suma(n: int) -> None:
r = linea1(n)
assert linea2(n) == r
assert linea3(n) == r
# La comprobación es
138 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('linea1(10**7)')
# 0.53 segundos
# >>> tiempo('linea2(10**7)')
# 0.40 segundos
# >>> tiempo('linea3(10**7)')
# 0.29 segundos
# 1ª definición de triangulo
# ==========================
# 2ª definición de triangulo
# ==========================
# 3ª definición de triangulo
# ==========================
@given(st.integers(min_value=1, max_value=1000))
def test_triangulo(n: int) -> None:
r = triangulo1(n)
assert triangulo2(n) == r
assert triangulo3(n) == r
# La comprobación es
# src> poetry run pytest -q triangulo_aritmetico.py
# 1 passed in 3.44s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_sumaDivisores :: Positive Integer -> Bool
prop_sumaDivisores (Positive x) =
all (== sumaDivisores1 x)
[ sumaDivisores2 x
, sumaDivisores3 x
, sumaDivisores4 x
, sumaDivisores5 x
, sumaDivisores6 x
, sumaDivisores7 x
, sumaDivisores8 x
]
-- La comprobación es
-- λ> quickCheck prop_sumaDivisores
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
Capítulo 2. Definiciones por comprensión 143
-- =========================
-- La comparación es
-- λ> sumaDivisores1 5336100
-- 21386001
-- (2.25 secs, 1,067,805,248 bytes)
-- λ> sumaDivisores2 5336100
-- 21386001
-- (0.01 secs, 659,112 bytes)
-- λ> sumaDivisores3 5336100
-- 21386001
-- (0.01 secs, 635,688 bytes)
-- λ> sumaDivisores4 5336100
-- 21386001
-- (0.01 secs, 648,992 bytes)
-- λ> sumaDivisores5 5336100
-- 21386001
-- (2.44 secs, 1,323,924,176 bytes)
-- λ> sumaDivisores6 5336100
-- 21386001
-- (0.01 secs, 832,104 bytes)
-- λ> sumaDivisores7 5336100
-- 21386001
-- (0.01 secs, 571,040 bytes)
-- λ> sumaDivisores8 5336100
-- 21386001
-- (0.00 secs, 558,296 bytes)
--
-- λ> sumaDivisores2 251888923423315469521109880000000
-- 1471072204661054993275791673480320
-- (2.30 secs, 1,130,862,080 bytes)
-- λ> sumaDivisores3 251888923423315469521109880000000
-- 1471072204661054993275791673480320
-- (1.83 secs, 896,386,232 bytes)
-- λ> sumaDivisores4 251888923423315469521109880000000
-- 1471072204661054993275791673480320
-- (1.52 secs, 997,992,328 bytes)
-- λ> sumaDivisores6 251888923423315469521109880000000
-- 1471072204661054993275791673480320
-- (2.35 secs, 5,719,848,600 bytes)
144 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaDivisores : (int) -> int
# tal que sumaDivisores(x) es la suma de los divisores de x. Por ejemplo,
# sumaDivisores(12) == 28
# sumaDivisores(25) == 31
# sumaDivisores (reduce(mul, range(1, 26))) == 93383273455325195473152000
# len(str(sumaDivisores6(reduce(mul, range(1, 30001))))) == 121289
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
Capítulo 2. Definiciones por comprensión 145
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_sumaDivisores(n: int) -> None:
r = sumaDivisores1(n)
assert sumaDivisores2(n) == r
assert sumaDivisores3(n) == r
assert sumaDivisores4(n) == r
assert sumaDivisores5(n) == r
Capítulo 2. Definiciones por comprensión 147
assert sumaDivisores6(n) == r
# La comprobación es
# src> poetry run pytest -q suma_de_divisores.py
# 1 passed in 0.90s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('sumaDivisores1(5336100)')
# 0.29 segundos
# >>> tiempo('sumaDivisores2(5336100)')
# 0.00 segundos
# >>> tiempo('sumaDivisores3(5336100)')
# Process Python terminado (killed)
# >>> tiempo('sumaDivisores4(5336100)')
# 0.00 segundos
# >>> tiempo('sumaDivisores5(5336100)')
# 0.00 segundos
# >>> tiempo('sumaDivisores6(5336100)')
# 0.00 segundos
#
# >>> tiempo('sumaDivisores1(2**9 * 3**8 * 5**2)')
# 4.52 segundos
# >>> tiempo('sumaDivisores2(2**9 * 3**8 * 5**2)')
# 0.00 segundos
# >>> tiempo('sumaDivisores4(2**9 * 3**8 * 5**2)')
# 0.00 segundos
# >>> tiempo('sumaDivisores5(2**9 * 3**8 * 5**2)')
# 0.00 segundos
# >>> tiempo('sumaDivisores6(2**9 * 3**8 * 5**2)')
# 0.00 segundos
#
# >>> tiempo('sumaDivisores2(2**9 * 3**8 * 5**7 * 7**4)')
148 Ejercicios de programación con Python
# 0.00 segundos
# >>> tiempo('sumaDivisores4(2**9 * 3**8 * 5**7 * 7**4)')
# 0.00 segundos
# >>> tiempo('sumaDivisores5(2**9 * 3**8 * 5**7 * 7**4)')
# 3.24 segundos
# >>> tiempo('sumaDivisores6(2**9 * 3**8 * 5**7 * 7**4)')
# 0.00 segundos
#
# >>> tiempo('sumaDivisores2(251888923423315469521109880000000)')
# 1.13 segundos
# >>> tiempo('sumaDivisores4(251888923423315469521109880000000)')
# 0.00 segundos
# >>> tiempo('sumaDivisores6(251888923423315469521109880000000)')
# 0.00 segundos
#
# >>> tiempo('sumaDivisores4(reduce(mul, list(range(1, 30000))))')
# 1.89 segundos
# >>> tiempo('sumaDivisores6(reduce(mul, list(range(1, 30000))))')
# 1.88 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
[x | x <- [1..n],
esPerfecto2 x]
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_perfectos :: Positive Integer -> Bool
prop_perfectos (Positive n) =
all (== perfectos1 n)
[perfectos2 n,
perfectos3 n,
perfectos4 n]
-- La comprobación es
-- λ> quickCheck prop_perfectos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
Capítulo 2. Definiciones por comprensión 151
-- La comparación es
-- λ> perfectos1 (4*10^3)
-- [6,28,496]
-- (4.64 secs, 1,606,883,384 bytes)
-- λ> perfectos2 (4*10^3)
-- [6,28,496]
-- (0.02 secs, 9,167,208 bytes)
--
-- λ> perfectos2 (2*10^6)
-- [6,28,496,8128]
-- (3.32 secs, 5,120,880,728 bytes)
-- λ> perfectos3 (2*10^6)
-- [6,28,496,8128]
-- (2.97 secs, 5,040,880,632 bytes)
-- λ> perfectos4 (2*10^6)
-- [6,28,496,8128]
-- (2.80 secs, 5,040,880,608 bytes)
En Python
# ---------------------------------------------------------------------
# Un números entero positivo es [perfecto](https://bit.ly/3BIN0be) si
# es igual a la suma de sus divisores, excluyendo el propio número. Por
# ejemplo, 6 es un número perfecto porque sus divisores propios son 1,
# 2 y 3; y 6 = 1 + 2 + 3.
#
# Definir la función
# perfectos (int) -> list[int]
# tal que perfectos(n) es la lista de todos los números perfectos
# menores que n. Por ejemplo,
# perfectos(500) == [6, 28, 496]
# perfectos(10^5) == [6, 28, 496, 8128]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_perfectos(n: int) -> None:
assert perfectos1(n) == perfectos2(n)
# La comprobación es
# src> poetry run pytest -q numeros_perfectos.py
# 1 passed in 1.43s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('perfectos1(10**4)')
# 2.97 segundos
# >>> tiempo('perfectos2(10**4)')
# 0.57 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_numeroAbundante :: Positive Integer -> Bool
prop_numeroAbundante (Positive n) =
numeroAbundante1 n == numeroAbundante2 n
-- La comprobación es
-- λ> quickCheck prop_numeroAbundante
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> numeroAbundante1 (5*10^6)
-- True
-- (2.55 secs, 1,000,558,840 bytes)
-- λ> numeroAbundante2 (5*10^6)
-- True
-- (0.00 secs, 555,408 bytes)
En Python
# ---------------------------------------------------------------------
# Un número natural n se denomina [abundante](https://bit.ly/3Uk4XUE)
# si es menor que la suma de sus divisores propios. Por ejemplo, 12 es
# abundante ya que la suma de sus divisores propios es 16
# (= 1 + 2 + 3 + 4 + 6), pero 5 y 28 no lo son.
#
# Definir la función
156 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_numeroAbundante(n: int) -> None:
assert numeroAbundante1(n) == numeroAbundante2(n)
# La comprobación es
# src> poetry run pytest -q numeros_abundantes.py
# 1 passed in 0.38s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('numeroAbundante1(4 * 10**7)')
# 2.02 segundos
# >>> tiempo('numeroAbundante2(4 * 10**7)')
# 0.00 segundos
158 Ejercicios de programación con Python
-- 1ª solución
-- ===========
numeroAbundante1 x =
x < sumaDivisores1 x - x
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_numerosAbundantesMenores :: Positive Integer -> Bool
prop_numerosAbundantesMenores (Positive n) =
all (== numerosAbundantesMenores1 n)
[numerosAbundantesMenores2 n,
numerosAbundantesMenores3 n,
numerosAbundantesMenores4 n]
-- La comprobación es
-- λ> quickCheck prop_numerosAbundantesMenores
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (numerosAbundantesMenores1 (5*10^3))
-- 1239
-- (5.49 secs, 2,508,692,808 bytes)
-- λ> length (numerosAbundantesMenores2 (5*10^3))
-- 1239
-- (0.01 secs, 11,501,944 bytes)
En Python
# ---------------------------------------------------------------------
# Un número natural n se denomina [abundante](https://bit.ly/3Uk4XUE)
# si es menor que la suma de sus divisores propios. Por ejemplo, 12 es
# abundante ya que la suma de sus divisores propios es 16
# (= 1 + 2 + 3 + 4 + 6), pero 5 y 28 no lo son.
#
# Definir la función
# numerosAbundantesMenores : (int) -> list[Int]
# tal que numerosAbundantesMenores(n) es la lista de números
# abundantes menores o iguales que n. Por ejemplo,
# numerosAbundantesMenores(50) == [12,18,20,24,30,36,40,42,48]
# numerosAbundantesMenores(48) == [12,18,20,24,30,36,40,42,48]
# leng(numerosAbundantesMenores(10**6)) == 247545
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return sum(divisores1(n))
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_numerosAbundantesMenores(n: int) -> None:
assert numerosAbundantesMenores1(n) == numerosAbundantesMenores2(n)
Capítulo 2. Definiciones por comprensión 163
# La comprobación es
# src> poetry run pytest -q numeros_abundantes_menores_o_iguales_que_n.py
# 1 passed in 1.54s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('len(numerosAbundantesMenores1(10**4))')
# 2.21 segundos
# >>> tiempo('len(numerosAbundantesMenores2(10**4))')
# 0.55 segundos
#
# >>> tiempo('len(numerosAbundantesMenores2(10**5))')
# 5.96 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- anterior por cada una de las del ejercicio anterior se obtiene una
-- nueva definición de todosPares. La usada en la definición anterior es
-- la menos eficiente y la que se usa en la siguiente definición es la
-- más eficiente.
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_todosPares :: Positive Integer -> Bool
prop_todosPares (Positive n) =
all (== todosPares1 n)
[todosPares2 n,
todosPares3 n,
todosPares4 n,
todosPares5 n]
-- La comprobación es
-- λ> quickCheck prop_todosPares
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> todosPares1 (10^3)
-- False
-- (0.22 secs, 91,257,744 bytes)
-- λ> todosPares2 (10^3)
-- False
-- (0.01 secs, 2,535,656 bytes)
-- λ> todosPares3 (10^3)
-- False
-- (0.03 secs, 11,530,528 bytes)
-- λ> todosPares4 (10^3)
-- False
-- (0.24 secs, 91,231,144 bytes)
-- λ> todosPares5 (10^3)
-- False
-- (0.22 secs, 91,231,208 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
Capítulo 2. Definiciones por comprensión 167
# 1ª solución
# ===========
# numerosAbundantesMenores(48) == [12,18,20,24,30,36,40,42,48]
def numerosAbundantesMenores1(n: int) -> list[int]:
return [x for x in range(1, n + 1) if numeroAbundante1(x)]
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=1000))
def test_todosPares(n: int) -> None:
Capítulo 2. Definiciones por comprensión 169
# La comprobación es
# src> poetry run pytest -q todos_los_abundantes_hasta_n_son_pares.py
# 1 passed in 2.63s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('todosPares1(1000)')
# 0.03 segundos
# >>> tiempo('todosPares2(1000)')
# 0.05 segundos
# >>> tiempo('todosPares3(1000)')
# 0.02 segundos
#
# >>> tiempo('todosPares1(10000)')
# 2.07 segundos
# >>> tiempo('todosPares2(10000)')
# 0.47 segundos
# >>> tiempo('todosPares3(10000)')
# 2.42 segundos
-- 1ª solución
-- ===========
abundantesImpares1 :: [Integer]
abundantesImpares1 = [x | x <- [1,3..], numeroAbundante1 x]
-- 2ª solución
-- ===========
abundantesImpares2 :: [Integer]
abundantesImpares2 = filter numeroAbundante1 [1,3..]
Capítulo 2. Definiciones por comprensión 171
-- 3ª solución
-- ===========
abundantesImpares3 :: [Integer]
abundantesImpares3 = filter numeroAbundante3 [1,3..]
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_abundantesImpares :: Positive Int -> Bool
prop_abundantesImpares (Positive n) =
all (== take n abundantesImpares1)
[take n abundantesImpares2,
take n abundantesImpares3]
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=10}) prop_abundantesImpares
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> abundantesImpares1 !! 5
-- 4095
-- (2.07 secs, 841,525,368 bytes)
172 Ejercicios de programación con Python
-- λ> abundantesImpares2 !! 5
-- 4095
-- (2.06 secs, 841,443,112 bytes)
-- λ> abundantesImpares3 !! 5
-- 4095
-- (0.01 secs, 550,776 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# abundantesImpares : (int) -> list[int]
# tal que abundantesImpares(n) son los números abundantes impares
# menores que n. Por ejemplo,
# >>> abundantesImpares1(10000)[:12]
# [945, 1575, 2205, 2835, 3465, 4095, 4725, 5355, 5775, 5985, 6435, 6615]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
#
# Sustituyendo la definición de numeroAbundante1 de las soluciones
# anteriores por cada una de las del ejercicio ”Números abundantes”
# https://bit.ly/3xSlWDU se obtiene una nueva definición de abundantes
# impares. La usada en las definiciones anteriores es la menos
# eficiente y la que se usa en la siguiente definición es la más eficiente.
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_abundantesImpares(n: int) -> None:
174 Ejercicios de programación con Python
r = abundantesImpares1(n)
assert abundantesImpares2(n) == r
assert abundantesImpares3(n) == r
# La comprobación es
# src> poetry run pytest -q numeros_abundantes_impares.py
# 1 passed in 1.42s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('abundantesImpares1(10000)[5]')
# 1.25 segundos
# >>> tiempo('abundantesImpares2(10000)[5]')
# 1.22 segundos
# >>> tiempo('abundantesImpares3(10000)[5]')
# 0.33 segundos
-- 1ª solución
-- ===========
-- 2ª solución --
-- ===========
-- 3ª solución --
-- ===========
-- 4ª solución --
-- ===========
-- 5ª solución --
-- ===========
-- 6ª solución --
-- ===========
-- 7ª solución --
-- ===========
-- Comprobación de equivalencia
-- ============================
Capítulo 2. Definiciones por comprensión 177
-- La propiedad es
prop_euler1 :: Positive Integer -> Bool
prop_euler1 (Positive n) =
all (== euler1a n)
[euler1b n,
euler1c n,
euler1d n,
euler1e n,
euler1f n,
euler1g n]
-- La comprobación es
-- λ> quickCheck prop_euler1
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> euler1a (5*10^4)
-- 583291668
-- (0.05 secs, 21,895,296 bytes)
-- λ> euler1b (5*10^4)
-- 583291668
-- (0.05 secs, 26,055,096 bytes)
-- λ> euler1c (5*10^4)
-- 583291668
-- (0.01 secs, 5,586,072 bytes)
-- λ> euler1d (5*10^4)
-- 583291668
-- (2.83 secs, 7,922,304 bytes)
-- λ> euler1e (5*10^4)
-- 583291668
-- (4.56 secs, 12,787,705,248 bytes)
-- λ> euler1f (5*10^4)
-- 583291668
-- (0.01 secs, 8,168,584 bytes)
-- λ> euler1g (5*10^4)
-- 583291668
178 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# euler1 : (int) -> int
# tal que euler1(n) es la suma de todos los múltiplos de 3 ó 5 menores
# que n. Por ejemplo,
# euler1(10) == 23
# euler1(10**2) == 2318
# euler1(10**3) == 233168
# euler1(10**4) == 23331668
# euler1(10**5) == 2333316668
Capítulo 2. Definiciones por comprensión 179
# euler1(10**10) == 23333333331666666668
# euler1(10**20) == 2333333333333333333316666666666666666668
#
# Nota: Este ejercicio está basado en el problema 1 del Proyecto Euler
# https://projecteuler.net/problem=1
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución --
# ===========
# 3ª solución --
# ===========
# 4ª solución --
# ===========
# 5ª solución --
# ===========
# 6ª solución --
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_euler1(n: int) -> None:
r = euler1a(n)
assert euler1b(n) == r
assert euler1c(n) == r
assert euler1d(n) == r
assert euler1e(n) == r
Capítulo 2. Definiciones por comprensión 181
# La comprobación es
# src> poetry run pytest -q suma_de_multiplos_de_3_o_5.py
# 1 passed in 0.16s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('euler1a(10**7)')
# 1.49 segundos
# >>> tiempo('euler1b(10**7)')
# 0.93 segundos
# >>> tiempo('euler1c(10**7)')
# 0.07 segundos
# >>> tiempo('euler1d(10**7)')
# 0.42 segundos
# >>> tiempo('euler1e(10**7)')
# 0.69 segundos
# >>> tiempo('euler1f(10**7)')
# 0.00 segundos
#
# >>> tiempo('euler1c(10**8)')
# 0.72 segundos
# >>> tiempo('euler1f(10**8)')
# 0.00 segundos
-- (0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2),(3,0)
--
-- Definir la función
-- circulo :: Int -> Int
-- tal que (circulo n) es el la cantidad de pares de números naturales
-- (x,y) que se encuentran en el círculo de radio n. Por ejemplo,
-- circulo 1 == 3
-- circulo 2 == 6
-- circulo 3 == 11
-- circulo 4 == 17
-- circulo 100 == 7955
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_circulo :: Positive Int -> Bool
prop_circulo (Positive n) =
circulo1 n == circulo2 n
-- La comprobación es
-- λ> quickCheck prop_circulo
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> circulo1 (2*10^3)
-- 3143587
-- (3.58 secs, 1,744,162,600 bytes)
-- λ> circulo2 (2*10^3)
-- 3143587
-- (0.41 secs, 266,374,208 bytes)
En Python
# ---------------------------------------------------------------------
# En el círculo de radio 2 hay 6 puntos cuyas coordenadas son puntos
# naturales:
# (0,0),(0,1),(0,2),(1,0),(1,1),(2,0)
# y en de radio 3 hay 11:
# (0,0),(0,1),(0,2),(0,3),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2),(3,0)
#
# Definir la función
# circulo : (int) -> int
# tal que circulo(n) es el la cantidad de pares de números naturales
# (x,y) que se encuentran en el círculo de radio n. Por ejemplo,
# circulo(1) == 3
184 Ejercicios de programación con Python
# circulo(2) == 6
# circulo(3) == 11
# circulo(4) == 17
# circulo(100) == 7955
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=100))
def test_circulo(n: int) -> None:
r = circulo1(n)
assert circulo2(n) == r
assert circulo3(n) == r
assert circulo4(n) == r
# La comprobación es
# src> poetry run pytest -q puntos_dentro_del_circulo.py
# 1 passed in 0.60s
# Comparación de eficiencia
# =========================
# La comparación es
186 Ejercicios de programación con Python
# >>> tiempo('circulo1(2000)')
# 0.71 segundos
# >>> tiempo('circulo2(2000)')
# 0.76 segundos
# >>> tiempo('circulo3(2000)')
# 2.63 segundos
# >>> tiempo('circulo4(2000)')
# 1.06 segundos
import Test.QuickCheck
Capítulo 2. Definiciones por comprensión 187
-- 1ª definición de aproxE
-- =======================
-- 2ª definición de aproxE
-- =======================
-- 3ª definición de aproxE
-- =======================
-- 4ª definición de aproxE
-- =======================
-- 5ª definición de aproxE
-- =======================
-- La propiedad es
prop_aproxE :: Positive Int -> Bool
prop_aproxE (Positive k) =
all (== aproxE1 k)
[aproxE2 k,
aproxE3 k,
aproxE4 k,
aproxE5 k]
-- La comprobación es
-- λ> quickCheck prop_aproxE
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> last (aproxE1 (2*10^4))
-- 2.718213874533619
-- (0.04 secs, 5,368,968 bytes)
-- λ> last (aproxE2 (2*10^4))
-- 2.718213874533619
-- (5.93 secs, 17,514,767,104 bytes)
-- λ> last (aproxE3 (2*10^4))
-- 2.718213874533619
-- (0.05 secs, 9,529,336 bytes)
-- λ> last (aproxE4 (2*10^4))
-- 2.718213874533619
-- (0.05 secs, 9,529,184 bytes)
-- λ> last (aproxE5 (2*10^4))
-- 2.718213874533619
-- (0.01 secs, 4,888,960 bytes)
--
-- λ> last (aproxE1 (2*10^6))
-- 2.7182811492688552
-- (0.54 secs, 480,570,120 bytes)
-- λ> last (aproxE3 (2*10^6))
-- 2.7182811492688552
-- (2.07 secs, 896,570,280 bytes)
Capítulo 2. Definiciones por comprensión 189
-- 1ª definición de errorAproxE
-- ============================
-- 2ª definición de errorAproxE
-- ============================
-- 3ª definición de errorAproxE
-- ============================
-- La propiedad es
prop_errorAproxE :: Positive Double -> Bool
prop_errorAproxE (Positive x) =
all (== errorAproxE1 x)
[errorAproxE2 x,
errorAproxE3 x]
-- La comprobación es
190 Ejercicios de programación con Python
-- La comparación es
-- λ> errorAproxE1 0.000001
-- 1358611
-- (1.70 secs, 674,424,552 bytes)
-- λ> errorAproxE2 0.000001
-- 1358611
-- (1.79 secs, 739,637,704 bytes)
-- λ> errorAproxE3 0.000001
-- 1358611
-- (1.20 secs, 609,211,144 bytes)
En Python
# ---------------------------------------------------------------------
# El [número e](https://bit.ly/3y17R7l) se define como el límite de la
# sucesión (1+1/n)**n; es decir,
# e = lim (1+1/n)**n
#
# Definir las funciones
# aproxE : (int) -> list[float]
# errorAproxE : (float) -> int
# tales que
# + aproxE(k) es la lista de los k primeros términos de la sucesión
# (1+1/n)**m. Por ejemplo,
# aproxE(4) == [2.0, 2.25, 2.37037037037037, 2.44140625]
# aproxE6(7*10**7)[-1] == 2.7182818287372563
# + errorE(x) es el menor número de términos de la sucesión
# (1+1/m)**m necesarios para obtener su límite con un error menor que
# x. Por ejemplo,
# errorAproxE(0.1) == 13
# errorAproxE(0.01) == 135
# errorAproxE(0.001) == 1359
# ---------------------------------------------------------------------
Capítulo 2. Definiciones por comprensión 191
setrecursionlimit(10**6)
# 1ª definición de aproxE
# =======================
# 2ª definición de aproxE
# =======================
# 3ª definición de aproxE
# =======================
return list(reversed(aux(n)))
# 4ª definición de aproxE
# =======================
return aux([], n)
# 5ª definición de aproxE
# =======================
# 6ª definición de aproxE
# =======================
# La propiedad es
@given(st.integers(min_value=1, max_value=100))
def test_aproxE(n: int) -> None:
r = aproxE1(n)
assert aproxE2(n) == r
assert aproxE3(n) == r
assert aproxE4(n) == r
assert aproxE5(n) == r
assert aproxE6(n) == r
# La comprobación es
# src> poetry run pytest -q aproximacion_del_numero_e.py
# 1 passed in 0.60s
# ===================================
# La comparación es
# >>> tiempo('aproxE1(20000)')
# 0.00 segundos
# >>> tiempo('aproxE2(20000)')
# 0.43 segundos
# >>> tiempo('aproxE3(20000)')
# 0.60 segundos
# >>> tiempo('aproxE4(20000)')
# 1.23 segundos
# >>> tiempo('aproxE5(20000)')
# 0.00 segundos
# >>> tiempo('aproxE6(20000)')
# 0.00 segundos
# >>> tiempo('aproxE1(10**7)')
# 1.18 segundos
# >>> tiempo('aproxE5(10**7)')
# 1.48 segundos
# >>> tiempo('aproxE6(10**7)')
# 1.43 segundos
# 1ª definición de errorAproxE
# ============================
# # 2ª definición de errorAproxE
# # ============================
return aux(1)
# 3ª definición de errorAproxE
# ============================
@given(st.integers(min_value=1, max_value=100))
def test_errorAproxE(n: int) -> None:
r = errorAproxE1(n)
assert errorAproxE2(n) == r
assert errorAproxE3(n) == r
# La comprobación es
# src> poetry run pytest -q aproximacion_del_numero_e.py
# 2 passed in 0.60s
# La comparación es
# >>> tiempo('errorAproxE1(0.0001)')
# 0.00 segundos
# >>> tiempo('errorAproxE2(0.0001)')
# 0.00 segundos
# >>> tiempo('errorAproxE3(0.0001)')
# 0.00 segundos
#
# >>> tiempo('errorAproxE1(0.0000001)')
# 2.48 segundos
# >>> tiempo('errorAproxE3(0.0000001)')
# 2.61 segundos
import Test.QuickCheck
-- 1ª definición de aproxLimSeno
-- =============================
-- 2ª definición de aproxLimSeno
-- =============================
-- 3ª definición de aproxLimSeno
-- =============================
-- 4ª definición de aproxLimSeno
-- =============================
-- 5ª definición de aproxLimSeno
-- =============================
-- La propiedad es
prop_aproxLimSeno :: Positive Int -> Bool
prop_aproxLimSeno (Positive k) =
all (== aproxLimSeno1 k)
[aproxLimSeno2 k,
aproxLimSeno3 k,
aproxLimSeno4 k,
aproxLimSeno5 k]
-- La comprobación es
-- λ> quickCheck prop_aproxLimSeno
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> last (aproxLimSeno1 (2*10^4))
-- 0.9999999995833334
-- (0.01 secs, 5,415,816 bytes)
-- λ> last (aproxLimSeno2 (2*10^4))
-- 0.9999999995833334
-- (4.48 secs, 17,514,768,064 bytes)
-- λ> last (aproxLimSeno3 (2*10^4))
-- 0.9999999995833334
-- (0.02 secs, 9,530,120 bytes)
-- λ> last (aproxLimSeno4 (2*10^4))
-- 0.9999999995833334
-- (0.02 secs, 9,529,968 bytes)
-- λ> last (aproxLimSeno5 (2*10^4))
-- 0.9999999995833334
-- (0.01 secs, 4,889,720 bytes)
--
-- λ> last (aproxLimSeno1 (2*10^6))
198 Ejercicios de programación con Python
-- 0.9999999999999583
-- (0.46 secs, 480,569,808 bytes)
-- λ> last (aproxLimSeno3 (2*10^6))
-- 0.9999999999999583
-- (1.96 secs, 896,569,992 bytes)
-- λ> last (aproxLimSeno4 (2*10^6))
-- 0.9999999999999583
-- (1.93 secs, 896,570,048 bytes)
-- λ> last (aproxLimSeno5 (2*10^6))
-- 0.9999999999999583
-- (0.05 secs, 432,569,800 bytes)
--
-- λ> last (aproxLimSeno1 (10^7))
-- 0.9999999999999983
-- (2.26 secs, 2,400,569,760 bytes)
-- λ> last (aproxLimSeno5 (10^7))
-- 0.9999999999999983
-- (0.24 secs, 2,160,569,752 bytes)
-- 1ª definición de errorLimSeno
-- =============================
-- 2ª definición de errorLimSeno
-- =============================
-- 3ª definición de errorLimSeno
-- =============================
-- La propiedad es
prop_errorLimSeno :: Positive Double -> Bool
prop_errorLimSeno (Positive x) =
all (== errorLimSeno1 x)
[errorLimSeno2 x,
errorLimSeno3 x]
-- La comprobación es
-- λ> quickCheck prop_errorLimSeno
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> errorLimSeno1 (10**(-12))
-- 408230
-- (0.41 secs, 206,300,808 bytes)
-- λ> errorLimSeno2 (10**(-12))
-- 408230
-- (0.46 secs, 225,895,672 bytes)
-- λ> errorLimSeno3 (10**(-12))
-- 408230
-- (0.37 secs, 186,705,688 bytes)
En Python
# ---------------------------------------------------------------------
# El limite de sen(x)/x, cuando x tiende a cero, se puede calcular como
# el límite de la sucesión sen(1/n)/(1/n), cuando n tiende a infinito.
#
# Definir las funciones
# aproxLimSeno : (int) -> list[float]
# errorLimSeno : (float) -> int
# tales que
# + aproxLimSeno(n) es la lista cuyos elementos son los n primeros
200 Ejercicios de programación con Python
setrecursionlimit(10**6)
# 1ª definición de aproxLimSeno
# =============================
# 2ª definición de aproxLimSeno
# =============================
# 3ª definición de aproxLimSeno
# =============================
Capítulo 2. Definiciones por comprensión 201
return list(reversed(aux(n)))
# 4ª definición de aproxLimSeno
# =============================
return aux([], n)
# 5ª definición de aproxLimSeno
# =============================
# 6ª definición de aproxLimSeno
# =============================
# La propiedad es
@given(st.integers(min_value=1, max_value=100))
def test_aproxLimSeno(n: int) -> None:
202 Ejercicios de programación con Python
r = aproxLimSeno1(n)
assert aproxLimSeno2(n) == r
assert aproxLimSeno3(n) == r
assert aproxLimSeno4(n) == r
assert aproxLimSeno5(n) == r
assert aproxLimSeno6(n) == r
# La comprobación es
# src> poetry run pytest -q limite_del_seno.py
# 1 passed in 0.60s
# La comparación es
# >>> tiempo('aproxLimSeno1(3*10**5)')
# 0.03 segundos
# >>> tiempo('aproxLimSeno2(3*10**5)')
# Process Python violación de segmento (core dumped)
# >>> tiempo('aproxLimSeno3(3*10**5)')
# Process Python violación de segmento (core dumped)
# >>> tiempo('aproxLimSeno4(3*10**5)')
# Process Python violación de segmento (core dumped)
# >>> tiempo('aproxLimSeno5(3*10**5)')
# 0.04 segundos
# >>> tiempo('aproxLimSeno6(3*10**5)')
# 0.07 segundos
#
# >>> tiempo('aproxLimSeno1(10**7)')
# 1.29 segundos
# >>> tiempo('aproxLimSeno5(10**7)')
# 1.40 segundos
# >>> tiempo('aproxLimSeno6(10**7)')
# 1.45 segundos
Capítulo 2. Definiciones por comprensión 203
# 1ª definición de errorLimSeno
# ============================
# 2ª definición de errorLimSeno
# ============================
return aux(1)
# 3ª definición de errorLimSeno
# =============================
@given(st.integers(min_value=1, max_value=100))
def test_errorLimSeno(n: int) -> None:
r = errorLimSeno1(n)
assert errorLimSeno2(n) == r
assert errorLimSeno3(n) == r
# La comprobación es
# src> poetry run pytest -q limite_del_seno.py
# 2 passed in 0.60s
# La comparación es
# >>> tiempo('errorLimSeno1(10**(-12))')
# 0.07 segundos
# >>> tiempo('errorLimSeno2(10**(-12))')
# Process Python violación de segmento (core dumped)
# >>> tiempo('errorLimSeno3(10**(-12))')
# 0.10 segundos
-- calculaPi 3 == 2.8952380952380956
-- calculaPi 300 == 3.1449149035588526
-- + (errorPi x) es el menor número de términos de la serie
-- necesarios para obtener pi con un error menor que x. Por ejemplo,
-- errorPi 0.1 == 9
-- errorPi 0.01 == 99
-- errorPi 0.001 == 999
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- 1ª definición de calculaPi
-- ==========================
-- 2ª definición de calculaPi
-- ==========================
-- 3ª definición de calculaPi
-- ==========================
-- La propiedad es
206 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_calculaPi
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> calculaPi1 (10^6)
-- 3.1415936535887745
-- (1.31 secs, 609,797,408 bytes)
-- λ> calculaPi2 (10^6)
-- 3.1415936535887745
-- (1.68 secs, 723,032,272 bytes)
-- λ> calculaPi3 (10^6)
-- 3.1415936535887745
-- (2.22 secs, 1,099,032,608 bytes)
-- 1ª definición de errorPi
-- ========================
-- 2ª definición de errorPi
-- ========================
-- La propiedad es
prop_errorPi :: Positive Double -> Bool
prop_errorPi (Positive x) =
errorPi1 x == errorPi2 x
-- La comprobación es
-- λ> quickCheck prop_errorPi
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> errorPi1 0.0005
-- 1999
-- (1.88 secs, 1,189,226,384 bytes)
-- λ> errorPi2 0.0005
-- 1999
-- (1.87 secs, 1,213,341,096 bytes)
En Python
# ---------------------------------------------------------------------
# El número π puede calcularse con la [fórmula de
# Leibniz](https://bit.ly/3ERCwZd)
# π/4 = 1 - 1/3 + 1/5 - 1/7 + ...+ (-1)**n/(2*n+1) + ...
#
# Definir las funciones
# calculaPi : (int) -> float
# errorPi : (float) -> int
# tales que
# + calculaPi(n) es la aproximación del número π calculada
# mediante la expresión
# 4*(1 - 1/3 + 1/5 - 1/7 + ...+ (-1)**n/(2*n+1))
# Por ejemplo,
# calculaPi(3) == 2.8952380952380956
208 Ejercicios de programación con Python
# calculaPi(300) == 3.1449149035588526
# + errorPi(x) es el menor número de términos de la serie
# necesarios para obtener pi con un error menor que x. Por ejemplo,
# errorPi(0.1) == 9
# errorPi(0.01) == 99
# errorPi(0.001) == 999
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª definición de calculaPi
# ==========================
# 2ª definición de calculaPi
# ==========================
# 3ª definición de calculaPi
# ==========================
return 4 * r
# La propiedad es
@given(st.integers(min_value=1, max_value=100))
def test_calculaPi(n: int) -> None:
r = calculaPi1(n)
assert calculaPi2(n) == r
assert calculaPi3(n) == r
# La comprobación es
# src> poetry run pytest -q calculo_de_pi_mediante_la_formula_de_Leibniz.py
# 1 passed in 0.14s
# La comparación es
# >>> tiempo('calculaPi1(10**6)')
# 0.37 segundos
# >>> tiempo('calculaPi2(10**6)')
# Process Python violación de segmento (core dumped)
# >>> tiempo('calculaPi3(10**6)')
# 0.39 segundos
# 1ª definición de errorPi
# ========================
i = 1
while True:
yield i
i += 1
# 2ª definición de errorPi
# ========================
return aux(1)
@given(st.integers(min_value=1, max_value=100))
def test_errorPi(n: int) -> None:
assert errorPi1(n) == errorPi2(n)
# La comprobación es
# src> poetry run pytest -q calculo_de_pi_mediante_la_formula_de_Leibniz.py
# 2 passed in 0.60s
# La comparación es
# >>> tiempo('errorPi1(0.0005)')
# 0.63 segundos
# >>> tiempo('errorPi2(0.0005)')
# 0.58 segundos
Capítulo 2. Definiciones por comprensión 211
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
212 Ejercicios de programación con Python
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_pitagoricas :: Positive Int -> Bool
prop_pitagoricas (Positive n) =
all (== pitagoricas1 n)
[pitagoricas2 n,
pitagoricas3 n]
-- La comprobación es
-- λ> quickCheck prop_pitagoricas
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (pitagoricas1 200)
-- 127
-- (12.25 secs, 12,680,320,400 bytes)
-- λ> length (pitagoricas2 200)
-- 127
-- (1.61 secs, 1,679,376,824 bytes)
-- λ> length (pitagoricas3 200)
-- 127
-- (0.06 secs, 55,837,072 bytes)
Capítulo 2. Definiciones por comprensión 213
En Python
# ---------------------------------------------------------------------
# Una terna (x,y,z) de enteros positivos es pitagórica si x^2 + y^2 =
# z^2 y x < y < z.
#
# Definir, por comprensión, la función
# pitagoricas : (int) -> list[tuple[int,int,int]]
# tal que pitagoricas(n) es la lista de todas las ternas pitagóricas
# cuyas componentes están entre 1 y n. Por ejemplo,
# pitagoricas(10) == [(3, 4, 5), (6, 8, 10)]
# pitagoricas(15) == [(3, 4, 5), (5, 12, 13), (6, 8, 10), (9, 12, 15)]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
214 Ejercicios de programación con Python
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=50))
def test_pitagoricas(n: int) -> None:
r = pitagoricas1(n)
assert pitagoricas2(n) == r
assert pitagoricas3(n) == r
# La comprobación es
# src> poetry run pytest -q ternas_pitagoricas.py
# 1 passed in 1.83s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('pitagoricas1(200)')
# 4.76 segundos
# >>> tiempo('pitagoricas2(200)')
# 0.69 segundos
# >>> tiempo('pitagoricas3(200)')
# 0.02 segundos
Capítulo 2. Definiciones por comprensión 215
-- 1ª solución --
-- ===========
-- 2ª solución --
-- ===========
a^2+b^2 == c^2]
-- 3ª solución --
-- ===========
-- La propiedad es
prop_ternasPitagoricas :: Positive Integer -> Bool
prop_ternasPitagoricas (Positive x) =
all (== (ternasPitagoricas1 x))
[ternasPitagoricas2 x,
ternasPitagoricas3 x]
-- La comprobación es
-- λ> quickCheck prop_ternasPitagoricas
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
Capítulo 2. Definiciones por comprensión 217
-- La comparación es
-- λ> ternasPitagoricas1 200
-- [(40,75,85)]
-- (1.90 secs, 2,404,800,856 bytes)
-- λ> ternasPitagoricas2 200
-- [(40,75,85)]
-- (0.06 secs, 19,334,232 bytes)
-- λ> ternasPitagoricas3 200
-- [(40,75,85)]
-- (0.01 secs, 994,224 bytes)
--
-- λ> ternasPitagoricas2 3000
-- [(500,1200,1300),(600,1125,1275),(750,1000,1250)]
-- (4.41 secs, 4,354,148,136 bytes)
-- λ> ternasPitagoricas3 3000
-- [(500,1200,1300),(600,1125,1275),(750,1000,1250)]
-- (0.05 secs, 17,110,360 bytes)
En Python
# ---------------------------------------------------------------------
# Una terna pitagórica es una terna de números naturales (a,b,c) tal
# que a<b<c y a^2+b^2=c^2. Por ejemplo (3,4,5) es una terna pitagórica.
#
# Definir la función
# ternasPitagoricas : (int) -> list[tuple[int, int, int]]
# tal que ternasPitagoricas(x) es la lista de las ternas pitagóricas
# cuya suma es x. Por ejemplo,
# ternasPitagoricas(12) == [(3, 4, 5)]
# ternasPitagoricas(60) == [(10, 24, 26), (15, 20, 25)]
# ternasPitagoricas(10**6) == [(218750, 360000, 421250),
# (200000, 375000, 425000)]
# ---------------------------------------------------------------------
# 1ª solución --
# ===========
# 2ª solución --
# ===========
# 3ª solución --
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=50))
def test_ternasPitagoricas(n: int) -> None:
r = set(ternasPitagoricas1(n))
assert set(ternasPitagoricas2(n)) == r
assert set(ternasPitagoricas3(n)) == r
# La comprobación es
# src> poetry run pytest -q ternas_pitagoricas_con_suma_dada.py
# 1 passed in 0.35s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('ternasPitagoricas1(300)')
# 2.83 segundos
# >>> tiempo('ternasPitagoricas2(300)')
# 0.01 segundos
# >>> tiempo('ternasPitagoricas3(300)')
# 0.00 segundos
#
# >>> tiempo('ternasPitagoricas2(3000)')
# 1.48 segundos
# >>> tiempo('ternasPitagoricas3(3000)')
# 0.02 segundos
220 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
Capítulo 2. Definiciones por comprensión 221
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_productoEscalar :: [Integer] -> [Integer] -> Bool
prop_productoEscalar xs ys =
all (== productoEscalar1 xs ys)
[productoEscalar2 xs ys,
productoEscalar3 xs ys,
productoEscalar4 xs ys,
productoEscalar5 xs ys]
-- La comprobación es
-- λ> quickCheck prop_productoEscalar
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> productoEscalar1 (replicate (2*10^6) 1) (replicate (2*10^6) 1)
-- 2000000
-- (1.37 secs, 803,827,520 bytes)
-- λ> productoEscalar2 (replicate (2*10^6) 1) (replicate (2*10^6) 1)
-- 2000000
-- (0.69 secs, 611,008,272 bytes)
222 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# El producto escalar de dos listas de enteros xs y ys de longitud n
# viene dado por la suma de los productos de los elementos
# correspondientes.
#
# Definir la función
# productoEscalar : (list[int], list[int]) -> int
# tal que productoEscalar(xs, ys) es el producto escalar de las listas
# xs e ys. Por ejemplo,
# productoEscalar([1, 2, 3], [4, 5, 6]) == 32
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(min_value=1, max_value=100)),
st.lists(st.integers(min_value=1, max_value=100)))
def test_productoEscalar(xs: list[int], ys: list[int]) -> None:
224 Ejercicios de programación con Python
r = productoEscalar1(xs, ys)
assert productoEscalar2(xs, ys) == r
assert productoEscalar3(xs, ys) == r
n = min(len(xs), len(ys))
xs1 = xs[:n]
ys1 = ys[:n]
assert productoEscalar4(xs1, ys1) == r
# La comprobación es
# src> poetry run pytest -q producto_escalar.py
# 1 passed in 0.37s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('productoEscalar1([1]*(10**4), [1]*(10**4))')
# 0.00 segundos
# >>> tiempo('productoEscalar3([1]*(10**4), [1]*(10**4))')
# 0.55 segundos
#
# >>> tiempo('productoEscalar1([1]*(10**7), [1]*(10**7))')
# 0.60 segundos
# >>> tiempo('productoEscalar2([1]*(10**7), [1]*(10**7))')
# 0.26 segundos
# >>> tiempo('productoEscalar4([1]*(10**7), [1]*(10**7))')
# 1.73 segundos
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_densa :: NonEmptyList Int -> Bool
prop_densa (NonEmpty xs) =
all (== densa1 xs)
[densa2 xs,
densa3 xs,
densa4 xs]
-- La comprobación es
-- λ> quickCheck prop_densa
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> last (densa1 [1..2*10^6])
-- (0,2000000)
-- (0.95 secs, 880,569,400 bytes)
-- λ> last (densa2 [1..2*10^6])
-- (0,2000000)
-- (0.52 secs, 800,569,432 bytes)
-- λ> last (densa3 [1..2*10^6])
-- (0,2000000)
-- (0.53 secs, 752,569,552 bytes)
Capítulo 2. Definiciones por comprensión 227
En Python
# ---------------------------------------------------------------------
# Los polinomios pueden representarse de forma dispersa o densa. Por
# ejemplo, el polinomio 6x^4-5x^2+4x-7 se puede representar de forma
# dispersa por [6,0,-5,4,-7] y de forma densa por
# [(4,6),(2,-5),(1,4),(0,-7)].
#
# Definir la función
# densa : (list[int]) -> list[tuple[int, int]]
# tal que densa(xs) es la representación densa del polinomio cuya
# representación dispersa es xs. Por ejemplo,
# densa([6, 0, -5, 4, -7]) == [(4, 6), (2, -5), (1, 4), (0, -7)]
# densa([6, 0, 0, 3, 0, 4]) == [(5, 6), (2, 3), (0, 4)]
# densa([0]) == [(0, 0)]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
228 Ejercicios de programación con Python
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(), min_size=1))
def test_densa(xs: list[int]) -> None:
r = densa1(xs)
assert densa2(xs) == r
assert densa3(xs) == r
# La comprobación es
Capítulo 2. Definiciones por comprensión 229
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('densa1(range(1, 10**4))')
# 0.00 segundos
# >>> tiempo('densa2(range(1, 10**4))')
# 0.00 segundos
# >>> tiempo('densa3(range(1, 10**4))')
# 0.25 segundos
#
# >>> tiempo('densa1(range(1, 10**7))')
# 1.87 segundos
# >>> tiempo('densa2(range(1, 10**7))')
# 2.15 segundos
-- (”Quevedo”,”Literatura”,1580,1654),
-- (”Goya”,”Pintura”,1746,1828),
-- (”Einstein”,”Ciencia”,1879,1955),
-- (”Mozart”,”Musica”,1756,1791),
-- (”Botticelli”,”Pintura”,1445,1510),
-- (”Borromini”,”Arquitectura”,1599,1667),
-- (”Bach”,”Musica”,1685,1750)]
--
-- Definir las funciones
-- nombres :: [(String,String,Int,Int)] -> [String]
-- musicos :: [(String,String,Int,Int)] -> [String]
-- seleccion :: [(String,String,Int,Int)] -> String -> [String]
-- musicos' :: [(String,String,Int,Int)] -> [String]
-- vivas :: [(String,String,Int,Int)] -> Int -> [String]
-- tales que
-- + (nombres bd) es la lista de los nombres de las personas de la- base
-- de datos bd. Por ejemplo,
-- λ> nombres personas
-- [”Cervantes”,”Velazquez”,”Picasso”,”Beethoven”,”Poincare”,
-- ”Quevedo”,”Goya”,”Einstein”,”Mozart”,”Botticelli”,”Borromini”,
-- ”Bach”]
-- + (musicos bd) es la lista de los nombres de los músicos de la base
-- de datos bd. Por ejemplo,
-- musicos personas == [”Beethoven”,”Mozart”,”Bach”]
-- + (seleccion bd m) es la lista de los nombres de las personas de la
-- base de datos bd cuya actividad es m. Por ejemplo,
-- λ> seleccion personas ”Pintura”
-- [”Velazquez”,”Picasso”,”Goya”,”Botticelli”]
-- λ> seleccion personas ”Musica”
-- [”Beethoven”,”Mozart”,”Bach”]
-- + (musicos' bd) es la lista de los nombres de los músicos de la base
-- de datos bd. Por ejemplo,
-- musicos' personas == [”Beethoven”,”Mozart”,”Bach”]
-- + (vivas bd a) es la lista de los nombres de las personas de la base
-- de datos bd que estaban vivas en el año a. Por ejemplo,
-- λ> vivas personas 1600
-- [”Cervantes”,”Velazquez”,”Quevedo”,”Borromini”]
-- ---------------------------------------------------------------------
personas :: [(String,String,Int,Int)]
personas = [(”Cervantes”,”Literatura”,1547,1616),
(”Velazquez”,”Pintura”,1599,1660),
(”Picasso”,”Pintura”,1881,1973),
(”Beethoven”,”Musica”,1770,1823),
(”Poincare”,”Ciencia”,1854,1912),
(”Quevedo”,”Literatura”,1580,1654),
(”Goya”,”Pintura”,1746,1828),
(”Einstein”,”Ciencia”,1879,1955),
(”Mozart”,”Musica”,1756,1791),
(”Botticelli”,”Pintura”,1445,1510),
(”Borromini”,”Arquitectura”,1599,1667),
(”Bach”,”Musica”,1685,1750)]
En Python
# ---------------------------------------------------------------------
# Las bases de datos sobre actividades de personas pueden representarse
# mediante listas de elementos de la forma (a,b,c,d), donde a es el
# nombre de la persona, b su actividad, c su fecha de nacimiento y d la
# de su fallecimiento. Un ejemplo es la siguiente que usaremos a lo
# largo de este ejercicio,
# BD = list[tuple[str, str, int, int]]
#
232 Ejercicios de programación con Python
# personas: BD = [
# (”Cervantes”, ”Literatura”, 1547, 1616),
# (”Velazquez”, ”Pintura”, 1599, 1660),
# (”Picasso”, ”Pintura”, 1881, 1973),
# (”Beethoven”, ”Musica”, 1770, 1823),
# (”Poincare”, ”Ciencia”, 1854, 1912),
# (”Quevedo”, ”Literatura”, 1580, 1654),
# (”Goya”, ”Pintura”, 1746, 1828),
# (”Einstein”, ”Ciencia”, 1879, 1955),
# (”Mozart”, ”Musica”, 1756, 1791),
# (”Botticelli”, ”Pintura”, 1445, 1510),
# (”Borromini”, ”Arquitectura”, 1599, 1667),
# (”Bach”, ”Musica”, 1685, 1750)]
#
# Definir las funciones
# nombres : (BD) -> list[str]
# musicos : (BD) -> list[str]
# seleccion : (BD, str) -> list[str]
# musicos2 : (BD) -> list[str]
# vivas : (BD, int) -> list[str]
# tales que
# + nombres(bd) es la lista de los nombres de las personas de la- base
# de datos bd. Por ejemplo,
# >>> nombres(personas)
# ['Cervantes','Velazquez','Picasso','Beethoven','Poincare',
# 'Quevedo','Goya','Einstein','Mozart','Botticelli','Borromini',
# 'Bach']
# + musicos(bd) es la lista de los nombres de los músicos de la base
# de datos bd. Por ejemplo,
# musicos(personas) == ['Beethoven','Mozart','Bach']
# + seleccion(bd, m) es la lista de los nombres de las personas de la
# base de datos bd cuya actividad es m. Por ejemplo,
# >>> seleccion(personas, 'Pintura')
# ['Velazquez', 'Picasso', 'Goya', 'Botticelli']
# >>> seleccion(personas, 'Musica')
# ['Beethoven', 'Mozart', 'Bach']
# + musicos2(bd) es la lista de los nombres de los músicos de la base
# de datos bd. Por ejemplo,
# musicos2(personas) == ['Beethoven','Mozart','Bach']
# + vivas(bd, a) es la lista de los nombres de las personas de la base
Capítulo 2. Definiciones por comprensión 233
personas: BD = [
(”Cervantes”, ”Literatura”, 1547, 1616),
(”Velazquez”, ”Pintura”, 1599, 1660),
(”Picasso”, ”Pintura”, 1881, 1973),
(”Beethoven”, ”Musica”, 1770, 1823),
(”Poincare”, ”Ciencia”, 1854, 1912),
(”Quevedo”, ”Literatura”, 1580, 1654),
(”Goya”, ”Pintura”, 1746, 1828),
(”Einstein”, ”Ciencia”, 1879, 1955),
(”Mozart”, ”Musica”, 1756, 1791),
(”Botticelli”, ”Pintura”, 1445, 1510),
(”Borromini”, ”Arquitectura”, 1599, 1667),
(”Bach”, ”Musica”, 1685, 1750)]
Contenido
3.1. Potencia entera . . . . . . . . . . . . . . . . . . . . . . . . . . .223
3.2. Algoritmo de Euclides del mcd . . . . . . . . . . . . . . . . . .230
3.3. Dígitos de un número . . . . . . . . . . . . . . . . . . . . . . .232
3.4. Suma de los digitos de un número . . . . . . . . . . . . . . .240
3.5. Número a partir de sus dígitos . . . . . . . . . . . . . . . . . .245
3.6. Exponente de la mayor potencia de x que divide a y . . . .251
3.7. Producto cartesiano de dos conjuntos . . . . . . . . . . . . .256
3.8. Subconjuntos de un conjunto . . . . . . . . . . . . . . . . . .260
3.9. El algoritmo de Luhn . . . . . . . . . . . . . . . . . . . . . . .266
3.10. Números de Lychrel . . . . . . . . . . . . . . . . . . . . . . . .271
3.11. Suma de los dígitos de una cadena . . . . . . . . . . . . . . .282
3.12. Primera en mayúscula y restantes en minúscula . . . . . . .286
3.13. Mayúsculas iniciales . . . . . . . . . . . . . . . . . . . . . . . .290
3.14. Posiciones de un carácter en una cadena . . . . . . . . . . .295
3.15. Reconocimiento de subcadenas . . . . . . . . . . . . . . . . .298
1
https://jaalonso.github.io/materias/PFconHaskell/temas/tema-6.html
235
236 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
Capítulo 3. Definiciones por recursión 237
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- 9ª solución
-- ===========
238 Ejercicios de programación con Python
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_potencia :: Integer -> NonNegative Integer -> Bool
prop_potencia m (NonNegative n) =
all (== potencia1 m n)
[potencia2 m n,
potencia3 m n,
potencia4 m n,
potencia5 m n,
potencia6 m n,
potencia7 m n,
potencia8 m n,
potencia9 m n]
-- La comprobación es
-- λ> quickCheck prop_potencia
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (show (potencia1 2 (2*10^5)))
-- 60206
-- (2.97 secs, 2,602,252,408 bytes)
-- λ> length (show (potencia2 2 (2*10^5)))
-- 60206
-- (2.63 secs, 2,624,652,624 bytes)
-- λ> length (show (potencia3 2 (2*10^5)))
-- 60206
-- (3.41 secs, 2,619,606,368 bytes)
-- λ> length (show (potencia4 2 (2*10^5)))
-- 60206
-- (0.64 secs, 2,636,888,928 bytes)
-- λ> length (show (potencia5 2 (2*10^5)))
Capítulo 3. Definiciones por recursión 239
-- 60206
-- (2.47 secs, 2,597,108,000 bytes)
-- λ> length (show (potencia6 2 (2*10^5)))
-- 60206
-- (0.35 secs, 2,582,488,824 bytes)
-- λ> length (show (potencia7 2 (2*10^5)))
-- 60206
-- (2.48 secs, 2,616,406,272 bytes)
-- λ> length (show (potencia8 2 (2*10^5)))
-- 60206
-- (2.40 secs, 2,608,652,736 bytes)
-- λ> length (show (potencia9 2 (2*10^5)))
-- 60206
-- (0.01 secs, 4,212,968 bytes)
--
-- λ> length (show (potencia4 2 (10^6)))
-- 301030
-- (10.39 secs, 63,963,999,656 bytes)
-- λ> length (show (potencia6 2 (10^6)))
-- 301030
-- (8.90 secs, 63,691,999,552 bytes)
-- λ> length (show (potencia9 2 (10^6)))
-- 301030
-- (0.04 secs, 19,362,032 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# potencia : (int, int) -> int
# tal que potencia(x, n) es x elevado al número natural n. Por ejemplo,
# potencia(2, 3) == 8
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(),
st.integers(min_value=0, max_value=100))
def test_potencia(m: int, n: int) -> None:
r = potencia1(m, n)
assert potencia2(m, n) == r
assert potencia3(m, n) == r
assert potencia4(m, n) == r
assert potencia5(m, n) == r
assert potencia6(m, n) == r
# La comprobación es
# src> poetry run pytest -q potencia_entera.py
# 1 passed in 0.17s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('potencia1(2, 2*10**4)')
# 0.01 segundos
# >>> tiempo('potencia2(2, 2*10**4)')
# 0.01 segundos
# >>> tiempo('potencia3(2, 2*10**4)')
# 0.02 segundos
# >>> tiempo('potencia4(2, 2*10**4)')
# 0.01 segundos
# >>> tiempo('potencia5(2, 2*10**4)')
# 0.01 segundos
# >>> tiempo('potencia6(2, 2*10**4)')
# 0.00 segundos
#
# >>> tiempo('potencia4(2, 5*10**5)')
# 2.87 segundos
# >>> tiempo('potencia5(2, 5*10**5)')
# 3.17 segundos
# >>> tiempo('potencia6(2, 5*10**5)')
# 0.00 segundos
-- mcd 30 45 == 15
--
-- Comprobar con QuickCheck que el máximo común divisor de dos números a
-- y b (ambos mayores que 0) es siempre mayor o igual que 1 y además es
-- menor o igual que el menor de los números a y b.
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- La propiedad es
prop_mcd :: Positive Integer -> Positive Integer -> Bool
prop_mcd (Positive a) (Positive b) =
m >= 1 && m <= min a b
where m = mcd a b
-- La comprobación es
-- λ> quickCheck prop_mcd
-- OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Dados dos números naturales, a y b, es posible calcular su máximo
# común divisor mediante el Algoritmo de Euclides. Este algoritmo se
# puede resumir en la siguiente fórmula:
# mcd(a,b) = a, si b = 0
# = mcd (b, a módulo b), si b > 0
#
# Definir la función
# mcd : (int, nt) -> int
# tal que mcd(a, b) es el máximo común divisor de a y b calculado
# mediante el algoritmo de Euclides. Por ejemplo,
# mcd(30, 45) == 15
# mcd(45, 30) == 15
244 Ejercicios de programación con Python
#
# Comprobar con Hypothesis que el máximo común divisor de dos números a
# y b (ambos mayores que 0) es siempre mayor o igual que 1 y además es
# menor o igual que el menor de los números a y b.
# ---------------------------------------------------------------------
# La propiedad es
@given(st.integers(min_value=1, max_value=1000),
st.integers(min_value=1, max_value=1000))
def test_mcd(a: int, b: int) -> None:
assert 1 <= mcd(a, b) <= min(a, b)
# La comprobación es
# src> poetry run pytest -q algoritmo_de_Euclides_del_mcd.py
# 1 passed in 0.22s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- 9ª solución
-- ===========
-- 10ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
Capítulo 3. Definiciones por recursión 247
-- La propiedad es
prop_digitos :: NonNegative Integer -> Bool
prop_digitos (NonNegative n) =
all (== digitos1 n)
[digitos2 n,
digitos3 n,
digitos4 n,
digitos5 n,
digitos6 n,
digitos7 n,
digitos8 n,
digitos9 n,
digitos10 n]
-- La comprobación es
-- λ> quickCheck prop_digitos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> n = product [1..5000]
-- λ> length (digitos1 n)
-- 16326
-- (3.00 secs, 11,701,450,912 bytes)
-- λ> length (digitos2 n)
-- 16326
-- (0.13 secs, 83,393,816 bytes)
-- λ> length (digitos3 n)
-- 16326
-- (0.11 secs, 83,132,552 bytes)
-- λ> length (digitos4 n)
-- 16326
-- (0.01 secs, 23,054,920 bytes)
-- λ> length (digitos5 n)
-- 16326
-- (0.01 secs, 22,663,088 bytes)
-- λ> length (digitos6 n)
248 Ejercicios de programación con Python
-- 16326
-- (0.06 secs, 22,663,224 bytes)
-- λ> length (digitos7 n)
-- 16326
-- (0.01 secs, 22,663,064 bytes)
-- λ> length (digitos8 n)
-- 16326
-- (0.03 secs, 22,663,192 bytes)
-- λ> length (digitos9 n)
-- 16326
-- (0.05 secs, 82,609,944 bytes)
-- λ> length (digitos10 n)
-- 16326
-- (0.01 secs, 26,295,416 bytes)
--
-- λ> n = product [1..5*10^4]
-- λ> length (digitos2 n)
-- 213237
-- (10.17 secs, 12,143,633,056 bytes)
-- λ> length (digitos3 n)
-- 213237
-- (10.54 secs, 12,140,221,216 bytes)
-- λ> length (digitos4 n)
-- 213237
-- (1.29 secs, 2,638,199,328 bytes)
-- λ> length (digitos5 n)
-- 213237
-- (2.48 secs, 2,633,081,632 bytes)
-- λ> length (digitos6 n)
-- 213237
-- (2.59 secs, 2,633,081,600 bytes)
-- λ> length (digitos7 n)
-- 213237
-- (2.55 secs, 2,633,081,608 bytes)
-- λ> length (digitos8 n)
-- 213237
-- (2.49 secs, 2,633,081,600 bytes)
-- λ> length (digitos9 n)
-- 213237
-- (7.07 secs, 12,133,397,456 bytes)
Capítulo 3. Definiciones por recursión 249
En Python
# ---------------------------------------------------------------------
# Definir la función
# digitos : (int) -> list[int]
# tal que digitos(n) es la lista de los dígitos del número n. Por
# ejemplo,
# digitos(320274) == [3, 2, 0, 2, 7, 4]
# ---------------------------------------------------------------------
# pylint: disable=unused-import
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# 7ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1))
def test_digitos(n: int) -> None:
r = digitos1(n)
assert digitos2(n) == r
assert digitos3(n) == r
assert digitos4(n) == r
assert digitos5(n) == r
assert digitos6(n) == r
assert digitos7(n) == r
# La comprobación es
# src> poetry run pytest -q digitos_de_un_numero.py
# 1 passed in 0.49s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('digitos1(factorial(6000))')
# 0.58 segundos
# >>> tiempo('digitos2(factorial(6000))')
# 0.01 segundos
# >>> tiempo('digitos3(factorial(6000))')
# 0.01 segundos
# >>> tiempo('digitos4(factorial(6000))')
# 0.01 segundos
# >>> tiempo('digitos5(factorial(6000))')
# 0.60 segundos
# >>> tiempo('digitos6(factorial(6000))')
# 0.17 segundos
252 Ejercicios de programación con Python
# >>> tiempo('digitos7(factorial(6000))')
# 0.10 segundos
#
# >>> tiempo('digitos2(factorial(2*10**4))')
# 0.10 segundos
# >>> tiempo('digitos3(factorial(2*10**4))')
# 0.10 segundos
# >>> tiempo('digitos4(factorial(2*10**4))')
# 0.09 segundos
# >>> tiempo('digitos6(factorial(2*10**4))')
# 2.33 segundos
# >>> tiempo('digitos7(factorial(2*10**4))')
# 1.18 segundos
#
# >>> tiempo('digitos2(factorial(10**5))')
# 3.53 segundos
# >>> tiempo('digitos3(factorial(10**5))')
# 3.22 segundos
# >>> tiempo('digitos4(factorial(10**5))')
# 3.02 segundos
-- 1ª solución
Capítulo 3. Definiciones por recursión 253
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
254 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_sumaDigitos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sumaDigitos1 (product [1..2*10^4])
-- 325494
-- (0.64 secs, 665,965,832 bytes)
-- λ> sumaDigitos2 (product [1..2*10^4])
-- 325494
-- (0.41 secs, 660,579,064 bytes)
-- λ> sumaDigitos3 (product [1..2*10^4])
-- 325494
-- (1.58 secs, 1,647,082,224 bytes)
-- λ> sumaDigitos4 (product [1..2*10^4])
-- 325494
-- (1.72 secs, 1,662,177,792 bytes)
--
-- λ> sumaDigitos1 (product [1..5*10^4])
-- 903555
-- (2.51 secs, 3,411,722,136 bytes)
-- λ> sumaDigitos2 (product [1..5*10^4])
-- 903555
-- (2.30 secs, 3,396,802,856 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaDigitos : (int) -> int
Capítulo 3. Definiciones por recursión 255
# pylint: disable=unused-import
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
256 Ejercicios de programación con Python
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=1000))
def test_sumaDigitos(n: int) -> None:
r = sumaDigitos1(n)
assert sumaDigitos2(n) == r
assert sumaDigitos3(n) == r
assert sumaDigitos4(n) == r
assert sumaDigitos5(n) == r
# La comprobación es
# src> poetry run pytest -q suma_de_los_digitos_de_un_numero.py
Capítulo 3. Definiciones por recursión 257
# 1 passed in 0.35s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('sumaDigitos1(factorial(6*10**3))')
# 0.01 segundos
# >>> tiempo('sumaDigitos2(factorial(6*10**3))')
# 0.01 segundos
# >>> tiempo('sumaDigitos3(factorial(6*10**3))')
# 0.13 segundos
# >>> tiempo('sumaDigitos4(factorial(6*10**3))')
# 0.13 segundos
# >>> tiempo('sumaDigitos5(factorial(6*10**3))')
# 0.01 segundos
#
# >>> tiempo('sumaDigitos1(factorial(10**5))')
# 2.20 segundos
# >>> tiempo('sumaDigitos2(factorial(10**5))')
# 2.22 segundos
# >>> tiempo('sumaDigitos5(factorial(10**5))')
# 2.19 segundos
-- listaNumero [0,0,1] == 1
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 7ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_listaNumero :: NonEmptyList Integer -> Bool
prop_listaNumero (NonEmpty xs) =
all (== listaNumero1 ys)
[listaNumero2 ys,
listaNumero3 ys,
260 Ejercicios de programación con Python
listaNumero4 ys,
listaNumero5 ys,
listaNumero6 ys,
listaNumero7 ys,
listaNumero8 ys]
where ys = map (`mod` 10) xs
-- La comprobación es
-- λ> quickCheck prop_listaNumero
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (show (listaNumero1 (replicate (10^5) 9)))
-- 100000
-- (4.01 secs, 4,309,740,064 bytes)
-- λ> length (show (listaNumero2 (replicate (10^5) 9)))
-- 100000
-- (4.04 secs, 4,307,268,856 bytes)
-- λ> length (show (listaNumero3 (replicate (10^5) 9)))
-- 100000
-- (4.08 secs, 4,300,868,816 bytes)
-- λ> length (show (listaNumero4 (replicate (10^5) 9)))
-- 100000
-- (0.42 secs, 4,288,480,208 bytes)
-- λ> length (show (listaNumero4 (replicate (10^5) 9)))
-- 100000
-- (0.41 secs, 4,288,480,208 bytes)
-- λ> length (show (listaNumero5 (replicate (10^5) 9)))
-- 100000
-- (43.35 secs, 10,702,827,328 bytes)
-- λ> length (show (listaNumero6 (replicate (10^5) 9)))
-- 100000
-- (46.89 secs, 10,693,227,280 bytes)
-- λ> length (show (listaNumero7 (replicate (10^5) 9)))
-- 100000
-- (4.33 secs, 4,297,499,344 bytes)
-- λ> length (show (listaNumero8 (replicate (10^5) 9)))
Capítulo 3. Definiciones por recursión 261
-- 100000
-- (0.03 secs, 60,760,360 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# listaNumero : (list[int]) -> int
# tal que listaNumero(xs) es el número formado por los dígitos xs. Por
# ejemplo,
# listaNumero([5]) == 5
# listaNumero([1, 3, 4, 7]) == 1347
# listaNumero([0, 0, 1]) == 1
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# 6ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(min_value=0, max_value=9), min_size=1))
def test_listaNumero(xs: list[int]) -> None:
r = listaNumero1(xs)
assert listaNumero2(xs) == r
Capítulo 3. Definiciones por recursión 263
assert listaNumero3(xs) == r
assert listaNumero4(xs) == r
assert listaNumero5(xs) == r
assert listaNumero6(xs) == r
# La comprobación es
# src> poetry run pytest -q numero_a_partir_de_sus_digitos.py
# 1 passed in 0.27s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('listaNumero1([9]*(10**4))')
# 0.28 segundos
# >>> tiempo('listaNumero2([9]*(10**4))')
# 0.16 segundos
# >>> tiempo('listaNumero3([9]*(10**4))')
# 0.01 segundos
# >>> tiempo('listaNumero4([9]*(10**4))')
# 0.01 segundos
# >>> tiempo('listaNumero5([9]*(10**4))')
# 0.41 segundos
# >>> tiempo('listaNumero6([9]*(10**4))')
# 0.00 segundos
#
# >>> tiempo('listaNumero3([9]*(2*10**5))')
# 3.45 segundos
# >>> tiempo('listaNumero4([9]*(2*10**5))')
# 3.29 segundos
# >>> tiempo('listaNumero6([9]*(2*10**5))')
# 0.19 segundos
264 Ejercicios de programación con Python
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_mayorExponente :: Integer -> Integer -> Property
prop_mayorExponente a b =
a > 1 && b > 0 ==>
all (== mayorExponente1 a b)
[mayorExponente2 a b,
mayorExponente3 a b,
mayorExponente4 a b]
-- La comprobación es
-- λ> quickCheck prop_mayorExponente
-- +++ OK, passed 100 tests; 457 discarded.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> mayorExponente1 2 (2^(5*10^4))
-- 50000
-- (0.12 secs, 179,578,424 bytes)
-- λ> mayorExponente2 2 (2^(5*10^4))
266 Ejercicios de programación con Python
-- 50000
-- (0.13 secs, 181,533,376 bytes)
-- λ> mayorExponente3 2 (2^(5*10^4))
-- 50000
-- (3.88 secs, 818,319,096 bytes)
-- λ> mayorExponente4 2 (2^(5*10^4))
-- 50000
-- (0.13 secs, 181,133,344 bytes)
--
-- λ> mayorExponente1 2 (2^(3*10^5))
-- 300000
-- (2.94 secs, 5,762,199,064 bytes)
-- λ> mayorExponente2 2 (2^(3*10^5))
-- 300000
-- (2.91 secs, 5,773,829,624 bytes)
-- λ> mayorExponente4 2 (2^(3*10^5))
-- 300000
-- (3.70 secs, 5,771,396,824 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# mayorExponente : (int, int) -> int
# tal que mayorExponente(a, b) es el exponente de la mayor potencia de
# a que divide b. Por ejemplo,
# mayorExponente(2, 8) == 3
# mayorExponente(2, 9) == 0
# mayorExponente(5, 100) == 2
# mayorExponente(2, 60) == 2
#
# Nota: Se supone que a > 1 y b > 0.
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
268 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=10),
st.integers(min_value=1, max_value=10))
def test_mayorExponente(a: int, b: int) -> None:
r = mayorExponente1(a, b)
assert mayorExponente2(a, b) == r
assert mayorExponente3(a, b) == r
assert mayorExponente4(a, b) == r
# La comprobación es
# src> poetry run pytest -q exponente_mayor.py
# 1 passed in 0.16s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('mayorExponente1(2, 2**(2*10**4))')
# 0.13 segundos
Capítulo 3. Definiciones por recursión 269
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
270 Ejercicios de programación con Python
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_producto :: [Int] -> [Int] -> Bool
prop_producto xs ys =
producto1 xs ys `iguales` producto2 xs ys
-- La comprobación es
-- λ> quickCheck prop_producto
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (producto1 [1..4000] [1..4000])
-- 16000000
-- (2.33 secs, 1,537,551,208 bytes)
Capítulo 3. Definiciones por recursión 271
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_elementos_producto :: [Int] -> [Int] -> Bool
prop_elementos_producto xs ys =
length (producto1 xs ys) == length xs * length ys
-- La comprobación es
-- λ> quickCheck prop_elementos_producto
-- +++ OK, passed 100 tests.
En Python
# -------------------------------------------------------------------
# Definir la función
# producto : (list[A], list[B]) -> list[tuple[(A, B)]]
# tal que producto(xs, ys) es el producto cartesiano de xs e ys. Por
# ejemplo,
# producto([1, 3], [2, 4]) == [(1, 2), (1, 4), (3, 2), (3, 4)]
#
# Comprobar con Hypothesis que el número de elementos de (producto xs
# ys) es el producto del número de elementos de xs y de ys.
# -------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
B = TypeVar('B')
272 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_producto(xs: list[int], ys: list[int]) -> None:
assert sorted(producto1(xs, ys)) == sorted(producto2(xs, ys))
# La comprobación es
# src> poetry run pytest -q producto_cartesiano_de_dos_conjuntos.py
# 1 passed in 0.31s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('len(producto1(range(0, 1000), range(0, 500)))')
# 0.03 segundos
# >>> tiempo('len(producto2(range(0, 1000), range(0, 500)))')
Capítulo 3. Definiciones por recursión 273
# 2.58 segundos
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_elementos_producto(xs: list[int], ys: list[int]) -> None:
assert len(producto1(xs, ys)) == len(xs) * len(ys)
# La comprobación es
# src> poetry run pytest -q producto_cartesiano_de_dos_conjuntos.py
# 2 passed in 0.48s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_subconjuntos :: [Int] -> Bool
prop_subconjuntos xs =
all (== sort (subconjuntos1 xs))
[sort (subconjuntos2 xs),
sort (subconjuntos3 xs)]
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=7}) prop_subconjuntos
-- +++ OK, passed 100 tests.
Capítulo 3. Definiciones por recursión 275
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (subconjuntos1 [1..23])
-- 8388608
-- (2.05 secs, 1,476,991,840 bytes)
-- λ> length (subconjuntos2 [1..23])
-- 8388608
-- (0.87 secs, 1,208,555,312 bytes)
-- λ> length (subconjuntos3 [1..23])
-- 8388608
-- (0.09 secs, 873,006,608 bytes)
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_length_subconjuntos :: [Int] -> Bool
prop_length_subconjuntos xs =
length (subconjuntos1 xs) == 2 ^ length xs
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=7}) prop_length_subconjuntos
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# subconjuntos : (list[A]) -> list[list[A]]
# tal que subconjuntos(xs) es la lista de las subconjuntos de la lista
# xs. Por ejemplo,
# >>> subconjuntos([2, 3, 4])
# [[2,3,4], [2,3], [2,4], [2], [3,4], [3], [4], []]
# >>> subconjuntos([1, 2, 3, 4])
# [[1,2,3,4], [1,2,3], [1,2,4], [1,2], [1,3,4], [1,3], [1,4], [1],
# [2,3,4], [2,3], [2,4], [2], [3,4], [3], [4], []]
#
276 Ejercicios de programación con Python
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(), max_size=5))
def test_subconjuntos(xs: list[int]) -> None:
ys = list(set(xs))
r = sorted([sorted(zs) for zs in subconjuntos1(ys)])
assert sorted([sorted(zs) for zs in subconjuntos2(ys)]) == r
assert sorted([sorted(zs) for zs in subconjuntos3(ys)]) == r
assert sorted([sorted(zs) for zs in subconjuntos4(ys)]) == r
# La comprobación es
# src> poetry run pytest -q subconjuntos_de_un_conjunto.py
# 1 passed in 0.89s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('subconjuntos1(range(14))')
# 0.00 segundos
# >>> tiempo('subconjuntos2(range(14))')
# 0.00 segundos
# >>> tiempo('subconjuntos3(range(14))')
278 Ejercicios de programación con Python
# 6.01 segundos
# >>> tiempo('subconjuntos4(range(14))')
# 0.00 segundos
#
# >>> tiempo('subconjuntos1(range(23))')
# 1.95 segundos
# >>> tiempo('subconjuntos2(range(23))')
# 2.27 segundos
# >>> tiempo('subconjuntos4(range(23))')
# 1.62 segundos
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.lists(st.integers(), max_size=7))
def test_length_subconjuntos(xs: list[int]) -> None:
assert len(subconjuntos1(xs)) == 2 ** len(xs)
# La comprobación es
# src> poetry run pytest -q subconjuntos_de_un_conjunto.py
# 2 passed in 0.95s
-- ---------------------------------------------------------------------
-- El objetivo de este ejercicio es estudiar un algoritmo para validar
-- algunos identificadores numéricos como los números de algunas tarjetas
-- de crédito; por ejemplo, las de tipo Visa o Master Card.
--
-- El algoritmo que vamos a estudiar es el [algoritmo de
-- Luhn](https://bit.ly/3DX1llv) consistente en aplicar los siguientes
-- pasos a los dígitos del número de la tarjeta.
-- 1. Se invierten los dígitos del número; por ejemplo, [9,4,5,5] se
-- transforma en [5,5,4,9].
-- 2. Se duplican los dígitos que se encuentra en posiciones impares
Capítulo 3. Definiciones por recursión 279
-- Definición de digitosInv
-- ========================
-- Definiciones de doblePosImpar
-- =============================
-- 1ª definición
doblePosImpar :: [Integer] -> [Integer]
doblePosImpar [] = []
doblePosImpar [x] = [x]
doblePosImpar (x:y:zs) = x : 2*y : doblePosImpar zs
-- 2ª definición
doblePosImpar2 :: [Integer] -> [Integer]
doblePosImpar2 (x:y:zs) = x : 2*y : doblePosImpar2 zs
doblePosImpar2 xs = xs
-- 3ª definición
doblePosImpar3 :: [Integer] -> [Integer]
doblePosImpar3 xs = [f n x | (n,x) <- zip [0..] xs]
where f n x | odd n = 2*x
| otherwise = x
-- Definiciones de sumaDigitos
-- ===========================
-- Definición de ultimoDigito
-- ==========================
-- Definiciones de luhn
Capítulo 3. Definiciones por recursión 281
-- ====================
-- 1ª definición
luhn1 :: Integer -> Bool
luhn1 n =
ultimoDigito (sumaDigitos (doblePosImpar (digitosInv n))) == 0
-- 2ª definición
luhn2 :: Integer -> Bool
luhn2 =
(==0) . ultimoDigito . sumaDigitos . doblePosImpar . digitosInv
En Python
# ---------------------------------------------------------------------
# El objetivo de este ejercicio es estudiar un algoritmo para validar
# algunos identificadores numéricos como los números de algunas tarjetas
# de crédito; por ejemplo, las de tipo Visa o Master Card.
#
# El algoritmo que vamos a estudiar es el [algoritmo de
# Luhn](https://bit.ly/3DX1llv) consistente en aplicar los siguientes
# pasos a los dígitos del número de la tarjeta.
# 1. Se invierten los dígitos del número; por ejemplo, [9,4,5,5] se
# transforma en [5,5,4,9].
# 2. Se duplican los dígitos que se encuentra en posiciones impares
# (empezando a contar en 0); por ejemplo, [5,5,4,9] se transforma
# en [5,10,4,18].
# 3. Se suman los dígitos de cada número; por ejemplo, [5,10,4,18]
# se transforma en 5 + (1 + 0) + 4 + (1 + 8) = 19.
# 4. Si el último dígito de la suma es 0, el número es válido; y no
# lo es, en caso contrario.
#
# A los números válidos, se les llama números de Luhn.
#
# Definir las siguientes funciones:
# digitosInv : (int) -> list[int]
# doblePosImpar : (list[int]) -> list[int]
# sumaDigitos : (list[int]) -> int
# ultimoDigito : (int) -> int
# luhn : (int) -> bool
282 Ejercicios de programación con Python
# tales que
# + digitosInv(n) es la lista de los dígitos del número n. en orden
# inverso. Por ejemplo,
# digitosInv(320274) == [4,7,2,0,2,3]
# + doblePosImpar(ns) es la lista obtenida doblando los elementos en
# las posiciones impares (empezando a contar en cero y dejando igual
# a los que están en posiciones pares. Por ejemplo,
# doblePosImpar([4,9,5,5]) == [4,18,5,10]
# doblePosImpar([4,9,5,5,7]) == [4,18,5,10,7]
# + sumaDigitos(ns) es la suma de los dígitos de ns. Por ejemplo,
# sumaDigitos([10,5,18,4]) = 1 + 0 + 5 + 1 + 8 + 4 =
# = 19
# + ultimoDigito(n) es el último dígito de n. Por ejemplo,
# ultimoDigito(123) == 3
# ultimoDigito(0) == 0
# + luhn(n) se verifica si n es un número de Luhn. Por ejemplo,
# luhn(5594589764218858) == True
# luhn(1234567898765432) == False
# ---------------------------------------------------------------------
# Definición de digitosInv
# ========================
# Definiciones de doblePosImpar
# =============================
# 1ª definición
def doblePosImpar(xs: list[int]) -> list[int]:
if len(xs) <= 1:
return xs
return [xs[0]] + [2*xs[1]] + doblePosImpar(xs[2:])
# 2ª definición
def doblePosImpar2(xs: list[int]) -> list[int]:
Capítulo 3. Definiciones por recursión 283
# Definiciones de sumaDigitos
# ===========================
# Definición de ultimoDigito
# ==========================
# Definiciones de luhn
# ====================
-- 154+451=605, 605+506=1111
-- + 89, ya que en 24 pasos se obtiene un capicúa.
-- En esta serie de ejercicios vamos a buscar el primer número de
-- Lychrel.
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 1. Definir la función
-- esCapicua :: Integer -> Bool
-- tal que (esCapicua x) se verifica si x es capicúa. Por ejemplo,
-- esCapicua 252 == True
-- esCapicua 253 == False
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 2. Definir la función
-- inverso :: Integer -> Integer
-- tal que (inverso x) es el número obtenido escribiendo las cifras de x
-- en orden inverso. Por ejemplo,
-- inverso 253 == 352
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 3. Definir la función
-- siguiente :: Integer -> Integer
-- tal que (siguiente x) es el número obtenido sumándole a x su
-- inverso. Por ejemplo,
-- siguiente 253 == 605
-- ---------------------------------------------------------------------
Capítulo 3. Definiciones por recursión 285
-- ---------------------------------------------------------------------
-- Ejercicio 4. Definir la función
-- busquedaDeCapicua :: Integer -> [Integer]
-- tal que (busquedaDeCapicua x) es la lista de los números tal que el
-- primero es x, el segundo es (siguiente de x) y así sucesivamente
-- hasta que se alcanza un capicúa. Por ejemplo,
-- busquedaDeCapicua 253 == [253,605,1111]
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 5. Definir la función
-- capicuaFinal :: Integer -> Integer
-- tal que (capicuaFinal x) es la capicúa con la que termina la búsqueda
-- de capicúa a partir de x. Por ejemplo,
-- capicuaFinal 253 == 1111
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 6. Definir la función
-- orden :: Integer -> Integer
-- tal que (orden x) es el número de veces que se repite el proceso de
-- calcular el inverso a partir de x hasta alcanzar un número
-- capicúa. Por ejemplo,
-- orden 253 == 2
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 7. Definir la función
-- ordenMayor :: Integer -> Integer -> Bool
-- tal que (ordenMayor x n) se verifica si el orden de x es mayor o
-- igual que n. Dar la definición sin necesidad de evaluar el orden de
-- x. Por ejemplo,
-- λ> ordenMayor 1186060307891929990 2
-- True
-- λ> orden 1186060307891929990
-- 261
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 8. Definir la función
-- ordenEntre :: Integer -> Integer -> [Integer]
-- tal que (ordenEntre m n) es la lista de los elementos cuyo orden es
-- mayor o igual que m y menor que n. Por ejemplo,
-- take 5 (ordenEntre 10 11) == [829,928,9059,9149,9239]
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 9. Definir la función
-- menorDeOrdenMayor :: Integer -> Integer
-- tal que (menorDeOrdenMayor n) es el menor elemento cuyo orden es
-- mayor que n. Por ejemplo,
-- menorDeOrdenMayor 2 == 19
-- menorDeOrdenMayor 20 == 89
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 10. Definir la función
-- menoresdDeOrdenMayor :: Integer -> [(Integer,Integer)]
-- tal que (menoresdDeOrdenMayor m) es la lista de los pares (n,x) tales
-- que n es un número entre 1 y m y x es el menor elemento de orden
-- mayor que n. Por ejemplo,
-- menoresdDeOrdenMayor 5 == [(1,10),(2,19),(3,59),(4,69),(5,79)]
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 11. A la vista de los resultados de (menoresdDeOrdenMayor 5)
-- conjeturar sobre la última cifra de menorDeOrdenMayor.
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 12. Decidir con QuickCheck la conjetura.
-- ---------------------------------------------------------------------
-- La conjetura es
prop_menorDeOrdenMayor :: Integer -> Property
prop_menorDeOrdenMayor n =
n > 1 ==> menorDeOrdenMayor n `mod` 10 == 9
-- La comprobación es
-- λ> quickCheck prop_menorDeOrdenMayor
-- *** Failed! Falsifiable (after 22 tests and 2 shrinks):
-- 25
-- ---------------------------------------------------------------------
-- Ejercicio 13. Calcular (menoresdDeOrdenMayor 50)
288 Ejercicios de programación con Python
-- ---------------------------------------------------------------------
-- Solución: El cálculo es
-- λ> menoresdDeOrdenMayor 50
-- [(1,10),(2,19),(3,59),(4,69),(5,79),(6,79),(7,89),(8,89),(9,89),
-- (10,89),(11,89),(12,89),(13,89),(14,89),(15,89),(16,89),(17,89),
-- (18,89),(19,89),(20,89),(21,89),(22,89),(23,89),(24,89),(25,196),
-- (26,196),(27,196),(28,196),(29,196),(30,196),(31,196),(32,196),
-- (33,196),(34,196),(35,196),(36,196),(37,196),(38,196),(39,196),
-- (40,196),(41,196),(42,196),(43,196),(44,196),(45,196),(46,196),
-- (47,196),(48,196),(49,196),(50,196)]
-- ---------------------------------------------------------------------
-- Ejercicio 14. A la vista de (menoresdDeOrdenMayor 50), conjeturar el
-- orden de 196.
-- ---------------------------------------------------------------------
-- ---------------------------------------------------------------------
-- Ejercicio 15. Comprobar con QuickCheck la conjetura sobre el orden de
-- 196.
-- ---------------------------------------------------------------------
-- La propiedad es
prop_ordenDe196 :: Integer -> Bool
prop_ordenDe196 n =
ordenMayor 196 n
-- La comprobación es
-- λ> quickCheck prop_ordenDe196
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Un [número de Lychrel](http://bit.ly/2X4DzMf) es un número natural
# para el que nunca se obtiene un capicúa mediante el proceso de
# invertir las cifras y sumar los dos números. Por ejemplo, los
Capítulo 3. Definiciones por recursión 289
setrecursionlimit(10**6)
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# esCapicua : (int) -> bool
# tal que esCapicua(x) se verifica si x es capicúa. Por ejemplo,
# esCapicua(252) == True
# esCapicua(253) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# inverso : (int) -> int
# tal que inverso(x) es el número obtenido escribiendo las cifras de x
# en orden inverso. Por ejemplo,
# inverso(253) == 352
# ---------------------------------------------------------------------
return int(str(x)[::-1])
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# siguiente : (int) -> int
# tal que siguiente(x) es el número obtenido sumándole a x su
# inverso. Por ejemplo,
# siguiente(253) == 605
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# busquedaDeCapicua : (int) -> list[int]
# tal que busquedaDeCapicua(x) es la lista de los números tal que el
# primero es x, el segundo es (siguiente de x) y así sucesivamente
# hasta que se alcanza un capicúa. Por ejemplo,
# busquedaDeCapicua(253) == [253,605,1111]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# capicuaFinal : (int) -> int
# tal que (capicuaFinal x) es la capicúa con la que termina la búsqueda
# de capicúa a partir de x. Por ejemplo,
# capicuaFinal(253) == 1111
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
Capítulo 3. Definiciones por recursión 291
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# ordenMayor : (int, int) -> bool:
# tal que ordenMayor(x, n) se verifica si el orden de x es mayor o
# igual que n. Dar la definición sin necesidad de evaluar el orden de
# x. Por ejemplo,
# >>> ordenMayor(1186060307891929990, 2)
# True
# >>> orden(1186060307891929990)
# 261
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# ordenEntre : (int, int) -> Generator[int, None, None]
# tal que ordenEntre(m, n) es la lista de los elementos cuyo orden es
# mayor o igual que m y menor que n. Por ejemplo,
# >>> list(islice(ordenEntre(10, 11), 5))
# [829, 928, 9059, 9149, 9239]
# ---------------------------------------------------------------------
292 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# menorDeOrdenMayor : (int) -> int
# tal que menorDeOrdenMayor(n) es el menor elemento cuyo orden es
# mayor que n. Por ejemplo,
# menorDeOrdenMayor(2) == 19
# menorDeOrdenMayor(20) == 89
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. Definir la función
# menoresdDeOrdenMayor : (int) -> list[tuple[int, int]]
# tal que (menoresdDeOrdenMayor m) es la lista de los pares (n,x) tales
# que n es un número entre 1 y m y x es el menor elemento de orden
# mayor que n. Por ejemplo,
# menoresdDeOrdenMayor(5) == [(1,10),(2,19),(3,59),(4,69),(5,79)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11. A la vista de los resultados de (menoresdDeOrdenMayor 5)
Capítulo 3. Definiciones por recursión 293
# ---------------------------------------------------------------------
# Ejercicio 12. Decidir con Hypothesis la conjetura.
# ---------------------------------------------------------------------
# La conjetura es
# @given(st.integers(min_value=2, max_value=200))
# def test_menorDeOrdenMayor(n: int) -> None:
# assert menorDeOrdenMayor(n) % 10 == 9
# La comprobación es
# src> poetry run pytest -q numeros_de_Lychrel.py
# E assert (196 % 10) == 9
# E + where 196 = menorDeOrdenMayor(25)
# E Falsifying example: test_menorDeOrdenMayor(
# E n=25,
# E )
# ---------------------------------------------------------------------
# Ejercicio 13. Calcular menoresdDeOrdenMayor(50)
# ---------------------------------------------------------------------
# Solución: El cálculo es
# λ> menoresdDeOrdenMayor 50
# [(1,10),(2,19),(3,59),(4,69),(5,79),(6,79),(7,89),(8,89),(9,89),
# (10,89),(11,89),(12,89),(13,89),(14,89),(15,89),(16,89),(17,89),
# (18,89),(19,89),(20,89),(21,89),(22,89),(23,89),(24,89),(25,196),
# (26,196),(27,196),(28,196),(29,196),(30,196),(31,196),(32,196),
# (33,196),(34,196),(35,196),(36,196),(37,196),(38,196),(39,196),
# (40,196),(41,196),(42,196),(43,196),(44,196),(45,196),(46,196),
# (47,196),(48,196),(49,196),(50,196)]
294 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 14. A la vista de menoresdDeOrdenMayor(50), conjeturar el
# orden de 196.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 15. Comprobar con Hypothesis la conjetura sobre el orden de
# 196.
# ---------------------------------------------------------------------
# La propiedad es
@settings(deadline=None)
@given(st.integers(min_value=2, max_value=5000))
def test_ordenDe196(n: int) -> None:
assert ordenMayor(196, n)
# La comprobación es
# src> poetry run pytest -q numeros_de_Lychrel.py
# 1 passed in 7.74s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_sumaDigitos :: String -> Bool
prop_sumaDigitos xs =
all (== sumaDigitos1 xs)
[sumaDigitos2 xs,
sumaDigitos3 xs,
sumaDigitos4 xs]
296 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_sumaDigitos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sumaDigitos1 (take (4*10^6) (cycle ”ab12”))
-- 3000000
-- (1.92 secs, 819,045,328 bytes)
-- λ> sumaDigitos2 (take (4*10^6) (cycle ”ab12”))
-- 3000000
-- (1.79 secs, 856,419,112 bytes)
-- λ> sumaDigitos3 (take (4*10^6) (cycle ”ab12”))
-- 3000000
-- (0.62 secs, 723,045,296 bytes)
-- λ> sumaDigitos4 (take (4*10^6) (cycle ”ab12”))
-- 3000000
-- (0.63 secs, 723,045,552 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaDigitos : (str) -> int
# tal que sumaDigitos(xs) es la suma de los dígitos de la cadena
# xs. Por ejemplo,
# sumaDigitos(”SE 2431 X”) == 10
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
Capítulo 3. Definiciones por recursión 297
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.text())
def test_sumaDigitos(xs: str) -> None:
r = sumaDigitos1(xs)
assert sumaDigitos2(xs) == r
assert sumaDigitos3(xs) == r
# La comprobación es
# src> poetry run pytest -q suma_de_digitos_de_cadena.py
# 1 passed in 0.41s
298 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('sumaDigitos1(”ab12”*5000)')
# 0.00 segundos
# >>> tiempo('sumaDigitos2(”ab12”*5000)')
# 0.02 segundos
# >>> tiempo('sumaDigitos3(”ab12”*5000)')
# 0.00 segundos
#
# >>> tiempo('sumaDigitos1(”ab12”*(5*10**6))')
# 1.60 segundos
# >>> tiempo('sumaDigitos3(”ab12”*(5*10**6))')
# 1.83 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_mayusculaInicial :: String -> Bool
prop_mayusculaInicial xs =
all (== mayusculaInicial1 xs)
[mayusculaInicial2 xs,
mayusculaInicial3 xs]
-- La comprobación es
-- λ> quickCheck prop_mayusculaInicial
-- +++ OK, passed 100 tests.
300 Ejercicios de programación con Python
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (mayusculaInicial1 (take (10^7) (cycle ”aA”)))
-- 10000000
-- (2.22 secs, 1,680,592,240 bytes)
-- λ> length (mayusculaInicial2 (take (10^7) (cycle ”aA”)))
-- 10000000
-- (2.57 secs, 2,240,592,192 bytes)
-- λ> length (mayusculaInicial3 (take (10^7) (cycle ”aA”)))
-- 10000000
-- (0.16 secs, 1,440,592,192 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# mayusculaInicial : (str) -> str
# tal que mayusculaInicial(xs) es la palabra xs con la letra inicial
# en mayúscula y las restantes en minúsculas. Por ejemplo,
# mayusculaInicial(”sEviLLa”) == ”Sevilla”
# mayusculaInicial(””) == ””
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.text())
def test_mayusculaInicial(xs: str) -> None:
r = mayusculaInicial1(xs)
assert mayusculaInicial2(xs) == r
assert mayusculaInicial3(xs) == r
# La comprobación es
# src> poetry run pytest -q mayuscula_inicial.py
# 1 passed in 0.26s
302 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('len(mayusculaInicial1(”aB”*(10**7)))')
# 1.92 segundos
# >>> tiempo('len(mayusculaInicial2(”aB”*(10**7)))')
# Process Python terminado (killed)
# >>> tiempo('len(mayusculaInicial3(”aB”*(10**7)))')
# 1.59 segundos
# >>> tiempo('len(mayusculaInicial4(”aB”*(10**7)))')
# 0.13 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
304 Ejercicios de programación con Python
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_titulo :: [String] -> Bool
prop_titulo xs =
all (== titulo1 xs)
[titulo2 xs,
titulo3 xs]
-- La comprobación es
-- λ> quickCheck prop_titulo
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (titulo1 (take (10^7) (cycle [”hOy”,”Es”,”juEves”,”dE”,”Noviembre
-- 10000000
-- (2.17 secs, 1,680,592,512 bytes)
-- λ> length (titulo2 (take (10^7) (cycle [”hOy”,”Es”,”juEves”,”dE”,”Noviembre
-- 10000000
-- (2.45 secs, 2,240,592,464 bytes)
-- λ> length (titulo3 (take (10^7) (cycle [”hOy”,”Es”,”juEves”,”dE”,”Noviembre
-- 10000000
-- (0.16 secs, 1,440,592,464 bytes)
En Python
# ---------------------------------------------------------------------
# Se consideran las siguientes reglas de mayúsculas iniciales para los
# títulos:
# + la primera palabra comienza en mayúscula y
# + todas las palabras que tienen 4 letras como mínimo empiezan con
Capítulo 3. Definiciones por recursión 305
# mayúsculas
#
# Definir la función
# titulo : (list[str]) -> list[str]
# tal que titulo(ps) es la lista de las palabras de ps con
# las reglas de mayúsculas iniciales de los títulos. Por ejemplo,
# >>> titulo([”eL”, ”arTE”, ”DE”, ”La”, ”proGraMacion”])
# [”El”, ”Arte”, ”de”, ”la”, ”Programacion”]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.text()))
def test_titulo(ps: list[str]) -> None:
r = titulo1(ps)
assert titulo2(ps) == r
assert titulo3(ps) == r
# La comprobación es
# src> poetry run pytest -q mayusculas_iniciales.py
# 1 passed in 0.55s
# Comparación de eficiencia
# =========================
Capítulo 3. Definiciones por recursión 307
# La comparación es
# >>> tiempo('titulo1([”eL”,”arTE”,”DE”,”La”,”proGraMacion ”]*1900)')
# 0.00 segundos
# >>> tiempo('titulo2([”eL”,”arTE”,”DE”,”La”,”proGraMacion ”]*1900)')
# 0.30 segundos
# >>> tiempo('titulo3([”eL”,”arTE”,”DE”,”La”,”proGraMacion ”]*1900)')
# 0.00 segundos
#
# >>> tiempo('titulo1([”eL”,”arTE”,”DE”,”La”,”proGraMacion ”]*(2*10**6))')
# 2.93 segundos
# >>> tiempo('titulo3([”eL”,”arTE”,”DE”,”La”,”proGraMacion ”]*(2*10**6))')
# 2.35 segundos
-- 1ª solución
-- ===========
308 Ejercicios de programación con Python
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_posiciones :: Char -> String -> Bool
prop_posiciones x ys =
all (== posiciones1 x ys)
[posiciones2 x ys,
posiciones3 x ys]
-- La comprobación es
-- λ> quickCheck prop_posiciones
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (posiciones1 'a' (take (6*10^6) (cycle ”abc”)))
-- 2000000
-- (2.48 secs, 1,680,591,672 bytes)
Capítulo 3. Definiciones por recursión 309
En Python
# ---------------------------------------------------------------------
# Definir la función
# posiciones : (str, str) -> list[int]
# tal que (posiciones x ys) es la lista de la posiciones del carácter x
# en la cadena ys. Por ejemplo,
# posiciones('a', ”Salamamca”) == [1,3,5,8]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# -- 1ª solución
# -- ===========
# -- 2ª solución
# -- ===========
return []
return aux(x, ys, 0)
# -- 3ª solución
# -- ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.text(), st.text())
def test_posiciones(x: str, ys: str) -> None:
r = posiciones1(x, ys)
assert posiciones2(x, ys) == r
assert posiciones3(x, ys) == r
# La comprobación es
# src> poetry run pytest -q posiciones_de_un_caracter_en_una_cadena.py
# 1 passed in 0.29s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('posiciones1(”a”, ”abc”*6000)')
# 0.00 segundos
# >>> tiempo('posiciones2(”a”, ”abc”*6000)')
# 0.06 segundos
Capítulo 3. Definiciones por recursión 311
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
312 Ejercicios de programación con Python
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- 6ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_esSubcadena :: String -> String -> Bool
Capítulo 3. Definiciones por recursión 313
prop_esSubcadena xs ys =
all (== esSubcadena1 xs ys)
[esSubcadena2 xs ys,
esSubcadena3 xs ys,
esSubcadena4 xs ys,
esSubcadena5 xs ys,
esSubcadena6 xs ys]
-- La comprobación es
-- λ> quickCheck prop_esSubcadena
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> esSubcadena1 ”abc” (replicate (5*10^4) 'd' ++ ”abc”)
-- True
-- (0.03 secs, 17,789,392 bytes)
-- λ> esSubcadena2 ”abc” (replicate (5*10^4) 'd' ++ ”abc”)
-- True
-- (6.32 secs, 24,989,912 bytes)
--
-- λ> esSubcadena1 ”abc” (replicate (5*10^6) 'd' ++ ”abc”)
-- True
-- (3.24 secs, 1,720,589,432 bytes)
-- λ> esSubcadena3 ”abc” (replicate (5*10^6) 'd' ++ ”abc”)
-- True
-- (1.81 secs, 1,720,589,656 bytes)
-- λ> esSubcadena4 ”abc” (replicate (5*10^6) 'd' ++ ”abc”)
-- True
-- (0.71 secs, 1,120,589,480 bytes)
-- λ> esSubcadena5 ”abc” (replicate (5*10^6) 'd' ++ ”abc”)
-- True
-- (0.41 secs, 1,120,589,584 bytes)
-- λ> esSubcadena6 ”abc” (replicate (5*10^6) 'd' ++ ”abc”)
-- True
-- (0.11 secs, 560,589,200 bytes)
314 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# esSubcadena : (str, str) -> bool
# tal que esSubcadena(xs ys) se verifica si xs es una subcadena de ys.
# Por ejemplo,
# esSubcadena(”casa”, ”escasamente”) == True
# esSubcadena(”cante”, ”escasamente”) == False
# esSubcadena(””, ””) == True
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.text(), st.text())
def test_esSubcadena(xs: str, ys: str) -> None:
r = esSubcadena1(xs, ys)
assert esSubcadena2(xs, ys) == r
assert esSubcadena3(xs, ys) == r
# La comprobación es
# src> poetry run pytest -q reconocimiento_de_subcadenas.py
# 1 passed in 0.35s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('esSubcadena1(”abc”, ”d”*(10**4) + ”abc”)')
# 0.02 segundos
# >>> tiempo('esSubcadena2(”abc”, ”d”*(10**4) + ”abc”)')
# 0.01 segundos
# >>> tiempo('esSubcadena3(”abc”, ”d”*(10**4) + ”abc”)')
# 0.00 segundos
#
# >>> tiempo('esSubcadena2(”abc”, ”d”*(10**5) + ”abc”)')
# 1.74 segundos
# >>> tiempo('esSubcadena3(”abc”, ”d”*(10**5) + ”abc”)')
# 0.00 segundos
316 Ejercicios de programación con Python
Capítulo 4
Contenido
4.1. Segmentos cuyos elementos cumplen una propiedad . . . .305
4.2. Elementos consecutivos relacionados . . . . . . . . . . . . .308
4.3. Agrupación de elementos por posición . . . . . . . . . . . . .310
4.4. Concatenación de una lista de listas . . . . . . . . . . . . . .316
4.5. Aplica según propiedad . . . . . . . . . . . . . . . . . . . . . .321
4.6. Máximo de una lista . . . . . . . . . . . . . . . . . . . . . . . .326
317
318 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_segmentos :: (Int -> Bool) -> [Int] -> Bool
prop_segmentos p xs =
all (== segmentos1 p xs)
[segmentos2 p xs,
Capítulo 4. Funciones de orden superior 319
segmentos3 p xs]
-- La comprobación es
-- λ> quickCheck' prop_segmentos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (segmentos1 even [1..5*10^6])
-- 2500000
-- (2.52 secs, 2,080,591,088 bytes)
-- λ> length (segmentos2 even [1..5*10^6])
-- 2500000
-- (0.78 secs, 2,860,591,688 bytes)
-- λ> length (segmentos3 even [1..5*10^6])
-- 2500000
-- (0.82 secs, 2,860,592,000 bytes)
En Python
# ---------------------------------------------------------------------
# Definir la función
# segmentos : (Callable[[A], bool], list[A]) -> list[list[A]]
# tal que segmentos(p, xs) es la lista de los segmentos de xs cuyos
# elementos verifican la propiedad p. Por ejemplo,
# >>> segmentos1((lambda x: x % 2 == 0), [1,2,0,4,9,6,4,5,7,2])
# [[2, 0, 4], [6, 4], [2]]
# >>> segmentos1((lambda x: x % 2 == 1), [1,2,0,4,9,6,4,5,7,2])
# [[1], [9], [5, 7]]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('segmentos1(lambda x: x % 2 == 0, range(10**4))')
# 0.55 segundos
# >>> tiempo('segmentos2(lambda x: x % 2 == 0, range(10**4))')
# 0.00 segundos
Capítulo 4. Funciones de orden superior 321
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
En Python
# ---------------------------------------------------------------------
# Definir la función
# relacionados : (Callable[[A, A], bool], list[A]) -> bool
# tal que relacionados(r, xs) se verifica si para todo par (x,y) de
# elementos consecutivos de xs se cumple la relación r. Por ejemplo,
# >>> relacionados(lambda x, y: x < y, [2, 3, 7, 9])
# True
# >>> relacionados(lambda x, y: x < y, [2, 3, 1, 9])
# False
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_agrupa :: NonEmptyList [Int] -> Bool
prop_agrupa (NonEmpty xss) =
all (== agrupa1 xss)
[agrupa2 xss,
agrupa3 xss]
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (agrupa1 [[1..10^4] | _ <- [1..10^4]])
-- 10000
-- (3.96 secs, 16,012,109,904 bytes)
-- λ> length (agrupa2 [[1..10^4] | _ <- [1..10^4]])
-- 10000
-- (25.80 secs, 19,906,197,528 bytes)
-- λ> length (agrupa3 [[1..10^4] | _ <- [1..10^4]])
-- 10000
-- (9.56 secs, 7,213,797,984 bytes)
-- La comprobación es
Capítulo 4. Funciones de orden superior 325
-- La propiedad es
prop_agrupa_length :: [[Int]] -> Bool
prop_agrupa_length xss =
and [length xs == n | xs <- agrupa1 xss]
where n = length xss
-- La comprobación es
-- λ> quickCheck prop_agrupa_length
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir la función
# agrupa : (list[list[A]]) -> list[list[A]]
# tal que agrupa(xss) es la lista de las listas obtenidas agrupando
# los primeros elementos, los segundos, ... Por ejemplo,
# >>> agrupa([[1,6],[7,8,9],[3,4,5]])
# [[1, 7, 3], [6, 8, 4]]
#
# Comprobar con QuickChek que la longitud de todos los elementos de
# (agrupa xs) es igual a la longitud de xs.
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
326 Ejercicios de programación con Python
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 4. Funciones de orden superior 327
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.lists(st.integers()), min_size=1))
def test_agrupa(xss: list[list[int]]) -> None:
r = agrupa1(xss)
assert agrupa2(xss) == r
assert agrupa3(xss) == r
assert agrupa4(xss) == r
assert agrupa5(xss) == r
# La comprobación es
# src> poetry run pytest -q agrupacion_de_elementos_por_posicion.py
# 1 passed in 0.74s
328 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('agrupa1([list(range(10**3)) for _ in range(10**3)])')
# 4.44 segundos
# >>> tiempo('agrupa2([list(range(10**3)) for _ in range(10**3)])')
# 0.10 segundos
# >>> tiempo('agrupa3([list(range(10**3)) for _ in range(10**3)])')
# 0.10 segundos
# >>> tiempo('agrupa4([list(range(10**3)) for _ in range(10**3)])')
# 0.12 segundos
# >>> tiempo('agrupa5([list(range(10**3)) for _ in range(10**3)])')
# 0.15 segundos
#
# >>> tiempo('agrupa2([list(range(10**4)) for _ in range(10**4)])')
# 21.25 segundos
# >>> tiempo('agrupa3([list(range(10**4)) for _ in range(10**4)])')
# 20.82 segundos
# >>> tiempo('agrupa4([list(range(10**4)) for _ in range(10**4)])')
# 13.46 segundos
# >>> tiempo('agrupa5([list(range(10**4)) for _ in range(10**4)])')
# 21.70 segundos
# La propiedad es
@given(st.lists(st.lists(st.integers()), min_size=1))
def test_agrupa_length(xss: list[list[int]]) -> None:
n = len(xss)
assert all((len(xs) == n for xs in agrupa2(xss)))
# La comprobación es
# src> poetry run pytest -q agrupacion_de_elementos_por_posicion.py
# 2 passed in 1.25s
Capítulo 4. Funciones de orden superior 329
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
330 Ejercicios de programación con Python
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_conc :: [[Int]] -> Bool
prop_conc xss =
all (== conc1 xss)
[conc2 xss,
conc3 xss,
conc4 xss]
-- La comprobación es
-- λ> quickCheck prop_conc
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (conc1 [[1..n] | n <- [1..5000]])
-- 12502500
-- (2.72 secs, 1,802,391,200 bytes)
-- λ> length (conc2 [[1..n] | n <- [1..5000]])
-- 12502500
-- (0.27 secs, 1,602,351,160 bytes)
-- λ> length (conc3 [[1..n] | n <- [1..5000]])
-- 12502500
-- (0.28 secs, 1,602,071,192 bytes)
-- λ> length (conc4 [[1..n] | n <- [1..5000]])
-- 12502500
-- (0.26 secs, 1,602,071,184 bytes)
-- Comprobación de la propiedad
-- ============================
Capítulo 4. Funciones de orden superior 331
-- La propiedad es
prop_long_conc :: [[Int]] -> Bool
prop_long_conc xss =
length (conc1 xss) == sum (map length xss)
-- La comprobación es
-- λ> quickCheck prop_long_conc
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Definir, por recursión, la función
# conc : (list[list[A]]) -> list[A]
# tal que conc(xss) es la concenación de las listas de xss. Por
# ejemplo,
# conc([[1,3],[2,4,6],[1,9]]) == [1,3,2,4,6,1,9]
#
# Comprobar con hypothesis que la longitud de conc(xss) es la suma de
# las longitudes de los elementos de xss.
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(st.lists(st.lists(st.integers()), min_size=1))
def test_conc(xss: list[list[int]]) -> None:
r = conc1(xss)
assert conc2(xss) == r
assert conc3(xss) == r
assert conc4(xss) == r
# La comprobación es
# src> poetry run pytest -q concatenacion_de_una_lista_de_listas.py
# 1 passed in 0.63s
# Comparación de eficiencia
# =========================
Capítulo 4. Funciones de orden superior 333
# La comparación es
# >>> tiempo('conc1([list(range(n)) for n in range(1500)])')
# 0.04 segundos
# >>> tiempo('conc2([list(range(n)) for n in range(1500)])')
# 6.28 segundos
# >>> tiempo('conc3([list(range(n)) for n in range(1500)])')
# 2.55 segundos
# >>> tiempo('conc4([list(range(n)) for n in range(1500)])')
# 0.09 segundos
#
# >>> tiempo('conc1([list(range(n)) for n in range(10000)])')
# 2.01 segundos
# >>> tiempo('conc4([list(range(n)) for n in range(10000)])')
# 2.90 segundos
#
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.lists(st.lists(st.integers()), min_size=1))
def test_long_conc(xss: list[list[int]]) -> None:
assert len(conc1(xss)) == sum(map(len, xss))
# La comprobación es
# src> poetry run pytest -q concatenacion_de_una_lista_de_listas.py
# 2 passed in 0.81s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
Capítulo 4. Funciones de orden superior 335
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_filtraAplica :: (Int -> Int) -> (Int -> Bool) -> [Int] -> Bool
prop_filtraAplica f p xs =
all (== filtraAplica1 f p xs)
[filtraAplica2 f p xs,
filtraAplica3 f p xs,
filtraAplica4 f p xs,
filtraAplica5 f p xs]
-- La comprobación es
-- λ> quickCheck' prop_filtraAplica
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sum (filtraAplica1 id even [1..5*10^6])
-- 6250002500000
-- (2.92 secs, 1,644,678,696 bytes)
-- λ> sum (filtraAplica2 id even [1..5*10^6])
-- 6250002500000
-- (1.17 secs, 1,463,662,848 bytes)
-- λ> sum (filtraAplica3 id even [1..5*10^6])
-- 6250002500000
-- (3.18 secs, 1,964,678,640 bytes)
-- λ> sum (filtraAplica4 id even [1..5*10^6])
-- 6250002500000
-- (2.64 secs, 1,924,678,752 bytes)
-- λ> sum (filtraAplica5 id even [1..5*10^6])
-- 6250002500000
-- (2.61 secs, 1,824,678,712 bytes)
336 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Definir la función
# filtraAplica : (Callable[[A], B], Callable[[A], bool], list[A])
# -> list[B]
# tal que filtraAplica(f, p, xs) es la lista obtenida aplicándole a los
# elementos de xs que cumplen el predicado p la función f. Por ejemplo,
# >>> filtraAplica(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
B = TypeVar('B')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
338 Ejercicios de programación con Python
# La propiedad es
@given(st.lists(st.integers()))
def test_filtraAplica(xs: list[int]) -> None:
def f(x: int) -> int:
return x + 4
def p(x: int) -> bool:
return x < 3
r = filtraAplica1(f, p, xs)
assert filtraAplica2(f, p, xs) == r
assert filtraAplica3(f, p, xs) == r
assert filtraAplica4(f, p, xs) == r
assert filtraAplica5(f, p, xs) == r
# La comprobación es
# src> poetry run pytest -q aplica_segun_propiedad.py
# 1 passed in 0.25s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('filtraAplica1(lambda x: x, lambda x: x % 2 == 0, range(10**5))')
# 0.02 segundos
# >>> tiempo('filtraAplica2(lambda x: x, lambda x: x % 2 == 0, range(10**5))')
# 0.01 segundos
# >>> tiempo('filtraAplica3(lambda x: x, lambda x: x % 2 == 0, range(10**5))')
# Process Python violación de segmento (core dumped)
# >>> tiempo('filtraAplica4(lambda x: x, lambda x: x % 2 == 0, range(10**5))')
# 4.07 segundos
# >>> tiempo('filtraAplica5(lambda x: x, lambda x: x % 2 == 0, range(10**5))')
# 0.01 segundos
#
# >>> tiempo('filtraAplica1(lambda x: x, lambda x: x % 2 == 0, range(10**7))')
# 1.66 segundos
# >>> tiempo('filtraAplica2(lambda x: x, lambda x: x % 2 == 0, range(10**7))')
Capítulo 4. Funciones de orden superior 339
# 1.00 segundos
# >>> tiempo('filtraAplica5(lambda x: x, lambda x: x % 2 == 0, range(10**7))')
# 1.21 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
340 Ejercicios de programación con Python
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_maximo :: NonEmptyList Int -> Bool
prop_maximo (NonEmpty xs) =
all (== maximo1 xs)
[maximo2 xs,
maximo3 xs]
-- La comprobación es
-- λ> quickCheck prop_maximo
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> maximo1 [0..5*10^6]
-- 5000000
-- (3.42 secs, 1,783,406,728 bytes)
-- λ> maximo2 [0..5*10^6]
-- 5000000
-- (0.80 secs, 934,638,080 bytes)
-- λ> maximo3 [0..5*10^6]
-- 5000000
-- (0.12 secs, 360,591,360 bytes)
-- λ> maximo4 [0..5*10^6]
-- 5000000
-- (1.40 secs, 892,891,608 bytes)
Capítulo 4. Funciones de orden superior 341
En Python
# ---------------------------------------------------------------------
# Definir la función
# maximo : (list[A]) -> A:
# tal que maximo(xs) es el máximo de la lista xs. Por ejemplo,
# maximo([3,7,2,5]) == 7
# maximo([”todo”,”es”,”falso”]) == ”todo”
# maximo([”menos”,”alguna”,”cosa”]) == ”menos”
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
return max(xs)
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(), min_size=2))
def test_maximo(xs: list[int]) -> None:
r = maximo1(xs)
assert maximo2(xs) == r
assert maximo3(xs) == r
# La comprobación es
# src> poetry run pytest -q maximo_de_una_lista.py
# 1 passed in 0.33s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('maximo1(range(2*10**4))')
# 0.03 segundos
# >>> tiempo('maximo2(range(2*10**4))')
# 0.00 segundos
# >>> tiempo('maximo3(range(2*10**4))')
# 0.00 segundos
#
# >>> tiempo('maximo2(range(5*10**6))')
# 0.38 segundos
# >>> tiempo('maximo3(range(5*10**6))')
# 0.21 segundos
Capítulo 5
Contenido
5.1. Movimientos en el plano . . . . . . . . . . . . . . . . . . . . .331
5.2. El tipo de figuras geométricas . . . . . . . . . . . . . . . . . .336
5.3. El tipo de los números naturales . . . . . . . . . . . . . . . .338
5.4. El tipo de las listas . . . . . . . . . . . . . . . . . . . . . . . . .341
5.5. El tipo de los árboles binarios con valores en los nodos y
en las hojas . . . . . . . . . . . . . . . . . . . . . . . . . . . . .343
5.5.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .343
5.5.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .344
5.6. Pertenencia de un elemento a un árbol . . . . . . . . . . . .346
5.6.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .346
5.6.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .346
5.7. Aplanamiento de un árbol . . . . . . . . . . . . . . . . . . . .347
5.7.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .347
5.7.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .347
5.8. Número de hojas de un árbol binario . . . . . . . . . . . . . .348
1
https://jaalonso.github.io/materias/PFconHaskell/temas/tema-9.html
343
344 Ejercicios de programación con Python
-- Definición de opuesta
-- =====================
-- 1ª definición de movimiento
-- ===========================
-- 2ª definición de movimiento
-- ===========================
-- 1ª definición de movimientos
Capítulo 5. Tipos definidos y tipos de datos algebraicos 349
-- ============================
-- 2ª definición de movimientos
-- ============================
En Python
# ---------------------------------------------------------------------
# Se consideran el tipo de las posiciones del plano definido por
# Posicion = tuple[int, int]
# y el tipo de las direcciones definido por
# Direccion = Enum('Direccion', ['Izquierda', 'Derecha', 'Arriba', 'Abajo'])
# Definir las siguientes funciones
# opuesta : (Direccion) -> Direccion
# movimiento : (Posicion, Direccion) -> Posicion
# movimientos : (Posicion, list[Direccion]) -> Posicion
# tales que
# + opuesta(d) es la dirección opuesta de d. Por ejemplo,
# opuesta1(Direccion.Izquierda) == Direccion.Derecha
# + movimiento(p d) es la posición reultante de moverse, desde la
# posición p, un paso en la dirección d . Por ejemplo,
# movimiento1((2, 5), Direccion.Arriba) == (2, 6)
# movimiento1((2, 5), opuesta1(Direccion.Abajo)) == (2, 6)
# + movimientos(p, ds) es la posición obtenida aplicando la lista de
# movimientos según las direcciones de ds a la posición p. Por
# ejemplo,
# >>> movimientos1((2, 5), [Direccion.Arriba, Direccion.Izquierda])
# (1, 6)
# ---------------------------------------------------------------------
# 1ª definición de opuesta
# ========================
# 2ª definición de opuesta
# ========================
# 1ª definición de movimiento
# ===========================
if d == Direccion.Derecha:
return (x + 1, y)
if d == Direccion.Arriba:
return (x, y + 1)
if d == Direccion.Abajo:
return (x, y - 1)
assert False
# 2ª definición de movimiento
# ===========================
# 1ª definición de movimientos
# ============================
# 2ª definición de movimientos
# ============================
En Python
# ---------------------------------------------------------------------
# Se consideran las figuras geométricas formadas por circulos
# (definidos por su radio) y rectángulos (definidos por su base y su
# altura). El tipo de las figura geométricas se define por
# @dataclass
Capítulo 5. Tipos definidos y tipos de datos algebraicos 353
# class Figura:
# ”””Figuras geométricas”””
#
# @dataclass
# class Circulo(Figura):
# r: float
#
# @dataclass
# class Rect(Figura):
# x: float
# y: float
#
# Definir las funciones
# area : (Figura) -> float
# cuadrado : (float) -> Figura
# tales que
# + area(f) es el área de la figura f. Por ejemplo,
# area(Circulo(1)) == 3.141592653589793
# area(Circulo(2)) == 12.566370614359172
# area(Rect(2, 5)) == 10
# + cuadrado(n) es el cuadrado de lado n. Por ejemplo,
# area(cuadrado(3)) == 9.0
# ---------------------------------------------------------------------
@dataclass
class Figura:
”””Figuras geométricas”””
@dataclass
class Circulo(Figura):
r: float
@dataclass
class Rect(Figura):
x: float
y: float
354 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# El tipo de los números raturales se puede definir por
# @dataclass
# class Nat:
# pass
#
# @dataclass
# class Cero(Nat):
# pass
#
# @dataclass
# class Suc(Nat):
# n: Nat
# de forma que Suc(Suc(Suc(Cero()))) representa el número 3.
#
# Definir las siguientes funciones
# nat2int : (Nat) -> int
# int2nat : (int) -> Nat
# suma : (Nat, Nat) -> Nat
# tales que
356 Ejercicios de programación con Python
@dataclass
class Nat:
pass
@dataclass
class Cero(Nat):
pass
@dataclass
class Suc(Nat):
n: Nat
return Cero()
return Suc(int2nat(n - 1))
En Python
# ---------------------------------------------------------------------
# El tipo de las listas, con elementos de tipo a, se puede definir por
358 Ejercicios de programación con Python
# @dataclass
# class Lista(Generic[A]):
# pass
#
# @dataclass
# class Nil(Lista[A]):
# pass
#
# @dataclass
# class Cons(Lista[A]):
# x: A
# xs: Lista[A]
# Por ejemplo, la lista [4,2,5] se representa por
# Cons(4, Cons(2, Cons(5, Nil()))).
#
# Definir la función
# longitud :: Lista a -> Int
# tal que (longitud xs) es la longitud de la lista xs. Por ejemplo,
# >>> longitud(Cons(4, Cons(2, Cons(5, Nil()))))
# 3
# ---------------------------------------------------------------------
A = TypeVar(”A”)
@dataclass
class Lista(Generic[A]):
pass
@dataclass
class Nil(Lista[A]):
pass
@dataclass
class Cons(Lista[A]):
x: A
xs: Lista[A]
Capítulo 5. Tipos definidos y tipos de datos algebraicos 359
import Test.QuickCheck
data Arbol a = H a
| N a (Arbol a) (Arbol a)
deriving (Show, Eq)
5.5.2. En Python
# El árbol binario
# 9
# / \
# / \
# 3 7
# / \
# 2 4
# se puede representar por
# N(9, N(3, H(2), H(4)), H(7))
# usando la definición de los árboles binarios que se muestra a
# continuación.
A = TypeVar(”A”)
@dataclass
class Arbol(Generic[A]):
pass
@dataclass
class H(Arbol[A]):
x: A
@dataclass
class N(Arbol[A]):
x: A
i: Arbol[A]
d: Arbol[A]
# Generador de árboles
# ====================
5.6.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# pertenece : (A, Arbol[A]) -> bool
# tal que pertenece(m, a) se verifica si m pertenece en el árbol a. Por
# ejemplo,
# >>> pertenece(4, N(5, N(3, H(1), H(4)), N(7, H(6), (H(9)))))
# True
# >>> pertenece(0, N(5, N(3, H(1), H(4)), N(7, H(6), (H(9)))))
# False
# ---------------------------------------------------------------------
A = TypeVar(”A”)
5.7.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
364 Ejercicios de programación con Python
A = TypeVar(”A”)
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_nHojas :: Arbol Int -> Bool
prop_nHojas x =
nHojas x == nNodos x + 1
-- La comprobación es
-- λ> quickCheck prop_nHojas
-- OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir las funciones
# nHojas : (Arbol[A]) -> int
# nNodos : (Arbol[A]) -> int
# tales que
# + nHojas(x) es el número de hojas del árbol x. Por ejemplo,
# nHojas(N(9, N(3, H(2), H(4)), H(7))) == 3
# + nNodos(x) es el número de nodos del árbol x. Por ejemplo,
# nNodos(N(9, N(3, H(2), H(4)), H(7))) == 2
#
366 Ejercicios de programación con Python
A = TypeVar(”A”)
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=10))
def test_nHojas(n: int) -> None:
a = arbolArbitrario(n)
assert nHojas(a) == nNodos(a) + 1
# La comprobación es
# src> poetry run pytest -q numero_de_hojas_de_un_arbol_binario.py
Capítulo 5. Tipos definidos y tipos de datos algebraicos 367
# 1 passed in 0.10s
import Test.QuickCheck
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_nNodosProfundidad :: Arbol Int -> Bool
prop_nNodosProfundidad x =
nNodos x <= 2 ^ profundidad x - 1
-- La comprobación es
368 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# profundidad : (Arbol[A]) -> int
# tal que profundidad(x) es la profundidad del árbol x. Por ejemplo,
# profundidad(N(9, N(3, H(2), H(4)), H(7))) == 2
# profundidad(N(9, N(3, H(2), N(1, H(4), H(5))), H(7))) == 3
# profundidad(N(4, N(5, H(4), H(2)), N(3, H(7), H(4)))) == 2
#
# Comprobar con Hypothesis que para todo árbol biario x, se tiene que
# nNodos(x) <= 2^profundidad(x) - 1
# ---------------------------------------------------------------------
A = TypeVar(”A”)
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 5. Tipos definidos y tipos de datos algebraicos 369
@given(st.integers(min_value=1, max_value=10))
def test_nHojas(n: int) -> None:
a = arbolArbitrario(n)
assert nNodos(a) <= 2 ** profundidad(a) - 1
# La comprobación es
# src> poetry run pytest -q profundidad_de_un_arbol_binario.py
# 1 passed in 0.11s
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_longitud_recorrido :: Arbol Int -> Bool
prop_longitud_recorrido x =
length (preorden x) == n &&
length (postorden x) == n
where n = nNodos x + nHojas x
-- La comprobación es
-- λ> quickCheck prop_longitud_recorrido
-- OK, passed 100 tests.
5.10.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir las funciones
# preorden : (Arbol[A]) -> list[A]
# postorden : (Arbol[A]) -> list[A]
# tales que
# + preorden(x) es la lista correspondiente al recorrido preorden del
# árbol x; es decir, primero visita la raíz del árbol, a continuación
# recorre el subárbol izquierdo y, finalmente, recorre el subárbol
# derecho. Por ejemplo,
# >>> preorden(N(9, N(3, H(2), H(4)), H(7)))
# [9, 3, 2, 4, 7]
# + (postorden x) es la lista correspondiente al recorrido postorden
Capítulo 5. Tipos definidos y tipos de datos algebraicos 371
A = TypeVar(”A”)
# Comprobación de la propiedad
# ============================
372 Ejercicios de programación con Python
# La propiedad es
@given(st.integers(min_value=1, max_value=10))
def test_recorrido(n: int) -> None:
a = arbolArbitrario(n)
m = nNodos(a) + nHojas(a)
assert len(preorden(a)) == m
assert len(postorden(a)) == m
# La comprobación es
# src> poetry run pytest -q recorrido_de_arboles_binarios.py
# 1 passed in 0.16s
-- La comprobación es
-- λ> quickCheck prop_espejo
-- OK, passed 100 tests.
5.11.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# espejo : (Arbol[A]) -> Arbol[A]
# tal que espejo(x) es la imagen especular del árbol x. Por ejemplo,
# espejo(N(9, N(3, H(2), H(4)), H(7))) == N(9, H(7), N(3, H(4), H(2)))
#
# Comprobar con Hypothesis las siguientes propiedades, para todo árbol
# x,
# espejo(espejo(x)) = x
# list(reversed(preorden(espejo(x)))) == postorden(x)
# postorden(espejo(x)) == list(reversed(preorden(x)))
# ---------------------------------------------------------------------
A = TypeVar(”A”)
# La comprobación es
# src> poetry run pytest -q imagen_especular_de_un_arbol_binario.py
# 1 passed in 0.16s
-- ejemplo,
-- takeArbol 0 (N 9 (N 3 (H 2) (H 4)) (H 7)) == H 9
-- takeArbol 1 (N 9 (N 3 (H 2) (H 4)) (H 7)) == N 9 (H 3) (H 7)
-- takeArbol 2 (N 9 (N 3 (H 2) (H 4)) (H 7)) == N 9 (N 3 (H 2) (H 4)) (H 7)
-- takeArbol 3 (N 9 (N 3 (H 2) (H 4)) (H 7)) == N 9 (N 3 (H 2) (H 4)) (H 7)
--
-- Comprobar con QuickCheck que la profundidad de (takeArbol n x) es
-- menor o igual que n, para todo número natural n y todo árbol x.
-- ---------------------------------------------------------------------
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_takeArbol :: Int -> Arbol Int -> Property
prop_takeArbol n x =
n >= 0 ==> profundidad (takeArbol n x) <= n
-- La comprobación es
-- λ> quickCheck prop_takeArbol
-- +++ OK, passed 100 tests.
5.12.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
376 Ejercicios de programación con Python
A = TypeVar(”A”)
# Comprobación de la propiedad
# ============================
# La propiedad es
Capítulo 5. Tipos definidos y tipos de datos algebraicos 377
@given(st.integers(min_value=0, max_value=12),
st.integers(min_value=1, max_value=10))
def test_takeArbol(n: int, m: int) -> None:
x = arbolArbitrario(m)
assert profundidad(takeArbol(n, x)) <= n
# La comprobación es
# src> poetry run pytest -q subarbol_de_profundidad_dada.py
# 1 passed in 0.23s
import Test.QuickCheck
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_replicateArbol :: Int -> Int -> Property
prop_replicateArbol n x =
n >= 0 ==> nHojas (replicateArbol n x) == 2^n
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=7}) prop_replicateArbol
-- +++ OK, passed 100 tests.
5.13.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# replicateArbol : (int, A) -> Arbol[A]
# tal que (replicate n x) es el árbol de profundidad n cuyos nodos son
# x. Por ejemplo,
# >>> replicateArbol(0, 5)
Capítulo 5. Tipos definidos y tipos de datos algebraicos 379
# H(5)
# >>> replicateArbol(1, 5)
# N(5, H(5), H(5))
# >>> replicateArbol(2, 5)
# N(5, N(5, H(5), H(5)), N(5, H(5), H(5)))
#
# Comprobar con Hypothesis que el número de hojas de
# replicateArbol(n, x) es 2^n, para todo número natural n.
# ---------------------------------------------------------------------
A = TypeVar(”A”)
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=10),
st.integers(min_value=1, max_value=10))
def test_nHojas(n: int, x: int) -> None:
assert nHojas(replicateArbol(n, x)) == 2**n
# La comprobación es
# src> poetry run pytest -q arbol_de_profundidad_n_con_nodos_iguales.py
380 Ejercicios de programación con Python
# 1 passed in 0.20s
ej3arbol3 = N 5 (N 9 (H 1) (H 4)) (H 2)
ej3arbol4 = N 5 (H 4) (N 7 (H 6) (H 2))
5.14.2. En Python
# ---------------------------------------------------------------------
# Usaremos el [tipo de los árboles binarios](https://bit.ly/3H53exA).
#
# Por ejemplo, los árboles
# 5 8 5 5
# / \ / \ / \ / \
# / \ / \ / \ / \
# 9 7 9 3 9 2 4 7
# / \ / \ / \ / \ / \ / \
# 1 4 6 8 1 4 6 2 1 4 6 2
# se pueden representar por
# ej3arbol1: Arbol[int] = N(5, N(9, H(1), H(4)), N(7, H(6), H(8)))
# ej3arbol2: Arbol[int] = N(8, N(9, H(1), H(4)), N(3, H(6), H(2)))
# ej3arbol3: Arbol[int] = N(5, N(9, H(1), H(4)), H(2))
# ej3arbol4: Arbol[int] = N(5, H(4), N(7, H(6), H(2)))
#
# Definir la función
# igualEstructura : (Arbol[A], Arbol[A]) -> bool
# tal que igualEstructura(a1, a2) se verifica si los árboles a1 y a2
# tienen la misma estructura. Por ejemplo,
# igualEstructura(ej3arbol1, ej3arbol2) == True
# igualEstructura(ej3arbol1, ej3arbol3) == False
# igualEstructura(ej3arbol1, ej3arbol4) == False
# ---------------------------------------------------------------------
A = TypeVar(”A”)
5.15.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# algunoArbol : (Arbol[A], Callable[[A], bool]) -> bool
# tal que algunoArbol(a, p) se verifica si algún elemento del árbol a
# cumple la propiedad p. Por ejemplo,
# >>> algunoArbol(N(5, N(3, H(1), H(4)), H(2)), lambda x: x > 4)
# True
# >>> algunoArbol(N(5, N(3, H(1), H(4)), H(2)), lambda x: x > 7)
# False
# ---------------------------------------------------------------------
A = TypeVar(”A”)
5.16.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios](https://bit.ly/3H53exA),
# definir la función
# nivel : (int, Arbol[A]) -> list[A]
# tal que nivel(k, a) es la lista de los elementos de nivel k del árbol
# a. Por ejemplo,
# >>> nivel(0, N(7, N(2, H(5), H(4)), H(9)))
# [7]
# >>> nivel(1, N(7, N(2, H(5), H(4)), H(9)))
# [2, 9]
# >>> nivel(2, N(7, N(2, H(5), H(4)), H(9)))
# [5, 4]
# >>> nivel(3, N(7, N(2, H(5), H(4)), H(9)))
# []
# ---------------------------------------------------------------------
Capítulo 5. Tipos definidos y tipos de datos algebraicos 385
A = TypeVar(”A”)
import Test.QuickCheck
386 Ejercicios de programación con Python
5.17.2. En Python
# El árbol binario
# ·
# / \
# / \
# · ·
# / \ / \
# 1 4 6 9
Capítulo 5. Tipos definidos y tipos de datos algebraicos 387
A = TypeVar(”A”)
@dataclass
class Arbol(Generic[A]):
pass
@dataclass
class Hoja(Arbol[A]):
x: A
@dataclass
class Nodo(Arbol[A]):
i: Arbol[A]
d: Arbol[A]
# Generador
# =========
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con los valores en las hojas]
# (https://bit.ly/3N5RuyE), definir la función
# altura : (Arbol) -> int
# tal que altura(t) es la altura del árbol t. Por ejemplo,
# >>> altura(Hoja(1))
# 0
# >>> altura(Nodo(Hoja(1), Hoja(6)))
# 1
# >>> altura(Nodo(Nodo(Hoja(1), Hoja(6)), Hoja(2)))
# 2
Capítulo 5. Tipos definidos y tipos de datos algebraicos 389
A = TypeVar(”A”)
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con los valores en las hojas]
# (https://bit.ly/3N5RuyE), definir la función
# mapArbol : (Callable[[A], B], Arbol[A]) -> Arbol[B]
# tal que mapArbol(f, t) es el árbolo obtenido aplicando la función f a
# los elementos del árbol t. Por ejemplo,
# >>> mapArbol(lambda x: 1 + x, Nodo(Hoja(2), Hoja(4)))
# Nodo(i=Hoja(x=3), d=Hoja(x=5))
# ---------------------------------------------------------------------
A = TypeVar(”A”)
B = TypeVar(”B”)
-- True
-- λ> arbol3 = Nodo (Hoja 6) (Hoja 7)
-- λ> mismaForma arbol1 arbol3
-- False
-- λ> arbol4 = Nodo (Hoja 9) (Hoja 5)
-- λ> mismaForma arbol3 arbol4
-- True
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_mismaForma :: Arbol Int -> Arbol Int -> Property
prop_mismaForma a1 a2 =
mismaForma1 a1 a2 === mismaForma2 a1 a2
-- La comprobación es
392 Ejercicios de programación con Python
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con los valores en las hojas]
# (https://bit.ly/3N5RuyE), definir la función
# mismaForma : (Arbol[A], Arbol[B]) -> bool
# tal que mismaForma(t1, t2) se verifica si t1 y t2 tienen la misma
# estructura. Por ejemplo,
# >>> arbol1 = Hoja(5)
# >>> arbol2 = Hoja(3)
# >>> mismaForma(arbol1, arbol2)
# True
# >>> arbol3 = Nodo(Hoja(6), Hoja(7))
# >>> mismaForma(arbol1, arbol3)
# False
# >>> arbol4 = Nodo(Hoja(9), Hoja(5))
# >>> mismaForma(arbol3, arbol4)
# True
# ---------------------------------------------------------------------
A = TypeVar(”A”)
B = TypeVar(”B”)
# -- 1ª solución
# -- ===========
# -- 2ª solución
# -- ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=10),
st.integers(min_value=1, max_value=10))
def test_mismaForma(n1: int, n2: int) -> None:
a1 = arbolArbitrario(n1)
a2 = arbolArbitrario(n2)
assert mismaForma1(a1, a2) == mismaForma2(a1, a2)
# La comprobación es
# src> poetry run pytest -q arboles_con_la_misma_forma.py
# 1 passed in 0.22s
-- / \ / \ / \ / \
-- 1 o o 3 o 3 o 1
-- / \ / \ / \ / \
-- 2 3 1 2 1 4 2 3
-- se representan por
-- arbol1, arbol2, arbol3, arbol4 :: Arbol Int
-- arbol1 = Nodo (Hoja 1) (Nodo (Hoja 2) (Hoja 3))
-- arbol2 = Nodo (Nodo (Hoja 1) (Hoja 2)) (Hoja 3)
-- arbol3 = Nodo (Nodo (Hoja 1) (Hoja 4)) (Hoja 3)
-- arbol4 = Nodo (Nodo (Hoja 2) (Hoja 3)) (Hoja 1)
--
-- Definir la función
-- igualBorde :: Eq a => Arbol a -> Arbol a -> Bool
-- tal que (igualBorde t1 t2) se verifica si los bordes de los árboles
-- t1 y t2 son iguales. Por ejemplo,
-- igualBorde arbol1 arbol2 == True
-- igualBorde arbol1 arbol3 == False
-- igualBorde arbol1 arbol4 == False
-- ---------------------------------------------------------------------
5.21.2. En Python
# ---------------------------------------------------------------------
# Usaremos el [tipo de los árboles binarios con los valores en las
# hojas](https://bit.ly/3N5RuyE).
#
# Por ejemplo, los árboles
# árbol1 árbol2 árbol3 árbol4
# o o o o
# / \ / \ / \ / \
# 1 o o 3 o 3 o 1
# / \ / \ / \ / \
# 2 3 1 2 1 4 2 3
# se representan por
# arbol1: Arbol[int] = N(H(1), N(H(2), H(3)))
# arbol2: Arbol[int] = N(N(H(1), H(2)), H(3))
# arbol3: Arbol[int] = N(N(H(1), H(4)), H(3))
# arbol4: Arbol[int] = N(N(H(2), H(3)), H(1))
#
# Definir la función
# igualBorde : (Arbol[A], Arbol[A]) -> bool
# tal que igualBorde(t1, t2) se verifica si los bordes de los árboles
# t1 y t2 son iguales. Por ejemplo,
# igualBorde(arbol1, arbol2) == True
# igualBorde(arbol1, arbol3) == False
# igualBorde(arbol1, arbol4) == False
# ---------------------------------------------------------------------
A = TypeVar(”A”)
# borde(arbol4) == [2, 3, 1]
def borde(a: Arbol[A]) -> list[A]:
match a:
case Hoja(x):
return [x]
case Nodo(i, d):
return borde(i) + borde(d)
assert False
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con los valores en las hojas]
# (https://bit.ly/3N5RuyE), definir la función
# creaArbol : (int) -> Arbol[Any]:
# tal que creaArbol(n) es el árbol cuyas hoyas están en la profundidad
# n. Por ejemplo,
# >>> creaArbol(2)
# Nodo(Nodo(Hoja(None), Hoja(None)), Nodo(Hoja(None), Hoja(None)))
# ---------------------------------------------------------------------
A = TypeVar(”A”)
-- N 9 (N 8 (N 3 H H) (N 2 H H)) (N 6 (N 4 H H) (N 5 H H))
-- usando el tipo de los árboles con valores en los nodos definido como
-- se muestra a continuación.
data Arbol a = H
| N a (Arbol a) (Arbol a)
deriving (Show, Eq)
5.23.2. En Python
# El árbol binario, con valores en los nodos,
# 9
# / \
# / \
# / \
# 8 6
# / \ / \
# 3 2 4 5
# /\ /\ /\ /\
# · ·· ·· · · ·
# se puede representar por
# N(9, N(8, N(3, H(), H()), N(2, H(), H())), N(6, N(4, H(), H()), N(5, H(), H(
# usando el tipo de los árboles binarios con valores en los nodos
# definido como se muestra a continuación.
A = TypeVar(”A”)
@dataclass
class Arbol(Generic[A]):
pass
@dataclass
class H(Arbol[A]):
pass
Capítulo 5. Tipos definidos y tipos de datos algebraicos 399
@dataclass
class N(Arbol[A]):
x: A
i: Arbol[A]
d: Arbol[A]
5.24.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con valores en los nodos]
# (https://bit.ly/40Pplzj), definir la función
# sumaArbol : (Arbol) -> int
# tal sumaArbol(x) es la suma de los valores que hay en el árbol x.
# Por ejemplo,
# >>> sumaArbol(N(2, N(5, N(3, H(), H()), N(7, H(), H())), N(4, H(), H())))
# 21
# ---------------------------------------------------------------------
400 Ejercicios de programación con Python
5.25.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de los árboles binarios con valores en los nodos]
# (https://bit.ly/40Pplzj), definir la función
# ramaIzquierda : (Arbol[A]) -> list[A]
Capítulo 5. Tipos definidos y tipos de datos algebraicos 401
A = TypeVar(”A”)
5.26.2. En Python
# ---------------------------------------------------------------------
# Diremos que un árbol está balanceado si para cada nodo la diferencia
# entre el número de nodos de sus subárboles izquierdo y derecho es
# menor o igual que uno.
#
# Usando el [tipo de los árboles binarios con valores en los nodos]
# (https://bit.ly/40Pplzj), definir la función
# balanceado : (Arbol[A]) -> bool
# tal que balanceado(a) se verifica si el árbol a está balanceado. Por
# ejemplo,
# >>> balanceado(N(5, H(), N(3, H(), H())))
# True
# >>> balanceado(N(4, N(3, N(2, H(), H()), H()), N(5, H(), N(6, H(), N(7, H(),
# False
# ---------------------------------------------------------------------
A = TypeVar(”A”)
Capítulo 5. Tipos definidos y tipos de datos algebraicos 403
-- 6 10
-- / \ / \
-- 2 3 2 5
--
-- Definir la función
-- arbolFactorizacion :: Int -> Arbol
-- tal que (arbolFactorizacion n) es el árbol de factorización de n. Por
-- ejemplo,
-- arbolFactorizacion 60 == N 60 (N 6 (H 2) (H 3)) (N 10 (H 2) (H 5))
-- arbolFactorizacion 45 == N 45 (H 5) (N 9 (H 3) (H 3))
-- arbolFactorizacion 7 == H 7
-- arbolFactorizacion 9 == N 9 (H 3) (H 3)
-- arbolFactorizacion 14 == N 14 (H 2) (H 7)
-- arbolFactorizacion 28 == N 28 (N 4 (H 2) (H 2)) (H 7)
-- arbolFactorizacion 84 == N 84 (H 7) (N 12 (H 3) (N 4 (H 2) (H 2)))
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_arbolFactorizacion :: Int -> Property
406 Ejercicios de programación con Python
prop_arbolFactorizacion n =
n > 1 ==> arbolFactorizacion1 n == arbolFactorizacion2 n
-- La comprobación es
-- λ> quickCheck prop_arbolFactorizacion
-- +++ OK, passed 100 tests; 162 discarded.
5.27.2. En Python
# ---------------------------------------------------------------------
# Los divisores medios de un número son los que ocupan la posición
# media entre los divisores de n, ordenados de menor a mayor. Por
# ejemplo, los divisores de 60 son [1,2,3,4,5,6,10,12,15,20,30,60] y
# sus divisores medios son 6 y 10. Para los números que son cuadrados
# perfectos, sus divisores medios de son sus raíces cuadradas; por
# ejemplos, los divisores medios de 9 son 3 y 3.
#
# El árbol de factorización de un número compuesto n se construye de la
# siguiente manera:
# * la raíz es el número n,
# * la rama izquierda es el árbol de factorización de su divisor
# medio menor y
# * la rama derecha es el árbol de factorización de su divisor
# medio mayor
# Si el número es primo, su árbol de factorización sólo tiene una hoja
# con dicho número. Por ejemplo, el árbol de factorización de 60 es
# 60
# / \
# 6 10
# / \ / \
# 2 3 2 5
#
# Definir la función
# arbolFactorizacion :: Int -> Arbol
# tal que (arbolFactorizacion n) es el árbol de factorización de n. Por
# ejemplo,
# arbolFactorizacion(60) == N(60, N(6, H(2), H(3)), N(10, H(2), H(5)))
# arbolFactorizacion(45) == N(45, H(5), N(9, H(3), H(3)))
# arbolFactorizacion(7) == H(7)
# arbolFactorizacion(9) == N(9, H(3), H(3))
Capítulo 5. Tipos definidos y tipos de datos algebraicos 407
# 1ª solución
# ===========
@dataclass
class Arbol:
pass
@dataclass
class H(Arbol):
x: int
@dataclass
class N(Arbol):
x: int
i: Arbol
d: Arbol
x = xs[len(xs) // 2]
return (n // x, x)
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=2, max_value=200))
def test_arbolFactorizacion(n: int) -> None:
Capítulo 5. Tipos definidos y tipos de datos algebraicos 409
# La comprobación es
# src> poetry run pytest -q arbol_de_factorizacion.py
# 1 passed in 0.14s
5.28.2. En Python
# ---------------------------------------------------------------------
# Se consideran los árboles con operaciones booleanas definidos por
# @dataclass
# class Arbol:
# pass
#
# @dataclass
Capítulo 5. Tipos definidos y tipos de datos algebraicos 411
# class H(Arbol):
# x: bool
#
# @dataclass
# class Conj(Arbol):
# i: Arbol
# d: Arbol
#
# @dataclass
# class Disy(Arbol):
# i: Arbol
# d: Arbol
#
# @dataclass
# class Neg(Arbol):
# a: Arbol
#
# Por ejemplo, los árboles
# Conj Conj
# / \ / \
# / \ / \
# Disy Conj Disy Conj
# / \ / \ / \ / \
# Conj Neg Neg True Conj Neg Neg True
# / \ | | / \ | |
# True False False False True False True False
#
# se definen por
# ej1: Arbol = Conj(Disy(Conj(H(True), H(False)),
# (Neg(H(False)))),
# (Conj(Neg(H(False)),
# (H(True)))))
#
# ej2: Arbol = Conj(Disy(Conj(H(True), H(False)),
# (Neg(H(True)))),
# (Conj(Neg(H(False)),
# (H(True)))))
#
# Definir la función
# valor : (Arbol) -> bool
412 Ejercicios de programación con Python
@dataclass
class Arbol:
pass
@dataclass
class H(Arbol):
x: bool
@dataclass
class Conj(Arbol):
i: Arbol
d: Arbol
@dataclass
class Disy(Arbol):
i: Arbol
d: Arbol
@dataclass
class Neg(Arbol):
a: Arbol
5.29.2. En Python
# La fórmula A → ⊥ ∧ ¬B se representa por
# Impl(Var('A'), Conj(Const(False), Neg (Var('B'))))
# usando el tipo de las fórmulas proposicionales definido como se
# muestra a continuación.
@dataclass
class FProp:
pass
@dataclass
class Const(FProp):
x: bool
@dataclass
class Var(FProp):
x: str
@dataclass
class Neg(FProp):
x: FProp
@dataclass
class Conj(FProp):
x: FProp
y: FProp
@dataclass
class Impl(FProp):
x: FProp
y: FProp
-- λ> variables (Impl (Var 'A') (Conj (Const False) (Neg (Var 'B'))))
-- ”AB”
-- λ> variables (Impl (Var 'A') (Conj (Var 'A') (Neg (Var 'B'))))
-- ”AAB”
-- ---------------------------------------------------------------------
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las fórmulas proposicionales](https://bit.ly/3L3G2SX),
# definir la función
# variables : (FProp) -> list[str]:
# tal que variables(p) es la lista de las variables de la fórmula
# p. Por ejemplo,
# >>> variables (Impl(Var('A'), Conj(Const(False), Neg (Var('B')))))
# ['A', 'B']
# >>> variables (Impl(Var('A'), Conj(Var('A'), Neg (Var('B')))))
# ['A', 'A', 'B']
# ---------------------------------------------------------------------
case Neg(p):
return variables(p)
case Conj(p, q):
return variables(p) + variables(q)
case Impl(p, q):
return variables(p) + variables(q)
assert False
En Python
# ---------------------------------------------------------------------
# Una interpretación de una fórmula es una función de sus variables en
# los booleanos. Por ejemplo, la interpretación que a la variable A le
# asigna verdadero y a la B falso se puede representar por
# [('A', True), ('B', False)]
#
# El tipo de las intepretaciones de puede definir por
# Interpretacion = list[tuple[str, bool]]
#
# El valor de una fórmula en una interpretación se calcula usando las
# funciones de verdad de las conectivas que se muestran a continuación
# |---+----| |---+---+-------+-------|
# | p | ¬p | | p | q | p ∧ q | p → q |
418 Ejercicios de programación con Python
# |---+----| |---+---+-------+-------|
# | T | F | | T | T | T | T |
# | F | T | | T | F | F | F |
# |---+----| | F | T | F | T |
# | F | F | F | T |
# |---+---+-------+-------|
#
# Usando el [tipo de las fórmulas proposicionales](https://bit.ly/3L3G2SX),
# definir la función
# valor: (Interpretacion, FProp) -> bool:
# tal que valor(i, p) es el valor de la fórmula p en la interpretación
# i. Por ejemplo,
# >>> p = Impl(Var('A'), Conj(Var('A'), Var('B')))
# >>> valor([('A',False),('B',False)], p)
# True
# >>> valor([('A',True),('B',False)], p)
# False
# ---------------------------------------------------------------------
-- [True,False],
-- [True,True]]
interpretacionesVar :: Int -> [[Bool]]
interpretacionesVar 0 = [[]]
interpretacionesVar n = map (False:) bss ++ map (True:) bss
where bss = interpretacionesVar (n-1)
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las fórmulas proposicionales](https://bit.ly/3L3G2SX),
# definir la función
# interpretaciones : (FProp) -> list[Interpretacion]
# tal que interpretaciones(p) es la lista de las interpretaciones de
# la fórmula p. Por ejemplo,
# >>> interpretaciones(Impl(Var('A'), Conj(Var('A'), Var('B'))))
# [[('B', False), ('A', False)],
# [('B', False), ('A', True)],
# [('B', True), ('A', False)],
# [('B', True), ('A', True)]]
# ---------------------------------------------------------------------
# pylint: disable=unused-import
En Python
# ---------------------------------------------------------------------
# Una fórmula es una tautología si es verdadera en todas sus
# interpretaciones. Por ejemplo,
# + (A ∧ B) → A es una tautología
# + A → (A ∧ B) no es una tautología
#
# Usando el [tipo de las fórmulas proposicionales](https://bit.ly/3L3G2SX),
# definir la función
# esTautologia :: FProp -> Bool
# tal que (esTautologia p) se verifica si la fórmula p es una
# tautología. Por ejemplo,
# >>> esTautologia(Impl(Conj(Var('A'), Var ('B')), Var('A')))
# True
# >>> esTautologia(Impl(Var('A'), Conj(Var('A'), Var('B'))))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
5.34.2. En Python
# El tipo de las expresiones aritméticas formadas por
# + literales (p.e. Lit 7),
# + sumas (p.e. Suma (Lit 7) (Suma (Lit 3) (Lit 5)))
# + opuestos (p.e. Op (Suma (Op (Lit 7)) (Suma (Lit 3) (Lit 5))))
# + expresiones condicionales (p.e. (SiCero (Lit 3) (Lit 4) (Lit 5))
# se define como se muestra a continuación.
@dataclass
class Expr:
pass
@dataclass
class Lit(Expr):
x: int
@dataclass
class Suma(Expr):
x: Expr
y: Expr
@dataclass
class Op(Expr):
x: Expr
@dataclass
class SiCero(Expr):
424 Ejercicios de programación con Python
x: Expr
y: Expr
z: Expr
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas](https://bit.ly/40vCQUh),
# definir la función
# valor : (Expr) -> int
# tal que valor(e) es el valor de la expresión e (donde el valor de
Capítulo 5. Tipos definidos y tipos de datos algebraicos 425
# 1ª solución
# ===========
# 2ª solución
# ===========
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_resta :: Expr -> Expr -> Property
prop_resta x y =
valor (resta x y) === valor x - valor y
-- La comprobación es
-- λ> quickCheck prop_resta
-- +++ OK, passed 100 tests.
En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas](https://bit.ly/40vCQUh),
# definir la función
# resta : (Expr, Expr) -> Expr
# tal que resta(e1, e2) es la expresión correspondiente a la diferencia
# de e1 y e2. Por ejemplo,
428 Ejercicios de programación con Python
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=10),
st.integers(min_value=1, max_value=10))
def test_mismaForma(n1: int, n2: int) -> None:
x = exprArbitraria(n1)
y = exprArbitraria(n2)
assert valor(resta(x, y)) == valor(x) - valor(y)
# La comprobación es
# src> poetry run pytest -q valor_de_la_resta.py
# 1 passed in 0.21s
5.37.2. En Python
# La expresión aritmética 2*(3+7) se representa por
# P(C(2), S(C(3), C(7)))
# usando el tipo de dato definido a continuación.
@dataclass
class Expr:
430 Ejercicios de programación con Python
pass
@dataclass
class C(Expr):
x: int
@dataclass
class S(Expr):
x: Expr
y: Expr
@dataclass
class P(Expr):
x: Expr
y: Expr
5.38.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas básicas]
# (https://bit.ly/43EuWL4), definir la función
# valor : (Expr) -> int:
# tal que valor(e) es el valor de la expresión aritmética e. Por
# ejemplo,
# valor(P(C(2), S(C(3), C(7)))) == 20
# ---------------------------------------------------------------------
5.39.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas básicas]
# (https://bit.ly/43EuWL4), definir la función
# aplica : (Callable[[int], int], Expr) -> Expr
# tal que aplica(f, e) es la expresión obtenida aplicando la función f
# a cada uno de los números de la expresión e. Por ejemplo,
# >>> aplica(lambda x: 2 + x, S(P(C(3), C(5)), P(C(6), C(7))))
# S(P(C(5), C(7)), P(C(8), C(9)))
# >>> aplica(lambda x: 2 * x, S(P(C(3), C(5)), P(C(6), C(7))))
# S(P(C(6), C(10)), P(C(12), C(14)))
# ---------------------------------------------------------------------
data Expr = X
| C Int
| S Expr Expr
| P Expr Expr
5.40.2. En Python
# La expresión X*(13+X) se representa por
# P(X(), S(C(13), X()))
# usando el tipo de las expresiones aritméticas con una variable
# (denotada por X) que se define como se muestra a continuación,
@dataclass
class Exp:
pass
@dataclass
class X(Exp):
pass
@dataclass
class C(Exp):
x: int
@dataclass
434 Ejercicios de programación con Python
class S(Exp):
x: Exp
y: Exp
@dataclass
class P(Exp):
x: Exp
y: Exp
5.41.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con una variable](https://bit.ly
# definir la función
# valor : (Exp, int) -> int
Capítulo 5. Tipos definidos y tipos de datos algebraicos 435
5.42.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con una variable](https://bit.ly
# definir la función
# numVars : (Exp) -> int
# tal que numVars(e) es el número de variables en la expresión e. Por
# ejemplo,
# numVars(C(3)) == 0
# numVars(X()) == 1
# numVars(P(X(), S(C(13), X()))) == 2
# ---------------------------------------------------------------------
5.43.2. En Python
# La expresión 2*(a+5) puede representarse por
# P(C(2), S(V('a'), C(5)))
# usando el tipo de las expresiones aritméticas con variables definido
# como se muestra a continuación.
@dataclass
class Expr:
pass
@dataclass
class C(Expr):
x: int
@dataclass
class V(Expr):
x: str
438 Ejercicios de programación con Python
@dataclass
class S(Expr):
x: Expr
y: Expr
@dataclass
class P(Expr):
x: Expr
y: Expr
5.44.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con variables]
# (https://bit.ly/3HfB0QO), definir la función
# valor : (Expr, list[tuple[str, int]]) -> int
# tal que valor(x, e) es el valor de la expresión x en el entorno e (es
# decir, el valor de la expresión donde las variables de x se sustituyen
# por los valores según se indican en el entorno e). Por ejemplo,
# λ> valor(P(C(2), S(V('a'), V('b'))), [('a', 2), ('b', 5)])
# 14
# ---------------------------------------------------------------------
5.45.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con variables]
# (https://bit.ly/3HfB0QO), definir la función
# sumas : (Expr) -> int
# tal que sumas(e) es el número de sumas en la expresión e. Por
# ejemplo,
# sumas(P(V('z'), S(C(3), V('x')))) == 1
# sumas(S(V('z'), S(C(3), V('x')))) == 2
# sumas(P(V('z'), P(C(3), V('x')))) == 0
# ---------------------------------------------------------------------
assert False
5.46.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con variables]
# (https://bit.ly/3HfB0QO), definir la función
# sustitucion : (Expr, list[tuple[str, int]]) -> Expr
# tal que sustitucion(e s) es la expresión obtenida sustituyendo las
442 Ejercicios de programación con Python
5.47.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las expresiones aritméticas con variables]
# (https://bit.ly/3HfB0QO), definir la función
# reducible : (Expr) -> bool
# tal que reducible(a) se verifica si a es una expresión reducible; es
# decir, contiene una operación en la que los dos operandos son números.
# Por ejemplo,
# reducible(S(C(3), C(4))) == True
# reducible(S(C(3), V('x'))) == False
# reducible(S(C(3), P(C(4), C(5)))) == True
# reducible(S(V('x'), P(C(4), C(5)))) == True
# reducible(S(C(3), P(V('x'), C(5)))) == False
# reducible(C(3)) == False
# reducible(V('x')) == False
# ---------------------------------------------------------------------
5.48.2. En Python
# ---------------------------------------------------------------------
# Las expresiones aritméticas generales se pueden definir usando el
# siguiente tipo de datos
# @dataclass
# class Expr:
# pass
#
# @dataclass
# class C(Expr):
# x: int
446 Ejercicios de programación con Python
#
# @dataclass
# class X(Expr):
# pass
#
# @dataclass
# class S(Expr):
# x: Expr
# y: Expr
#
# @dataclass
# class R(Expr):
# x: Expr
# y: Expr
#
# @dataclass
# class P(Expr):
# x: Expr
# y: Expr
#
# @dataclass
# class E(Expr):
# x: Expr
# y: int
# Por ejemplo, la expresión
# 3*x - (x+2)^7
# se puede definir por
# R(P(C(3), X()), E(S(X(), C(2)), 7))
#
# Definir la función
# maximo : (Expr, list[int]) -> tuple[int, list[int]]
# tal que maximo(e, xs) es el par formado por el máximo valor de la
# expresión e para los puntos de xs y en qué puntos alcanza el
# máximo. Por ejemplo,
# >>> maximo(E(S(C(10), P(R(C(1), X()), X())), 2), range(-3, 4))
# (100, [0, 1])
# ---------------------------------------------------------------------
@dataclass
class Expr:
pass
@dataclass
class C(Expr):
x: int
@dataclass
class X(Expr):
pass
@dataclass
class S(Expr):
x: Expr
y: Expr
@dataclass
class R(Expr):
x: Expr
y: Expr
@dataclass
class P(Expr):
x: Expr
y: Expr
@dataclass
class E(Expr):
x: Expr
y: int
data Op = S | R | M
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
5.49.2. En Python
# ---------------------------------------------------------------------
# Las operaciones de suma, resta y multiplicación se pueden
# representar mediante el siguiente tipo de datos
# Op = Enum('Op', ['S', 'R', 'M'])
# La expresiones aritméticas con dichas operaciones se pueden
# representar mediante el siguiente tipo de dato algebraico
# @dataclass
# class Expr:
# pass
#
450 Ejercicios de programación con Python
# @dataclass
# class C(Expr):
# x: int
#
# @dataclass
# class A(Expr):
# o: Op
# x: Expr
# y: Expr
# Por ejemplo, la expresión
# (7-3)+(2*5)
# se representa por
# A(Op.S, A(Op.R, C(7), C(3)), A(Op.M, C(2), C(5)))
#
# Definir la función
# valor : (Expr) -> int
# tal que valor(e) es el valor de la expresión e. Por ejemplo,
# >>> valor(A(Op.S, A(Op.R, C(7), C(3)), A(Op.M, C(2), C(5))))
# 14
# >>> valor(A(Op.M, A(Op.R, C(7), C(3)), A(Op.S, C(2), C(5))))
# 28
# ---------------------------------------------------------------------
@dataclass
class Expr:
pass
@dataclass
class C(Expr):
x: int
@dataclass
class A(Expr):
o: Op
x: Expr
Capítulo 5. Tipos definidos y tipos de datos algebraicos 451
y: Expr
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
5.50.2. En Python
# ---------------------------------------------------------------------
# Se consideran las expresiones vectoriales formadas por un vector, la
# suma de dos expresiones vectoriales o el producto de un entero por
# una expresión vectorial. El siguiente tipo de dato define las
# expresiones vectoriales
# @dataclass
# class ExpV:
# pass
#
# @dataclass
# class Vec(ExpV):
# x: int
# y: int
#
# @dataclass
# class Sum(ExpV):
# x: ExpV
# y: ExpV
#
# @dataclass
# class Mul(ExpV):
# x: int
# y: ExpV
#
# Definir la función
# valorEV : (ExpV) -> tuple[int, int]
# tal que valorEV(e) es el valorEV de la expresión vectorial c. Por
# ejemplo,
# valorEV(Vec(1, 2)) == (1,2)
# valorEV(Sum(Vec(1, 2), Vec(3, 4))) == (4,6)
# valorEV(Mul(2, Vec(3, 4))) == (6,8)
# valorEV(Mul(2, Sum(Vec(1, 2), Vec(3, 4)))) == (8,12)
# valorEV(Sum(Mul(2, Vec(1, 2)), Mul(2, Vec(3, 4)))) == (8,12)
# ---------------------------------------------------------------------
@dataclass
454 Ejercicios de programación con Python
class ExpV:
pass
@dataclass
class Vec(ExpV):
x: int
y: int
@dataclass
class Sum(ExpV):
x: ExpV
y: ExpV
@dataclass
class Mul(ExpV):
x: int
y: ExpV
# 1ª solución
# ===========
# 2ª solución
# ===========
return (a + c, b + d)
Algorítmica
457
Capítulo 6
Contenido
6.1. El tipo abstracto de datos de las pilas . . . . . . . . . . . . .443
6.1.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .443
6.1.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .444
6.2. El tipo de datos de las pilas mediante listas . . . . . . . . . .445
6.2.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .445
6.2.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .448
6.3. El tipo de datos de las pilas con librerías . . . . . . . . . . .453
6.3.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .453
6.3.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .455
6.4. Transformación entre pilas y listas . . . . . . . . . . . . . . .460
6.4.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .460
6.4.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .463
6.5. Filtrado de pilas según una propiedad . . . . . . . . . . . . .467
6.5.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .467
6.5.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .468
6.6. Aplicación de una función a los elementos de una pila . . .471
6.6.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .471
6.6.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .472
6.7. Pertenencia a una pila . . . . . . . . . . . . . . . . . . . . . .475
459
460 Ejercicios de programación con Python
module TAD.Pila
(Pila,
vacia, -- Pila a
apila, -- a -> Pila a -> Pila a
cima, -- Pila a -> a
desapila, -- Pila a -> Pila a
esVacia, -- Pila a -> Bool
) where
import TAD.PilaConListas
-- import TAD.PilaConSucesiones
462 Ejercicios de programación con Python
6.1.2. En Python
# Una pila es una estructura de datos, caracterizada por ser una
# secuencia de elementos en la que las operaciones de inserción y
# extracción se realizan por el mismo extremo.
#
# Las operaciones que definen a tipo abstracto de datos (TAD) de las
# pilas (cuyos elementos son del tipo a) son las siguientes:
# vacia :: Pila a
# apila :: a -> Pila a -> Pila a
# cima :: Pila a -> a
# desapila :: Pila a -> Pila a
# esVacia :: Pila a -> Bool
# tales que
# + vacia es la pila vacía.
# + (apila x p) es la pila obtenida añadiendo x al principio de p.
# + (cima p) es la cima de la pila p.
# + (desapila p) es la pila obtenida suprimiendo la cima de p.
# + (esVacia p) se verifica si p es la pila vacía.
#
# Las operaciones tienen que verificar las siguientes propiedades:
# + cima(apila(x, p) == x
# + desapila(apila(x, p)) == p
# + esVacia(vacia)
# + not esVacia(apila(x, p))
#
# Para usar el TAD hay que usar una implementación concreta. En
# principio, consideraremos dos una usando listas y otra usando
# sucesiones. Hay que elegir la que se desee utilizar, descomentándola
# y comentando las otras.
__all__ = [
'Pila',
'vacia',
'apila',
'esVacia',
'cima',
'desapila',
'pilaAleatoria'
]
from src.TAD.pilaConListas import (Pila, apila, cima, desapila, esVacia,
Capítulo 6. El tipo abstracto de datos de las pilas 463
pilaAleatoria, vacia)
module TAD.PilaConListas
(Pila,
vacia, -- Pila a
apila, -- a -> Pila a -> Pila a
cima, -- Pila a -> a
desapila, -- Pila a -> Pila a
esVacia, -- Pila a -> Bool
escribePila -- Show a => Pila a -> String
) where
import Test.QuickCheck
-- Ejemplo de pila:
-- λ> apila 1 (apila 2 (apila 3 vacia))
-- 1 | 2 | 3
-- Generador de pilas --
Capítulo 6. El tipo abstracto de datos de las pilas 465
-- ==================
-- Propiedades
-- ===========
-- La comprobación es:
-- λ> quickCheck prop_pilas
-- +++ OK, passed 100 tests.
466 Ejercicios de programación con Python
6.2.2. En Python
# Se define la clase Pila con los siguientes métodos:
# + apila(x) añade x al principio de la pila.
# + cima() devuelve la cima de la pila.
# + desapila() elimina la cima de la pila.
# + esVacia() se verifica si la pila es vacía.
# Por ejemplo,
# >>> p = Pila()
# >>> p
# -
# >>> p.apila(5)
# >>> p.apila(2)
# >>> p.apila(3)
# >>> p.apila(4)
# >>> p
# 4 | 3 | 2 | 5
# >>> p.cima()
# 4
# >>> p.desapila()
# >>> p
# 3 | 2 | 5
# >>> p.esVacia()
# False
# >>> p = Pila()
# >>> p.esVacia()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacia()
# -
# >>> apila(4, apila(3, apila(2, apila(5, vacia()))))
# 4 | 3 | 2 | 5
# >>> cima(apila(4, apila(3, apila(2, apila(5, vacia())))))
# 4
# >>> desapila(apila(4, apila(3, apila(2, apila(5, vacia())))))
# 3 | 2 | 5
# >>> esVacia(apila(4, apila(3, apila(2, apila(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
Capítulo 6. El tipo abstracto de datos de las pilas 467
#
# Finalmente, se define un generador aleatorio de pilas y se comprueba
# que las pilas cumplen las propiedades de su especificación.
__all__ = [
'Pila',
'vacia',
'apila',
'esVacia',
'cima',
'desapila',
'pilaAleatoria'
]
A = TypeVar('A')
@dataclass
class Pila(Generic[A]):
_elementos: list[A] = field(default_factory=list)
”””
Agrega el elemento x al inicio de la pila.
”””
self._elementos.insert(0, x)
aux = deepcopy(p)
aux.apila(x)
return aux
# Generador de pilas
# ==================
# La comprobación es
# > poetry run pytest -q pilaConListas.py
# 1 passed in 0.25s
module TAD.PilaConSucesiones
(Pila,
vacia, -- Pila a
apila, -- a -> Pila a -> Pila a
cima, -- Pila a -> a
desapila, -- Pila a -> Pila a
esVacia, -- Pila a -> Bool
escribePila -- Show a => Pila a -> String
) where
import Data.Sequence as S
import Test.QuickCheck
-- Ejemplo de pila:
-- λ> apila 1 (apila 2 (apila 3 vacia))
-- 1 | 2 | 3
-- Generador de pilas --
-- ==================
-- Propiedades
-- ===========
prop_pilas x p =
cima (apila x p) == x &&
desapila (apila x p) == p &&
esVacia vacia &&
not (esVacia (apila x p))
-- La comprobación e:
-- λ> quickCheck prop_pilas
-- +++ OK, passed 100 tests.
6.3.2. En Python
# Se define la clase Pila con los siguientes métodos:
# + apila(x) añade x al principio de la pila.
# + cima() devuelve la cima de la pila.
# + desapila() elimina la cima de la pila.
# + esVacia() se verifica si la pila es vacía.
# Por ejemplo,
# >>> p = Pila()
# >>> p
# -
# >>> p.apila(5)
# >>> p.apila(2)
# >>> p.apila(3)
# >>> p.apila(4)
# >>> p
# 4 | 3 | 2 | 5
# >>> p.cima()
# 4
# >>> p.desapila()
# >>> p
# 3 | 2 | 5
# >>> p.esVacia()
# False
# >>> p = Pila()
# >>> p.esVacia()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacia()
474 Ejercicios de programación con Python
# -
# >>> apila(4, apila(3, apila(2, apila(5, vacia()))))
# 4 | 3 | 2 | 5
# >>> cima(apila(4, apila(3, apila(2, apila(5, vacia())))))
# 4
# >>> desapila(apila(4, apila(3, apila(2, apila(5, vacia())))))
# 3 | 2 | 5
# >>> esVacia(apila(4, apila(3, apila(2, apila(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
#
# Finalmente, se define un generador aleatorio de pilas y se comprueba
# que las pilas cumplen las propiedades de su especificación.
__all__ = [
'Pila',
'vacia',
'apila',
'esVacia',
'cima',
'desapila',
'pilaAleatoria'
]
A = TypeVar('A')
@dataclass
class Pila(Generic[A]):
Capítulo 6. El tipo abstracto de datos de las pilas 475
# Generador de pilas
# ==================
Capítulo 6. El tipo abstracto de datos de las pilas 477
# La comprobación es
# > poetry run pytest -q pilaConQueue.py
# 1 passed in 0.25s
-- Por ejemplo,
-- λ> listaApila [3, 2, 5]
-- 5 | 2 | 3
-- + (pilaAlista p) es la lista formada por los elementos de la
-- pila p. Por ejemplo,
-- λ> pilaAlista (apila 5 (apila 2 (apila 3 vacia)))
-- [3, 2, 5]
--
-- Comprobar con QuickCheck que ambas funciones son inversa; es decir,
-- pilaAlista (listaApila xs) = xs
-- listaApila (pilaAlista p) = p
-- ---------------------------------------------------------------------
-- 1ª definición de listaApila
-- ===========================
-- 2ª definición de listaApila
-- ===========================
-- 3ª definición de listaApila
-- ===========================
-- 4ª definición de listaApila
-- ===========================
-- 5ª definición de listaApila
-- ===========================
-- La propiedad es
prop_listaApila :: [Int] -> Bool
prop_listaApila xs =
all (== listaApila xs)
[listaApila2 xs,
listaApila3 xs,
listaApila4 xs,
listaApila5 xs]
-- La comprobación es
-- λ> quickCheck prop_listaApila
-- +++ OK, passed 100 tests.
-- 1ª definición de pilaAlista
-- ===========================
-- 2ª definición de pilaAlista
-- ===========================
-- La propiedad es
prop_pilaAlista :: Pila Int -> Bool
prop_pilaAlista p =
pilaAlista p == pilaAlista2 p
-- La comprobación es
-- λ> quickCheck prop_pilaAlista
-- +++ OK, passed 100 tests.
-- La primera propiedad es
prop_1_listaApila :: [Int] -> Bool
prop_1_listaApila xs =
pilaAlista (listaApila xs) == xs
-- La comprobación es
-- λ> quickCheck prop_1_listaApila
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_2_listaApila :: Pila Int -> Bool
prop_2_listaApila p =
listaApila (pilaAlista p) == p
Capítulo 6. El tipo abstracto de datos de las pilas 481
-- La comprobación es
-- λ> quickCheck prop_2_listaApila
-- +++ OK, passed 100 tests.
6.4.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstacto de datos de las pilas](https://bit.ly/3GTToyK)
# definir las funciones
# listaApila : (list[A]) -> Pila[A]
# pilaAlista : (Pila[A]) -> list[A]
# tales que
# + listaApila(xs) es la pila formada por los elementos de xs.
# Por ejemplo,
# >>> listaApila([3, 2, 5])
# 5 | 2 | 3
# + pilaAlista(p) es la lista formada por los elementos de la
# pila p. Por ejemplo,
# >>> ej = apila(5, apila(2, apila(3, vacia())))
# >>> pilaAlista(ej)
# [3, 2, 5]
# >>> print(ej)
# 5 | 2 | 3
#
# Comprobar con Hypothesis que ambas funciones son inversas; es decir,
# pilaAlista(listaApila(xs)) == xs
# listaApila(pilaAlista(p)) == p
# ---------------------------------------------------------------------
A = TypeVar('A')
482 Ejercicios de programación con Python
# 1ª definición de listaApila
# ===========================
return aux(list(reversed(ys)))
# 2ª solución de listaApila
# =========================
# La propiedad es
@given(st.lists(st.integers()))
def test_listaApila(xs: list[int]) -> None:
assert listaApila(xs) == listaApila2(xs)
# 1ª definición de pilaAlista
# ===========================
# 2ª definición de pilaAlista
# ===========================
Capítulo 6. El tipo abstracto de datos de las pilas 483
# 3ª definición de pilaAlista
# ===========================
@given(p=pilaAleatoria())
def test_pilaAlista(p: Pila[int]) -> None:
assert pilaAlista(p) == pilaAlista2(p)
assert pilaAlista(p) == pilaAlista3(p)
# La primera propiedad es
@given(st.lists(st.integers()))
def test_1_listaApila(xs: list[int]) -> None:
484 Ejercicios de programación con Python
assert pilaAlista(listaApila(xs)) == xs
# La segunda propiedad es
@given(p=pilaAleatoria())
def test_2_listaApila(p: Pila[int]) -> None:
assert listaApila(pilaAlista(p)) == p
# La comprobación es
# src> poetry run pytest -v transformaciones_pilas_listas.py
# test_listaApila PASSED
# test_pilaAlista PASSED
# test_1_listaApila PASSED
# test_2_listaApila PASSED
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_filtraPila :: (Int -> Bool) -> [Int] -> Bool
prop_filtraPila p xs =
filtraPila1 p q == filtraPila2 p q
where q = listaApila xs
-- La comprobación es
-- λ> quickCheck' prop_filtraPila
-- +++ OK, passed 100 tests.
6.5.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK)
486 Ejercicios de programación con Python
# definir la función
# filtraPila : (Callable[[A], bool], Pila[A]) -> Pila[A]
# tal que filtraPila(p, q) es la pila obtenida con los elementos de
# pila q que verifican el predicado p, en el mismo orden. Por ejemplo,
# >>> ej = apila(3, apila(4, apila(6, apila(5, vacia()))))
# >>> filtraPila(lambda x: x % 2 == 0, ej)
# 4 | 6
# >>> filtraPila(lambda x: x % 2 == 1, ej)
# 3 | 5
# >>> ej
# 3 | 4 | 6 | 5
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
Capítulo 6. El tipo abstracto de datos de las pilas 487
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
return r1
# La propiedad es
@given(p=pilaAleatoria())
def test_filtraPila(p: Pila[int]) -> None:
r = filtraPila1(lambda x: x % 2 == 0, p)
assert filtraPila2(lambda x: x % 2 == 0, p) == r
assert filtraPila3(lambda x: x % 2 == 0, p) == r
assert filtraPila4(lambda x: x % 2 == 0, p) == r
# La comprobación es
# src> poetry run pytest -q filtraPila.py
# 1 passed in 0.25s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_mapPila :: (Int -> Int) -> [Int] -> Bool
prop_mapPila f p =
mapPila1 f q == mapPila2 f q
where q = listaApila p
-- La comprobación es
-- λ> quickCheck' prop_mapPila
-- +++ OK, passed 100 tests.
490 Ejercicios de programación con Python
6.6.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK)
# definir la función
# mapPila : (Callable[[A], A], Pila[A]) -> Pila[A]
# tal que mapPila(f, p) es la pila formada con las imágenes por f de
# los elementos de pila p, en el mismo orden. Por ejemplo,
# >>> ej = apila(5, apila(2, apila(7, vacia())))
# >>> mapPila1(lambda x: x + 1, ej)
# 6 | 3 | 8
# >>> ej
# 5 | 2 | 7
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 6. El tipo abstracto de datos de las pilas 491
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(p=pilaAleatoria())
def test_mapPila(p: Pila[int]) -> None:
r = mapPila1(lambda x: x + 1 == 0, p)
assert mapPila2(lambda x: x + 1 == 0, p) == r
assert mapPila3(lambda x: x + 1 == 0, p) == r
assert mapPila4(lambda x: x + 1 == 0, p) == r
# La comprobación es
# src> poetry run pytest -q mapPila.py
# 1 passed in 0.29s
-- 1ª solución
Capítulo 6. El tipo abstracto de datos de las pilas 493
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_pertenecePila :: Int -> Pila Int -> Bool
prop_pertenecePila x p =
pertenecePila x p == pertenecePila2 x p
-- La comprobación es
-- λ> quickCheck prop_pertenecePila
-- +++ OK, passed 100 tests.
6.7.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de las pilas de las pilas](https://bit.ly/3GTToyK
# definir la función
# pertenecePila : (A, Pila[A]) -> bool
# tal que pertenecePila(x, p) se verifica si x es un elemento de la
# pila p. Por ejemplo,
494 Ejercicios de programación con Python
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(x=st.integers(), p=pilaAleatoria())
def test_pertenecePila(x: int, p: Pila[int]) -> None:
r = pertenecePila(x, p)
assert pertenecePila2(x, p) == r
assert pertenecePila3(x, p) == r
assert pertenecePila4(x, p) == r
496 Ejercicios de programación con Python
# La comprobación es
# src> poetry run pytest -q pertenecePila.py
# 1 passed in 0.37s
-- 1ª solución
-- ===========
| esVacia p1 = True
| otherwise = pertenecePila cp1 p2 && contenidaPila1 dp1 p2
where cp1 = cima p1
dp1 = desapila p1
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_contenidaPila :: Pila Int -> Pila Int -> Bool
prop_contenidaPila p1 p2 =
contenidaPila1 p1 p2 == contenidaPila2 p1 p2
-- La comprobación es
-- λ> quickCheck prop_contenidaPila
-- +++ OK, passed 100 tests.
498 Ejercicios de programación con Python
6.8.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
# contenidaPila : (Pila[A], Pila[A]) -> bool
# tal que contenidaPila(p1, p2) se verifica si todos los elementos de
# de la pila p1 son elementos de la pila p2. Por ejemplo,
# >>> ej1 = apila(3, apila(2, vacia()))
# >>> ej2 = apila(3, apila(4, vacia()))
# >>> ej3 = apila(5, apila(2, apila(3, vacia())))
# >>> contenidaPila(ej1, ej3)
# True
# >>> contenidaPila(ej2, ej3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
cp1 = cima(p1)
dp1 = desapila(p1)
return pertenecePila(cp1, p2) and contenidaPila1(dp1, p2)
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
q = deepcopy(p1)
return contenidaPila4Aux(q, p2)
# La propiedad es
@given(p1=pilaAleatoria(), p2=pilaAleatoria())
def test_contenidaPila(p1: Pila[int], p2: Pila[int]) -> None:
r = contenidaPila1(p1, p2)
assert contenidaPila2(p1, p2) == r
assert contenidaPila3(p1, p2) == r
assert contenidaPila4(p1, p2) == r
# La comprobación es
# src> poetry run pytest -q contenidaPila.py
# 1 passed in 0.40s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_prefijoPila :: Pila Int -> Pila Int -> Bool
prop_prefijoPila p1 p2 =
prefijoPila p1 p2 == prefijoPila2 p1 p2
-- La comprobación es
-- λ> quickCheck prop_prefijoPila
502 Ejercicios de programación con Python
6.9.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
# prefijoPila : (Pila[A], Pila[A]) -> bool
# tal que prefijoPila(p1, p2) se verifica si la pila p1 es justamente
# un prefijo de la pila p2. Por ejemplo,
# >>> ej1 = apila(4, apila(2, vacia()))
# >>> ej2 = apila(4, apila(2, apila(5, vacia())))
# >>> ej3 = apila(5, apila(4, apila(2, vacia())))
# >>> prefijoPila(ej1, ej2)
# True
# >>> prefijoPila(ej1, ej3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
cp1 = cima(p1)
dp1 = desapila(p1)
cp2 = cima(p2)
dp2 = desapila(p2)
return cp1 == cp2 and prefijoPila(dp1, dp2)
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(p1=pilaAleatoria(), p2=pilaAleatoria())
def test_prefijoPila(p1: Pila[int], p2: Pila[int]) -> None:
r = prefijoPila(p1, p2)
assert prefijoPila2(p1, p2) == r
assert prefijoPila3(p1, p2) == r
assert prefijoPila4(p1, p2) == r
# La comprobación es
# src> poetry run pytest -q prefijoPila.py
# 1 passed in 0.32s
-- ejemplo,
-- λ> ej1 = apila 2 (apila 3 vacia)
-- λ> ej2 = apila 7 (apila 2 (apila 3 (apila 5 vacia)))
-- λ> ej3 = apila 2 (apila 7 (apila 3 (apila 5 vacia)))
-- λ> subPila ej1 ej2
-- True
-- λ> subPila ej1 ej3
-- False
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
506 Ejercicios de programación con Python
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_subPila :: Pila Int -> Pila Int -> Bool
prop_subPila p1 p2 =
subPila1 p1 p2 == subPila2 p1 p2
-- La comprobación es
-- λ> quickCheck prop_subPila
-- +++ OK, passed 100 tests.
6.10.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
# subPila : (Pila[A], Pila[A]) -> bool
# tal que subPila(p1, p2) se verifica si p1 es una subpila de p2. Por
# ejemplo,
# >>> ej1 = apila(2, apila(3, vacia()))
# >>> ej2 = apila(7, apila(2, apila(3, apila(5, vacia()))))
Capítulo 6. El tipo abstracto de datos de las pilas 507
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# La propiedad es
@given(p1=pilaAleatoria(), p2=pilaAleatoria())
def test_subPila(p1: Pila[int], p2: Pila[int]) -> None:
r = subPila1(p1, p2)
assert subPila2(p1, p2) == r
assert subPila3(p1, p2) == r
# La comprobación es
# src> poetry run pytest -q subPila.py
# 1 passed in 0.32s
-- 1ª solución
-- ===========
510 Ejercicios de programación con Python
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_ordenadaPila :: Pila Int -> Bool
prop_ordenadaPila p =
ordenadaPila p == ordenadaPila2 p
-- La comprobación es
-- λ> quickCheck prop_ordenadaPila
-- +++ OK, passed 100 tests.
Capítulo 6. El tipo abstracto de datos de las pilas 511
6.11.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
# ordenadaPila : (Pila[A]) -> bool
# tal que ordenadaPila(p) se verifica si los elementos de la pila p
# están ordenados en orden creciente. Por ejemplo,
# >>> ordenadaPila(apila(1, apila(5, apila(6, vacia()))))
# True
# >>> ordenadaPila(apila(1, apila(0, apila(6, vacia()))))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
# 1ª solución
# ===========
# 2ª solución
512 Ejercicios de programación con Python
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
p.desapila()
if not p.esVacia() and cp > p.cima():
return False
return True
# La propiedad es
@given(p=pilaAleatoria())
def test_ordenadaPila(p: Pila[int]) -> None:
r = ordenadaPila(p)
assert ordenadaPila2(p) == r
assert ordenadaPila3(p) == r
assert ordenadaPila4(p) == r
# La comprobación es
# src> poetry run pytest -q ordenadaPila.py
# 1 passed in 0.31s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
insertaLista x [] = [x]
insertaLista x (y:ys) | x < y = x : y : ys
| otherwise = y : insertaLista x ys
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_ordenaInserPila :: Pila Int -> Bool
prop_ordenaInserPila p =
ordenaInserPila1 p == ordenaInserPila2 p
-- La comprobación es
-- λ> quickCheck prop_ordenaInserPila
-- +++ OK, passed 100 tests.
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_ordenadaOrdenaInserPila :: Pila Int -> Bool
prop_ordenadaOrdenaInserPila p =
ordenadaPila (ordenaInserPila1 p)
-- La comprobación es
-- λ> quickCheck prop_ordenadaOrdenaInserPila
-- +++ OK, passed 100 tests.
6.12.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
516 Ejercicios de programación con Python
# definir la función
# ordenaInserPila : (A, Pila[A]) -> Pila[A]
# tal que ordenaInserPila(p) es la pila obtenida ordenando por
# inserción los los elementos de la pila p. Por ejemplo,
# >>> ordenaInserPila(apila(4, apila(1, apila(3, vacia()))))
# 1 | 3 | 4
#
# Comprobar con Hypothesis que la pila ordenaInserPila(p) está
# ordenada.
# ---------------------------------------------------------------------
# pylint: disable=unused-import
# 1ª solución
# ===========
cp = cima(p)
dp = desapila(p)
return insertaPila(cp, ordenaInserPila1(dp))
# 2ª solución
# ===========
# 3ª solución
# ===========
# La propiedad es
@given(p=pilaAleatoria())
def test_ordenaInserPila(p: Pila[int]) -> None:
r = ordenaInserPila1(p)
assert ordenaInserPila2(p) == r
assert ordenaInserPila3(p) == r
# La comprobación es
# src> poetry run pytest -q ordenaInserPila.py
# 1 passed in 0.31s
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(p=pilaAleatoria())
def test_ordenadaOrdenaInserPila(p: Pila[int]) -> None:
ordenadaPila(ordenaInserPila1(p))
# La comprobación es
# src> poetry run pytest -q ordenaInserPila.py
# 2 passed in 0.47s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_nubPila :: Pila Int -> Bool
prop_nubPila p =
nubPila1 p == nubPila2 p
-- La comprobación es
-- λ> quickCheck prop_nubPila
-- +++ OK, passed 100 tests.
6.13.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
# nubPila : (Pila[A]) -> Pila[A]
# tal que nubPila(p) es la pila con los elementos de p sin repeticiones.
# Por ejemplo,
# >>> ej = apila(3, apila(1, apila(3, apila(5, vacia()))))
# >>> ej
# 3 | 1 | 3 | 5
# >>> nubPila1(ej)
# 1 | 3 | 5
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
Capítulo 6. El tipo abstracto de datos de las pilas 521
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# La propiedad es
@given(p=pilaAleatoria())
def test_nubPila(p: Pila[int]) -> None:
r = nubPila1(p)
assert nubPila2(p) == r
assert nubPila3(p) == r
# La comprobación es
# src> poetry run pytest -q nubPila.py
# 1 passed in 0.27s
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_maxPila :: Pila Int -> Property
prop_maxPila p =
not (esVacia p) ==> maxPila1 p == maxPila2 p
-- La comprobación es
-- λ> quickCheck prop_maxPila
-- +++ OK, passed 100 tests; 17 discarded.
6.14.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las pilas](https://bit.ly/3GTToyK),
# definir la función
524 Ejercicios de programación con Python
# pylint: disable=unused-import
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(p=pilaAleatoria())
def test_maxPila(p: Pila[int]) -> None:
assume(not esVacia(p))
r = maxPila1(p)
assert maxPila2(p) == r
assert maxPila3(p) == r
526 Ejercicios de programación con Python
assert maxPila4(p) == r
# La comprobación es
# src> poetry run pytest -q maxPila.py
# 1 passed in 0.25s
Capítulo 7
Contenido
7.1. El tipo abstracto de datos de las colas . . . . . . . . . . . . .509
7.1.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .509
7.1.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .510
7.2. El tipo de datos de las colas mediante listas . . . . . . . . .512
7.2.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .512
7.2.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .515
7.3. El tipo de datos de las colas mediante dos listas . . . . . . .519
7.3.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .519
7.3.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .524
7.4. El tipo de datos de las colas mediante sucesiones . . . . . .529
7.4.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .529
7.4.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .532
7.5. Transformaciones entre colas y listas . . . . . . . . . . . . . .537
7.5.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .537
7.5.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .540
7.6. Último elemento de una cola . . . . . . . . . . . . . . . . . .543
7.6.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .543
7.6.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .544
7.7. Longitud de una cola . . . . . . . . . . . . . . . . . . . . . . .547
527
528 Ejercicios de programación con Python
module TAD.Cola
(Cola,
vacia, -- Cola a
inserta, -- a -> Cola a -> Cola a
primero, -- Cola a -> a
resto, -- Cola a -> Cola a
esVacia, -- Cola a -> Bool
) where
import TAD.ColaConListas
-- import TAD.ColaConDosListas
-- import TAD.ColaConSucesiones
7.1.2. En Python
# Una cola es una estructura de datos, caracterizada por ser una
# secuencia de elementos en la que la operación de inserción se realiza
# por un extremo (el posterior o final) y la operación de extracción
# por el otro (el anterior o frente).
#
# Las operaciones que definen a tipo abstracto de datos (TAD) de las
# colas (cuyos elementos son del tipo a) son las siguientes:
# vacia :: Cola a
# inserta :: a -> Cola a -> Cola a
# primero :: Cola a -> a
# resto :: Cola a -> Cola a
# esVacia :: Cola a -> Bool
# tales que
# + vacia es la cola vacía.
# + (inserta x c) es la cola obtenida añadiendo x al final de c.
# + (primero c) es el primero de la cola c.
# + (resto c) es la cola obtenida eliminando el primero de c.
# + (esVacia c) se verifica si c es la cola vacía.
#
# Las operaciones tienen que verificar las siguientes propiedades:
# + primero (inserta x vacia) == x
Capítulo 7. El tipo abstracto de datos de las colas 531
__all__ = [
'Cola',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
'colaAleatoria'
]
from src.TAD.colaConListas import (Cola, colaAleatoria, esVacia, inserta,
primero, resto, vacia)
module TAD.ColaConListas
(Cola,
vacia, -- Cola a
532 Ejercicios de programación con Python
import Test.QuickCheck
-- Ejemplo de cola:
-- λ> inserta 5 (inserta 2 (inserta 3 vacia))
-- 3 | 2 | 5
-- 3 | 2 | 5
inserta :: a -> Cola a -> Cola a
inserta x (C c) = C (c ++ [x])
-- Generador de colas
-- ==================
-- 11 | 6 | 15 | 13 | 20 | -7 | 11 | -5 | 13
genCola :: (Arbitrary a, Num a) => Gen (Cola a)
genCola = do
xs <- listOf arbitrary
return (foldr inserta vacia xs)
-- La comprobación es:
-- λ> quickCheck prop_colas1
-- +++ OK, passed 100 tests.
-- λ> quickCheck prop_colas2
-- +++ OK, passed 100 tests; 3 discarded.
7.2.2. En Python
# Se define la clase Cola con los siguientes métodos:
# + inserta(x) añade x al final de la cola.
# + primero() es el primero de la cola.
# + resto() elimina el primero de la cola.
# + esVacia() se verifica si la cola es vacía.
Capítulo 7. El tipo abstracto de datos de las colas 535
# Por ejemplo,
# >>> c = Cola()
# >>> c
# -
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c
# 5 | 2 | 3 | 4
# >>> c.primero()
# 5
# >>> c.resto()
# >>> c
# 2 | 3 | 4
# >>> c.esVacia()
# False
# >>> c = Cola()
# >>> c.esVacia()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacia()
# -
# >>> inserta(4, inserta(3, inserta(2, inserta(5, vacia()))))
# 5 | 2 | 3 | 4
# >>> inserta(4, inserta(3, inserta(2, inserta(5, vacia()))))
# 5
# >>> resto(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 2 | 3 | 4
# >>> esVacia(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
#
# Finalmente, se define un generador aleatorio de colas y se comprueba
# que las colas cumplen las propiedades de su especificación.
__all__ = [
'Cola',
536 Ejercicios de programación con Python
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
'colaAleatoria'
]
A = TypeVar('A')
@dataclass
class Cola(Generic[A]):
_elementos: list[A] = field(default_factory=list)
”””
Comprueba si la cola está vacía.
# Generador de colas
# ==================
assert esVacia(vacia())
assert not esVacia(inserta(x, c))
@given(c=colaAleatoria(), x=st.integers())
def test_cola2(c: Cola[int], x: int) -> None:
assume(not esVacia(c))
assert primero(inserta(x, c)) == primero(c)
assert resto(inserta(x, c)) == inserta(x, resto(c))
# La comprobación es
# > poetry run pytest -q colaConListas.py
# 1 passed in 0.24s
module TAD.ColaConDosListas
(Cola,
vacia, -- Cola a
inserta, -- a -> Cola a -> Cola a
primero, -- Cola a -> a
resto, -- Cola a -> Cola a
esVacia, -- Cola a -> Bool
) where
540 Ejercicios de programación con Python
import Test.QuickCheck
-- Ejemplo de cola:
-- λ> inserta 5 (inserta 2 (inserta 3 vacia))
-- 3 | 2 | 5
-- ---------------------------------------------------------------------
-- Igualdad de colas --
-- ---------------------------------------------------------------------
542 Ejercicios de programación con Python
-- Generador de colas --
-- ==================
-- La comprobación es:
-- λ> quickCheck prop_colas1
-- +++ OK, passed 100 tests.
-- λ> quickCheck prop_colas2
-- +++ OK, passed 100 tests; 3 discarded.
7.3.2. En Python
# Se define la clase Cola con los siguientes métodos:
# + inserta(x) añade x al final de la cola.
# + primero() es el primero de la cola.
# + resto() elimina el primero de la cola.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = Cola()
# >>> c
# -
# >>> c.inserta(5)
544 Ejercicios de programación con Python
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c
# 5 | 2 | 3 | 4
# >>> c.primero()
# 5
# >>> c.resto()
# >>> c
# 2 | 3 | 4
# >>> c.esVacia()
# False
# >>> c = Cola()
# >>> c.esVacia()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacia()
# -
# >>> inserta(4, inserta(3, inserta(2, inserta(5, vacia()))))
# 5 | 2 | 3 | 4
# >>> primero(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5
# >>> resto(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 2 | 3 | 4
# >>> esVacia(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
#
# Finalmente, se define un generador aleatorio de colas y se comprueba
# que las colas cumplen las propiedades de su especificación.
__all__ = [
'Cola',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
Capítulo 7. El tipo abstracto de datos de las colas 545
'colaAleatoria'
]
A = TypeVar('A')
@dataclass
class Cola(Generic[A]):
_primera: list[A] = field(default_factory=list)
_segunda: list[A] = field(default_factory=list)
Parámetro:
- c (Cola): La cola con la que se va a comparar.
# Generador de colas
# ==================
@given(c=colaAleatoria(), x=st.integers())
def test_cola2(c: Cola[int], x: int) -> None:
assume(not esVacia(c))
assert primero(inserta(x, c)) == primero(c)
assert resto(inserta(x, c)) == inserta(x, resto(c))
# La comprobación es
Capítulo 7. El tipo abstracto de datos de las colas 549
module TAD.ColaConSucesiones
(Cola,
vacia, -- Cola a
inserta, -- a -> Cola a -> Cola a
primero, -- Cola a -> a
resto, -- Cola a -> Cola a
esVacia, -- Cola a -> Bool
) where
import Data.Sequence as S
import Test.QuickCheck
show = escribeCola
-- Ejemplo de cola:
-- λ> inserta 5 (inserta 2 (inserta 3 vacia))
-- 3 | 2 | 5
-- Generador de colas --
-- ==================
-- La comprobación es:
-- λ> quickCheck prop_colas1
-- +++ OK, passed 100 tests.
-- λ> quickCheck prop_colas2
-- +++ OK, passed 100 tests; 9 discarded.
7.4.2. En Python
# Se define la clase Cola con los siguientes métodos:
# + inserta(x) añade x al final de la cola.
# + primero() es el primero de la cola.
# + resto() elimina el primero de la cola.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = Cola()
# >>> c
# -
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c
# 5 | 2 | 3 | 4
# >>> c.primero()
# 5
# >>> c.resto()
# >>> c
# 2 | 3 | 4
# >>> c.esVacia()
# False
# >>> c = Cola()
# >>> c.esVacia()
# True
Capítulo 7. El tipo abstracto de datos de las colas 553
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacia()
# -
# >>> inserta(4, inserta(3, inserta(2, inserta(5, vacia()))))
# 5 | 2 | 3 | 4
# >>> primero(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5
# >>> resto(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 2 | 3 | 4
# >>> esVacia(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
#
# Finalmente, se define un generador aleatorio de colas y se comprueba
# que las colas cumplen las propiedades de su especificación.
__all__ = [
'Cola',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
'colaAleatoria'
]
A = TypeVar('A')
@dataclass
class Cola(Generic[A]):
_elementos: deque[A] = field(default_factory=deque)
# Generador de colas
# ==================
556 Ejercicios de programación con Python
Utiliza la función ”builds” para construir una cola a partir de una lista
de enteros generada aleatoriamente.
”””
def _creaCola(elementos: list[int]) -> Cola[int]:
”””
Crea una cola de enteros a partir de una lista de elementos.
”””
cola: Cola[int] = vacia()
for x in elementos:
cola = inserta(x, cola)
return cola
return st.builds(_creaCola, st.lists(st.integers()))
@given(c=colaAleatoria(), x=st.integers())
def test_cola2(c: Cola[int], x: int) -> None:
assume(not esVacia(c))
assert primero(inserta(x, c)) == primero(c)
assert resto(inserta(x, c)) == inserta(x, resto(c))
# La comprobación es
# > poetry run pytest -q colaConDeque.py
# 1 passed in 0.24s
Capítulo 7. El tipo abstracto de datos de las colas 557
-- 1ª definición de listaAcola
-- ===========================
-- 2ª definición de listaAcola
-- ===========================
558 Ejercicios de programación con Python
-- 3ª definición de listaAcola
-- ===========================
-- 4ª definición de listaAcola
-- ===========================
-- 5ª definición de listaAcola
-- ===========================
-- La propiedad es
prop_listaAcola :: [Int] -> Bool
prop_listaAcola xs =
all (== listaAcola xs)
[listaAcola2 xs,
listaAcola3 xs,
listaAcola4 xs,
listaAcola5 xs]
-- La comprobación es
-- λ> quickCheck prop_listaAcola
-- +++ OK, passed 100 tests.
Capítulo 7. El tipo abstracto de datos de las colas 559
-- Definición de colaAlista
-- ========================
-- La primera propiedad es
prop_1_listaAcola :: [Int] -> Bool
prop_1_listaAcola xs =
colaAlista (listaAcola xs) == xs
-- La comprobación es
-- λ> quickCheck prop_1_listaAcola
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_2_listaAcola :: Cola Int -> Bool
prop_2_listaAcola c =
listaAcola (colaAlista c) == c
-- La comprobación es
-- λ> quickCheck prop_2_listaAcola
-- +++ OK, passed 100 tests.
7.5.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3GTToyK)
# definir las funciones
# listaAcola : (list[A]) -> Cola[A]
# colaAlista : (Cola[A]) -> list[A]
# tales que
560 Ejercicios de programación con Python
A = TypeVar('A')
# 1ª definición de listaAcola
# ===========================
return aux(list(reversed(ys)))
# 2ª solución de listaAcola
Capítulo 7. El tipo abstracto de datos de las colas 561
# =========================
# La propiedad es
@given(st.lists(st.integers()))
def test_listaAcola(xs: list[int]) -> None:
assert listaAcola(xs) == listaAcola2(xs)
# 1ª definición de colaAlista
# ===========================
# 2ª definición de colaAlista
# ===========================
# 3ª definición de colaAlista
# ===========================
@given(p=colaAleatoria())
def test_colaAlista(p: Cola[int]) -> None:
assert colaAlista(p) == colaAlista2(p)
assert colaAlista(p) == colaAlista3(p)
# La primera propiedad es
@given(st.lists(st.integers()))
def test_1_listaAcola(xs: list[int]) -> None:
assert colaAlista(listaAcola(xs)) == xs
# La segunda propiedad es
@given(c=colaAleatoria())
def test_2_listaAcola(c: Cola[int]) -> None:
assert listaAcola(colaAlista(c)) == c
# La comprobación es
# src> poetry run pytest -v transformaciones_colas_listas.py
# test_listaAcola PASSED
# test_colaAlista PASSED
# test_1_listaAcola PASSED
Capítulo 7. El tipo abstracto de datos de las colas 563
# test_2_listaAcola PASSED
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- https://bit.ly/3Xv0oIt
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_ultimoCola :: Cola Int -> Property
prop_ultimoCola c =
not (esVacia c) ==> ultimoCola c == ultimoCola2 c
-- La comprobación es
-- λ> quickCheck prop_ultimoCola
-- +++ OK, passed 100 tests; 16 discarded.
7.6.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# ultimoCola : (Cola[A]) -> A
# tal que ultimoCola(c) es el último elemento de la cola c. Por
# ejemplo:
# >>> ultimoCola(inserta(3, inserta(5, inserta(2, vacia()))))
# 3
# >>> ultimoCola(inserta(2, vacia()))
# 2
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c=colaAleatoria())
def test_ultimoCola(c: Cola[int]) -> None:
assume(not esVacia(c))
Capítulo 7. El tipo abstracto de datos de las colas 567
r = ultimoCola(c)
assert ultimoCola2(c) == r
assert ultimoCola3(c) == r
assert ultimoCola4(c) == r
assert ultimoCola5(c) == r
# La comprobación es
# src> poetry run pytest -q ultimoCola.py
# 1 passed in 0.25s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_longitudCola :: Cola Int -> Bool
prop_longitudCola c =
longitudCola1 c == longitudCola2 c
-- La comprobación es
-- λ> quickCheck prop_longitudCola
-- +++ OK, passed 100 tests.
7.7.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# longitudCola : (Cola[A]) -> int
# tal que longitudCola(c) es el número de elementos de la cola c. Por
# ejemplo,
# >>> longitudCola(inserta(4, inserta(2, inserta(5, vacia()))))
# 3
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
c = resto(c)
return r
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c=colaAleatoria())
def test_longitudCola_(c: Cola[int]) -> None:
r = longitudCola1(c)
assert longitudCola2(c) == r
assert longitudCola3(c) == r
assert longitudCola4(c) == r
assert longitudCola5(c) == r
# La comprobación es
# src> poetry run pytest -q longitudCola.py
# 1 passed in 0.28s
Capítulo 7. El tipo abstracto de datos de las colas 571
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_todosVerifican :: (Int -> Bool) -> [Int] -> Bool
prop_todosVerifican p xs =
todosVerifican1 p c == todosVerifican2 p c
where c = listaAcola xs
-- La comprobación es
-- λ> quickCheck' prop_todosVerifican
-- +++ OK, passed 100 tests.
7.8.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# todosVerifican : (Callable[[A], bool], Cola[A]) -> bool
# tal que todosVerifican(p, c) se verifica si todos los elementos de la
# cola c cumplen la propiedad p. Por ejemplo,
# >>> todosVerifican(lambda x: x > 0, inserta(3, inserta(2, vacia())))
# True
# >>> todosVerifican(lambda x: x > 0, inserta(3, inserta(-2, vacia())))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
574 Ejercicios de programación con Python
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c=colaAleatoria())
def test_filtraPila(c: Cola[int]) -> None:
r = todosVerifican1(lambda x: x > 0, c)
assert todosVerifican2(lambda x: x > 0, c) == r
assert todosVerifican3(lambda x: x > 0, c) == r
assert todosVerifican4(lambda x: x > 0, c) == r
assert todosVerifican5(lambda x: x > 0, c) == r
# La comprobación es
# src> poetry run pytest -q todosVerifican.py
Capítulo 7. El tipo abstracto de datos de las colas 575
# 1 passed in 0.25s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
algunoVerifica2 p c =
any p (colaAlista c)
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_algunoVerifica :: (Int -> Bool) -> [Int] -> Bool
prop_algunoVerifica p xs =
algunoVerifica1 p c == algunoVerifica2 p c
where c = listaAcola xs
-- La comprobación es
-- λ> quickCheck' prop_algunoVerifica
-- +++ OK, passed 100 tests.
7.9.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# algunoVerifica : (Callable[[A], bool], Cola[A]) -> bool
# tal que algunoVerifica(p, c) se verifica si alguno de los elementos de la
# cola c cumplen la propiedad p. Por ejemplo,
# >>> algunoVerifica(lambda x: x > 0, inserta(-3, inserta(2, vacia())))
# True
# >>> algunoVerifica(lambda x: x > 0, inserta(-3, inserta(-2, vacia())))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c=colaAleatoria())
def test_algunoVerifica(c: Cola[int]) -> None:
r = algunoVerifica1(lambda x: x > 0, c)
assert algunoVerifica2(lambda x: x > 0, c) == r
assert algunoVerifica3(lambda x: x > 0, c) == r
assert algunoVerifica4(lambda x: x > 0, c) == r
assert algunoVerifica5(lambda x: x > 0, c) == r
Capítulo 7. El tipo abstracto de datos de las colas 579
# La comprobación es
# src> poetry run pytest -q algunoVerifica.py
# 1 passed in 0.31s
-- 1ª solución
-- ===========
extiendeCola c1 c2
| esVacia c2 = c1
| otherwise = extiendeCola (inserta pc2 c1) rq2
where pc2 = primero c2
rq2 = resto c2
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_extiendeCola :: Cola Int -> Cola Int -> Bool
prop_extiendeCola p c =
extiendeCola p c == extiendeCola2 p c
-- La comprobación es
-- λ> quickCheck prop_extiendeCola
-- +++ OK, passed 100 tests.
7.10.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# extiendeCola : (Cola[A], Cola[A]) -> Cola[A]
# tal que extiendeCola(c1, c2) es la cola que resulta de poner los
# elementos de la cola c2 a continuación de los de la cola de c1. Por
# ejemplo,
# >>> ej1 = inserta(3, inserta(2, vacia()))
# >>> ej2 = inserta(5, inserta(3, inserta(4, vacia())))
Capítulo 7. El tipo abstracto de datos de las colas 581
# >>> ej1
# 2 | 3
# >>> ej2
# 4 | 3 | 5
# >>> extiendeCola(ej1, ej2)
# 2 | 3 | 4 | 3 | 5
# >>> extiendeCola(ej2, ej1)
# 4 | 3 | 5 | 2 | 3
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_extiendeCola(c1: Cola[int], c2: Cola[int]) -> None:
r = extiendeCola(c1, c2)
assert extiendeCola2(c1, c2) == r
assert extiendeCola3(c1, c2) == r
assert extiendeCola4(c1, c2) == r
# La comprobación es
# src> poetry run pytest -q extiendeCola.py
# 1 passed in 0.32s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
rd2 = resto d2
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_intercalaColas :: Cola Int -> Cola Int -> Bool
prop_intercalaColas c1 c2 =
all (== intercalaColas c1 c2)
[intercalaColas2 c1 c2,
intercalaColas2 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_intercalaColas
-- +++ OK, passed 100 tests.
7.11.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# intercalaColas : (Cola[A], Cola[A]) -> Cola[A]
# tal que (intercalaColas c1 c2) es la cola formada por los elementos de
# c1 y c2 colocados en una cola, de forma alternativa, empezando por
# los elementos de c1. Por ejemplo,
# >>> ej1 = inserta(3, inserta(5, vacia()))
586 Ejercicios de programación con Python
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 7. El tipo abstracto de datos de las colas 587
# 3ª solución
# ===========
# 4ª solución
# ===========
pc2 = c2.primero()
c2.resto()
return extiendeCola(inserta(pc2, inserta(pc1, vacia())),
intercalaColas4Aux(c1, c2))
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_intercalaCola(c1: Cola[int], c2: Cola[int]) -> None:
r = intercalaColas(c1, c2)
assert intercalaColas2(c1, c2) == r
assert intercalaColas3(c1, c2) == r
assert intercalaColas4(c1, c2) == r
Capítulo 7. El tipo abstracto de datos de las colas 589
# La comprobación es
# src> poetry run pytest -q intercalaColas.py
# 1 passed in 0.47s
-- 1ª solución
-- ===========
590 Ejercicios de programación con Python
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_agrupaColas :: [Cola Int] -> Bool
prop_agrupaColas cs =
agrupaColas1 cs == agrupaColas2 cs
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=30}) prop_agrupaColas
-- +++ OK, passed 100 tests.
7.12.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# agrupaColas : (list[Cola[A]]) -> Cola[A]
# tal que (agrupaColas [c1,c2,c3,...,cn]) es la cola formada mezclando
# las colas de la lista como sigue: mezcla c1 con c2, el resultado con
# c3, el resultado con c4, y así sucesivamente. Por ejemplo,
# >>> ej1 = inserta(2, inserta(5, vacia()))
# >>> ej2 = inserta(3, inserta(7, inserta(4, vacia())))
# >>> ej3 = inserta(9, inserta(0, inserta(1, inserta(6, vacia()))))
Capítulo 7. El tipo abstracto de datos de las colas 591
# >>> agrupaColas([])
# -
# >>> agrupaColas([ej1])
# 5 | 2
# >>> agrupaColas([ej1, ej2])
# 5 | 4 | 2 | 7 | 3
# >>> agrupaColas([ej1, ej2, ej3])
# 5 | 6 | 4 | 1 | 2 | 0 | 7 | 9 | 3
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
592 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(colaAleatoria(), max_size=4))
def test_agrupaCola(cs: list[Cola[int]]) -> None:
assert agrupaColas1(cs) == agrupaColas2(cs)
# La comprobación es
# src> poetry run pytest -q agrupaColas.py
# 1 passed in 0.50s
-- 1ª solución
-- ===========
Capítulo 7. El tipo abstracto de datos de las colas 593
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_perteneceCola :: Int -> Cola Int -> Bool
prop_perteneceCola x p =
perteneceCola x p == perteneceCola2 x p
-- La comprobación es
-- λ> quickCheck prop_perteneceCola
-- +++ OK, passed 100 tests.
7.13.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# perteneceCola : (A, Cola[A]) -> bool
# tal que perteneceCola(x, c) se verifica si x es un elemento de la
# cola p. Por ejemplo,
# >>> perteneceCola(2, inserta(5, inserta(2, inserta(3, vacia()))))
# True
# >>> perteneceCola(4, inserta(5, inserta(2, inserta(3, vacia()))))
# False
594 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
pc = c.primero()
c.resto()
return x == pc or perteneceCola3Aux(x, c)
# 4ª solución
# ===========
# La propiedad es
@given(x=st.integers(), c=colaAleatoria())
def test_perteneceCola(x: int, c: Cola[int]) -> None:
r = perteneceCola(x, c)
assert perteneceCola2(x, c) == r
assert perteneceCola3(x, c) == r
assert perteneceCola4(x, c) == r
# La comprobación es
# src> poetry run pytest -q perteneceCola.py
# 1 passed in 0.25s
596 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_contenidaCola :: Cola Int -> Cola Int -> Bool
prop_contenidaCola c1 c2 =
contenidaCola1 c1 c2 == contenidaCola2 c1 c2
-- La comprobación es
-- λ> quickCheck prop_contenidaCola
-- +++ OK, passed 100 tests.
7.14.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# contenidaCola : (Cola[A], Cola[A]) -> bool
# tal que contenidaCola(c1, c2) se verifica si todos los elementos de la
# cola c1 son elementos de la cola c2. Por ejemplo,
# >>> ej1 = inserta(3, inserta(2, vacia()))
# >>> ej2 = inserta(3, inserta(4, vacia()))
# >>> ej3 = inserta(5, inserta(2, inserta(3, vacia())))
# >>> contenidaCola(ej1, ej3)
# True
598 Ejercicios de programación con Python
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_contenidaCola(c1: Cola[int], c2: Cola[int]) -> None:
r = contenidaCola1(c1, c2)
assert contenidaCola2(c1, c2) == r
assert contenidaCola3(c1, c2) == r
assert contenidaCola4(c1, c2) == r
600 Ejercicios de programación con Python
# La comprobación es
# src> poetry run pytest -q contenidaCola.py
# 1 passed in 0.44s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_prefijoCola :: Cola Int -> Cola Int -> Bool
prop_prefijoCola c1 c2 =
prefijoCola c1 c2 == prefijoCola2 c1 c2
-- La comprobación es
-- λ> quickCheck prop_prefijoCola
-- +++ OK, passed 100 tests.
7.15.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# prefijoCola : (Cola[A], Cola[A]) -> bool
# tal que prefijoCola(c1, c2) se verifica si la cola c1 es justamente
# un prefijo de la cola c2. Por ejemplo,
# >>> ej1 = inserta(4, inserta(2, vacia()))
# >>> ej2 = inserta(5, inserta(4, inserta(2, vacia())))
# >>> ej3 = inserta(5, inserta(2, inserta(4, vacia())))
# >>> prefijoCola(ej1, ej2)
# True
# >>> prefijoCola(ej1, ej3)
# False
602 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 7. El tipo abstracto de datos de las colas 603
# 4ª solución
# ===========
# La propiedad es
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_prefijoCola(c1: Cola[int], c2: Cola[int]) -> None:
r = prefijoCola(c1, c2)
604 Ejercicios de programación con Python
# La comprobación es
# src> poetry run pytest -q prefijoCola.py
# 1 passed in 0.29s
-- 1ª solución
-- ===========
Capítulo 7. El tipo abstracto de datos de las colas 605
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_subCola :: Cola Int -> Cola Int -> Bool
prop_subCola c1 c2 =
606 Ejercicios de programación con Python
subCola1 c1 c2 == subCola2 c1 c2
-- La comprobación es
-- λ> quickCheck prop_subCola
-- +++ OK, passed 100 tests.
7.16.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# subCola : (Cola[A], Cola[A]) -> bool
# tal que subCola(c1, c2) se verifica si c1 es una subcola de c2. Por
# ejemplo,
# >>> ej1 = inserta(2, inserta(3, vacia()))
# >>> ej2 = inserta(7, inserta(2, inserta(3, inserta(5, vacia()))))
# >>> ej3 = inserta(2, inserta(7, inserta(3, inserta(5, vacia()))))
# >>> subCola(ej1, ej2)
# True
# >>> subCola(ej1, ej3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
A = TypeVar('A')
# 1ª solución
# ===========
Capítulo 7. El tipo abstracto de datos de las colas 607
# 2ª solución
# ===========
# 3ª solución
# ===========
if c1.esVacia():
return True
if c2.esVacia():
return False
if c1.primero() != c2.primero():
c2.resto()
return subCola3Aux(c1, c2)
q1 = deepcopy(c1)
c1.resto()
c2.resto()
return prefijoCola(c1, c2) or subCola3Aux(q1, c2)
# La propiedad es
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_subCola(c1: Cola[int], c2: Cola[int]) -> None:
r = subCola1(c1, c2)
assert subCola2(c1, c2) == r
assert subCola3(c1, c2) == r
# La comprobación es
# src> poetry run pytest -q subCola.py
# 1 passed in 0.31s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- https://bit.ly/3Xv0oIt
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_ordenadaCola :: Cola Int -> Bool
prop_ordenadaCola c =
ordenadaCola c == ordenadaCola2 c
-- La comprobación es
-- λ> quickCheck prop_ordenadaCola
-- +++ OK, passed 100 tests.
7.17.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# ordenadaCola : (Cola[A]) -> bool
# tal que ordenadaCola(c) se verifica si los elementos de la cola c
# están ordenados en orden creciente. Por ejemplo,
# >>> ordenadaCola(inserta(6, inserta(5, inserta(1, vacia()))))
# True
# >>> ordenadaCola(inserta(1, inserta(0, inserta(6, vacia()))))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
c.resto()
if c.esVacia():
return True
return pc <= c.primero() and ordenadaCola3Aux(c)
# 4ª solución
# ===========
# La propiedad es
@given(p=colaAleatoria())
def test_ordenadaCola(p: Cola[int]) -> None:
r = ordenadaCola(p)
assert ordenadaCola2(p) == r
assert ordenadaCola3(p) == r
assert ordenadaCola4(p) == r
# La comprobación es
# src> poetry run pytest -q ordenadaCola.py
# 1 passed in 0.27s
)
Capítulo 7. El tipo abstracto de datos de las colas 613
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- https://bit.ly/3Xv0oIt
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_maxCola :: Cola Int -> Property
prop_maxCola c =
not (esVacia c) ==> maxCola1 c == maxCola2 c
-- La comprobación es
-- λ> quickCheck prop_maxCola
-- +++ OK, passed 100 tests; 16 discarded.
7.18.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de las colas](https://bit.ly/3QWTsRL),
# definir la función
# maxCola : (Cola[A]) -> A
# tal que maxCola(c) sea el mayor de los elementos de la cola c. Por
# ejemplo,
# >>> maxCola(inserta(3, inserta(5, inserta(1, vacia()))))
# 5
# ---------------------------------------------------------------------
# pylint: disable=unused-import
# 1ª solución
Capítulo 7. El tipo abstracto de datos de las colas 615
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
if pc > r:
r = pc
c.resto()
return r
# La propiedad es
@given(c=colaAleatoria())
def test_maxCola(c: Cola[int]) -> None:
assume(not esVacia(c))
r = maxCola1(c)
assert maxCola2(c) == r
assert maxCola3(c) == r
assert maxCola4(c) == r
# La comprobación es
# src> poetry run pytest -q maxCola.py
# 1 passed in 0.30s
Capítulo 8
Contenido
8.1. El tipo abstracto de datos de los conjuntos . . . . . . . . . .597
8.1.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .597
8.1.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .599
8.2. El tipo de datos de los conjuntos mediante listas no orde-
nadas con duplicados . . . . . . . . . . . . . . . . . . . . . . .601
8.2.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .601
8.2.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .605
8.3. El tipo de datos de los conjuntos mediante listas no orde-
nadas sin duplicados . . . . . . . . . . . . . . . . . . . . . . .610
8.3.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .610
8.3.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .614
8.4. El tipo de datos de los conjuntos mediante listas ordenadas
sin duplicados . . . . . . . . . . . . . . . . . . . . . . . . . . .620
8.4.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .620
8.4.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .624
8.5. El tipo de datos de los conjuntos mediante librería . . . . .630
8.5.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .630
8.5.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .633
8.6. Transformaciones entre conjuntos y listas . . . . . . . . . . .639
617
618 Ejercicios de programación con Python
--
-- Las operaciones que definen al tipo abstracto de datos (TAD) de los
-- conjuntos (cuyos elementos son del tipo a) son las siguientes:
-- vacio :: Conj a
-- inserta :: Ord a => a -> Conj a -> Conj a
-- menor :: Ord a => Conj a -> a
-- elimina :: Ord a => a -> Conj a -> Conj a
-- pertenece :: Ord a => a -> Conj a -> Bool
-- esVacio :: Conj a -> Bool
-- tales que
-- + vacio es el conjunto vacío.
-- + (inserta x c) es el conjunto obtenido añadiendo el elemento x al
-- conjunto c.
-- + (menor c) es el menor elemento del conjunto c.
-- + (elimina x c) es el conjunto obtenido eliminando el elemento x
-- del conjunto c.
-- + (pertenece x c) se verifica si x pertenece al conjunto c.
-- + (esVacio c) se verifica si c es el conjunto vacío.
--
-- Las operaciones tienen que verificar las siguientes propiedades:
-- + inserta x (inserta x c) == inserta x c
-- + inserta x (inserta y c) == inserta y (inserta x c)
-- + not (pertenece x vacio)
-- + pertenece y (inserta x c) == (x==y) || pertenece y c
-- + elimina x vacio == vacio
-- + Si x == y, entonces
-- elimina x (inserta y c) == elimina x c
-- + Si x /= y, entonces
-- elimina x (inserta y c) == inserta y (elimina x c)
-- + esVacio vacio
-- + not (esVacio (inserta x c))
--
-- Para usar el TAD hay que usar una implementación concreta. En
-- principio, consideraremos las siguientes:
-- + mediante listas no ordenadas con duplicados,
-- + mediante listas no ordenadas sin duplicados,
-- + mediante listas ordenadas sin duplicados y
-- + mediante la librería Data.Set.
-- Hay que elegir la que se desee utilizar, descomentándola y comentando
-- las otras.
Capítulo 8. El tipo abstracto de datos de los conjuntos 621
module TAD.Conjunto
(Conj,
vacio, -- Conj a
inserta, -- Ord a => a -> Conj a -> Conj a
menor, -- Ord a => Conj a -> a
elimina, -- Ord a => a -> Conj a -> Conj a
pertenece, -- Ord a => a -> Conj a -> Bool
esVacio -- Conj a -> Bool
) where
-- import TAD.ConjuntoConListasNoOrdenadasConDuplicados
-- import TAD.ConjuntoConListasNoOrdenadasSinDuplicados
import TAD.ConjuntoConListasOrdenadasSinDuplicados
-- import TAD.ConjuntoConLibreria
8.1.2. En Python
# Un conjunto es una estructura de datos, caracterizada por ser una
# colección de elementos en la que no importe ni el orden ni la
# repetición de elementos.
#
# Las operaciones que definen al tipo abstracto de datos (TAD) de los
# conjuntos (cuyos elementos son del tipo a) son las siguientes:
# vacio :: Conj a
# inserta :: Ord a => a -> Conj a -> Conj a
# menor :: Ord a => Conj a -> a
# elimina :: Ord a => a -> Conj a -> Conj a
# pertenece :: Ord a => a -> Conj a -> Bool
# esVacio :: Conj a -> Bool
# tales que
# + vacio es el conjunto vacío.
# + (inserta x c) es el conjunto obtenido añadiendo el elemento x al
# conjunto c.
# + (menor c) es el menor elemento del conjunto c.
# + (elimina x c) es el conjunto obtenido eliminando el elemento x
# del conjunto c.
# + (pertenece x c) se verifica si x pertenece al conjunto c.
622 Ejercicios de programación con Python
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
module TAD.ConjuntoConListasNoOrdenadasConDuplicados
(Conj,
vacio, -- Conj a
inserta, -- Ord a => a -> Conj a -> Conj a
menor, -- Ord a => Conj a -> a
elimina, -- Ord a => a -> Conj a -> Conj a
pertenece, -- Ord a => a -> Conj a -> Bool
esVacio -- Conj a -> Bool
) where
-- ”{}”
-- λ> escribeConjunto (Cj [5])
-- ”{5}”
-- λ> escribeConjunto (Cj [2, 5])
-- ”{2, 5}”
-- λ> escribeConjunto (Cj [5, 2, 5])
-- ”{2, 5}”
escribeConjunto :: (Show a, Ord a) => Conj a -> String
escribeConjunto (Cj xs) =
”{” ++ intercalate ”, ” (map show (sort (nub xs))) ++ ”}”
-- Generador de conjuntos --
-- ======================
-- Comprobación
-- λ> quickCheck prop_conjuntos
-- +++ OK, passed 100 tests.
8.2.2. En Python
# Se define la clase Conj con los siguientes métodos:
# + inserta(x) añade x al conjunto.
# + menor() es el menor elemento del conjunto.
# + elimina(x) elimina las ocurrencias de x en el conjunto.
# + pertenece(x) se verifica si x pertenece al conjunto.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = Conj()
# >>> c
# {}
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c.inserta(5)
# >>> c
# {2, 3, 4, 5}
# >>> c.menor()
# 2
# >>> c.elimina(3)
# >>> c
# {2, 4, 5}
# >>> c.pertenece(4)
# True
# >>> c.pertenece(3)
# False
# >>> c.esVacio()
628 Ejercicios de programación con Python
# False
# >>> c = Conj()
# >>> c.esVacio()
# True
# >>> c = Conj()
# >>> c.inserta(2)
# >>> c.inserta(5)
# >>> d = Conj()
# >>> d.inserta(5)
# >>> d.inserta(2)
# >>> d.inserta(5)
# >>> c == d
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacio()
# {}
# >>> inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))
# {2, 3, 5}
# >>> menor(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# 2
# >>> elimina(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# {2, 3}
# >>> pertenece(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# True
# >>> pertenece(1, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
# >>> esVacio(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
# >>> esVacio(vacio())
# True
# >>> inserta(5, inserta(2, vacio())) == inserta(2, inserta(5, (inserta(2, vac
# True
#
# Finalmente, se define un generador aleatorio de conjuntos y se
# comprueba que los conjuntos cumplen las propiedades de su
# especificación.
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
@dataclass
class Conj(Generic[A]):
_elementos: list[A] = field(default_factory=list)
# ===========================
”””
Se verifica si el conjunto está vacío.
”””
return c.esVacio()
# Generador de conjuntos
# ======================
# La comprobación es
# > poetry run pytest -q conjuntoConListasNoOrdenadasConDuplicados.py
# 1 passed in 0.33s
module TAD.ConjuntoConListasNoOrdenadasSinDuplicados
(Conj,
vacio, -- Conj a
inserta, -- Ord a => a -> Conj a -> Conj a
menor, -- Ord a => Conj a -> a
elimina, -- Ord a => a -> Conj a -> Conj a
pertenece, -- Ord a => a -> Conj a -> Bool
esVacio -- Conj a -> Bool
) where
-- False
-- λ> esVacio vacio
-- True
esVacio :: Conj a -> Bool
esVacio (Cj xs) = null xs
-- Generador de conjuntos --
-- ======================
-- {}
-- {-1, 0}
-- {-4, 1, 2}
-- {-3, 0, 2, 3, 4}
-- {-7}
-- {-10, -7, -5, -2, -1, 2, 5, 8}
-- {5, 7, 8, 10}
-- {-9, -6, -3, 8}
-- {-8, -6, -5, -1, 7, 9, 14}
-- {-18, -15, -14, -13, -3, -2, 1, 2, 4, 8, 12, 17}
-- {-17, -16, -13, -12, -11, -9, -6, -3, 0, 1, 3, 5, 6, 7, 16, 18}
genConjunto :: (Arbitrary a, Ord a) => Gen (Conj a)
genConjunto = do
xs <- listOf arbitrary
return (foldr inserta vacio xs)
-- Comprobación
-- λ> quickCheck prop_conjuntos
-- +++ OK, passed 100 tests.
Capítulo 8. El tipo abstracto de datos de los conjuntos 637
8.3.2. En Python
# Se define la clase Conj con los siguientes métodos:
# + inserta(x) añade x al conjunto.
# + menor() es el menor elemento del conjunto.
# + elimina(x) elimina las ocurrencias de x en el conjunto.
# + pertenece(x) se verifica si x pertenece al conjunto.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = Conj()
# >>> c
# {}
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c.inserta(5)
# >>> c
# {2, 3, 4, 5}
# >>> c.menor()
# 2
# >>> c.elimina(3)
# >>> c
# {2, 4, 5}
# >>> c.pertenece(4)
# True
# >>> c.pertenece(3)
# False
# >>> c.esVacio()
# False
# >>> c = Conj()
# >>> c.esVacio()
# True
# >>> c = Conj()
# >>> c.inserta(2)
# >>> c.inserta(5)
# >>> d = Conj()
# >>> d.inserta(5)
# >>> d.inserta(2)
# >>> d.inserta(5)
# >>> c == d
638 Ejercicios de programación con Python
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacio()
# {}
# >>> inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))
# {2, 3, 5}
# >>> menor(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# 2
# >>> elimina(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# {2, 3}
# >>> pertenece(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# True
# >>> pertenece(1, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
# >>> esVacio(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
# >>> esVacio(vacio())
# True
# >>> inserta(5, inserta(2, vacio())) == inserta(2, inserta(5, (inserta(2, vac
# True
#
# Finalmente, se define un generador aleatorio de conjuntos y se
# comprueba que los conjuntos cumplen las propiedades de su
# especificación.
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
@dataclass
class Conj(Generic[A]):
_elementos: list[A] = field(default_factory=list)
if x not in self._elementos:
self._elementos.append(x)
# Generador de conjuntos
# ======================
# La comprobación es
# > poetry run pytest -q conjuntoConListasNoOrdenadasSinDuplicados.py
# 1 passed in 0.26s
Capítulo 8. El tipo abstracto de datos de los conjuntos 643
module TAD.ConjuntoConListasOrdenadasSinDuplicados
(Conj,
vacio, -- Conj a
inserta, -- Ord a => a -> Conj a -> Conj a
menor, -- Ord a => Conj a -> a
elimina, -- Ord a => a -> Conj a -> Conj a
pertenece, -- Ord a => a -> Conj a -> Bool
esVacio -- Conj a -> Bool
) where
show = escribeConjunto
-- Generador de conjuntos --
-- ======================
arbitrary = genConjunto
-- Comprobación
-- λ> quickCheck prop_conjuntos
-- +++ OK, passed 100 tests.
8.4.2. En Python
# Se define la clase Conj con los siguientes métodos:
# + inserta(x) añade x al conjunto.
# + menor() es el menor elemento del conjunto.
# + elimina(x) elimina las ocurrencias de x en el conjunto.
# + pertenece(x) se verifica si x pertenece al conjunto.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = Conj()
# >>> c
# {}
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c.inserta(5)
# >>> c
Capítulo 8. El tipo abstracto de datos de los conjuntos 647
# {2, 3, 4, 5}
# >>> c.menor()
# 2
# >>> c.elimina(3)
# >>> c
# {2, 4, 5}
# >>> c.pertenece(4)
# True
# >>> c.pertenece(3)
# False
# >>> c.esVacio()
# False
# >>> c = Conj()
# >>> c.esVacio()
# True
# >>> c = Conj()
# >>> c.inserta(2)
# >>> c.inserta(5)
# >>> d = Conj()
# >>> d.inserta(5)
# >>> d.inserta(2)
# >>> d.inserta(5)
# >>> c == d
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacio()
# {}
# >>> inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))
# {2, 3, 5}
# >>> menor(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# 2
# >>> elimina(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# {2, 3}
# >>> pertenece(5, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# True
# >>> pertenece(1, inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
# >>> esVacio(inserta(5, inserta(3, inserta(2, inserta(5, vacio())))))
# False
648 Ejercicios de programación con Python
# >>> esVacio(vacio())
# True
# >>> inserta(5, inserta(2, vacio())) == inserta(2, inserta(5, (inserta(2, vac
# True
#
# Finalmente, se define un generador aleatorio de conjuntos y se
# comprueba que los conjuntos cumplen las propiedades de su
# especificación.
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
Capítulo 8. El tipo abstracto de datos de los conjuntos 649
@dataclass
class Conj(Generic[A]):
_elementos: list[A] = field(default_factory=list)
# Generador de conjuntos
# ======================
assert elimina(x, v) == v
# La comprobación es
# > poetry run pytest -q conjuntoConListasOrdenadasSinDuplicados.py
# 1 passed in 0.13s
module TAD.ConjuntoConLibreria
(Conj,
vacio, -- Conj a
inserta, -- Ord a => a -> Conj a -> Conj a
menor, -- Ord a => Conj a -> a
elimina, -- Ord a => a -> Conj a -> Conj a
pertenece, -- Ord a => a -> Conj a -> Bool
esVacio -- Conj a -> Bool
) where
-- 2
menor :: Ord a => Conj a -> a
menor (Cj s) = findMin s
-- Generador de conjuntos --
-- ======================
-- Comprobación
-- λ> quickCheck prop_conjuntos
-- +++ OK, passed 100 tests.
8.5.2. En Python
# Se define la clase Conj con los siguientes métodos:
# + inserta(x) añade x al conjunto.
# + menor() es el menor elemento del conjunto.
# + elimina(x) elimina las ocurrencias de x en el conjunto.
# + pertenece(x) se verifica si x pertenece al conjunto.
# + esVacia() se verifica si la cola es vacía.
656 Ejercicios de programación con Python
# Por ejemplo,
# >>> c = Conj()
# >>> c
# {}
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c.inserta(5)
# >>> c
# {2, 3, 4, 5}
# >>> c.menor()
# 2
# >>> c.elimina(3)
# >>> c
# {2, 4, 5}
# >>> c.pertenece(4)
# True
# >>> c.pertenece(3)
# False
# >>> c.esVacio()
# False
# >>> c = Conj()
# >>> c.esVacio()
# True
# >>> c = Conj()
# >>> c.inserta(2)
# >>> c.inserta(5)
# >>> d = Conj()
# >>> d.inserta(5)
# >>> d.inserta(2)
# >>> d.inserta(5)
# >>> c == d
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> vacio()
# {}
# >>> inserta(5, inserta(3, inserta(2, inserta(5, vacio()))))
# {2, 3, 5}
Capítulo 8. El tipo abstracto de datos de los conjuntos 657
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
@dataclass
class Conj(Generic[A]):
_elementos: set[A] = field(default_factory=set)
”””
return not self._elementos
_aux.elimina(x)
return _aux
# Generador de conjuntos
# ======================
return elimina(x, c)
return inserta(y, elimina(x, c))
# La comprobación es
# > poetry run pytest -q conjuntoConLibreria.py
# 1 passed in 0.22s
-- 1ª definición de listaAconjunto
-- ===============================
-- 2ª definición de listaAconjunto
-- ===============================
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_listaAconjunto :: [Int] -> Bool
prop_listaAconjunto xs =
listaAconjunto xs == listaAconjunto2 xs
-- La comprobación es
-- λ> quickCheck prop_listaAconjunto
-- +++ OK, passed 100 tests.
-- Definición de conjuntoAlista
-- ============================
-- La primera propiedad es
prop_1_listaAconjunto :: [Int] -> Bool
prop_1_listaAconjunto xs =
conjuntoAlista (listaAconjunto xs) == sort (nub xs)
-- La comprobación es
-- λ> quickCheck prop_1_listaAconjunto
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_2_listaAconjunto :: Conj Int -> Bool
prop_2_listaAconjunto c =
listaAconjunto (conjuntoAlista c) == c
-- La comprobación es
-- λ> quickCheck prop_2_listaAconjunto
-- +++ OK, passed 100 tests.
8.6.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir las funciones
# listaAconjunto : (list[A]) -> Conj[A]
# conjuntoAlista : (Conj[A]) -> list[A]
# tales que
# + listaAconjunto(xs) es el conjunto formado por los elementos de xs.
# Por ejemplo,
# >>> listaAconjunto([3, 2, 5])
# {2, 3, 5}
# + conjuntoAlista(c) es la lista formada por los elementos del
# conjunto c. Por ejemplo,
# >>> conjuntoAlista(inserta(5, inserta(2, inserta(3, vacio()))))
# [2, 3, 5]
#
# Comprobar con Hypothesis que ambas funciones son inversa; es decir,
# conjuntoAlista (listaAconjunto xs) = sorted(list(set(xs)))
# listaAconjunto (conjuntoAlista c) = c
# ---------------------------------------------------------------------
664 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª definición de listaAconjunto
# ===============================
# 2ª definición de listaAconjunto
# ===============================
# 3ª solución de listaAconjunto
# =============================
c: Conj[A] = Conj()
for x in xs:
c.inserta(x)
return c
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()))
def test_listaAconjunto(xs: list[int]) -> None:
r = listaAconjunto(xs)
assert listaAconjunto2(xs) == r
assert listaAconjunto3(xs) == r
# 1ª definición de conjuntoAlista
# ===============================
# 2ª definición de conjuntoAlista
# ===============================
# 3ª definición de conjuntoAlista
666 Ejercicios de programación con Python
# ===============================
@given(c=conjuntoAleatorio())
def test_conjuntoAlista(c: Conj[int]) -> None:
r = conjuntoAlista(c)
assert conjuntoAlista2(c) == r
assert conjuntoAlista3(c) == r
# La primera propiedad es
@given(st.lists(st.integers()))
def test_1_listaAconjunto(xs: list[int]) -> None:
assert conjuntoAlista(listaAconjunto(xs)) == sorted(list(set(xs)))
# La segunda propiedad es
@given(c=conjuntoAleatorio())
def test_2_listaAconjunto(c: Conj[int]) -> None:
assert listaAconjunto(conjuntoAlista(c)) == c
# test_1_listaAconjunto PASSED
# test_2_listaAconjunto PASSED
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_subconjunto :: Conj Int -> Conj Int -> Bool
prop_subconjunto c1 c2 =
all (== subconjunto c1 c2)
Capítulo 8. El tipo abstracto de datos de los conjuntos 669
[subconjunto2 c1 c2,
subconjunto3 c1 c2,
subconjunto4 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_subconjunto
-- +++ OK, passed 100 tests.
8.7.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# subconjunto :: Ord a => Conj a -> Conj a -> Bool
# tal que (subconjunto c1 c2) se verifica si todos los elementos de c1
# pertenecen a c2. Por ejemplo,
# >>> ej1 = inserta(5, inserta(2, vacio()))
# >>> ej2 = inserta(3, inserta(2, inserta(5, vacio())))
# >>> ej3 = inserta(3, inserta(4, inserta(5, vacio())))
# >>> subconjunto(ej1, ej2)
# True
# >>> subconjunto(ej1, ej3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_subconjunto(c1: Conj[int], c2: Conj[int]) -> None:
r = subconjunto(c1, c2)
assert subconjunto2(c1, c2) == r
assert subconjunto3(c1, c2) == r
assert subconjunto4(c1, c2) == r
assert subconjunto5(c1, c2) == r
672 Ejercicios de programación con Python
8.8.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# subconjuntoPropio : (Conj[A], Conj[A]) -> bool
# tal subconjuntoPropio(c1, c2) se verifica si c1 es un subconjunto
# propio de c2. Por ejemplo,
# >>> ej1 = inserta(5, inserta(2, vacio()))
# >>> ej2 = inserta(3, inserta(2, inserta(5, vacio())))
# >>> ej3 = inserta(3, inserta(4, inserta(5, vacio())))
# >>> ej4 = inserta(2, inserta(5, vacio()))
# >>> subconjuntoPropio(ej1, ej2)
# True
# >>> subconjuntoPropio(ej1, ej3)
# False
# >>> subconjuntoPropio(ej1, ej4)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
8.9.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# unitario :: Ord a => a -> Conj a
# tal que (unitario x) es el conjunto {x}. Por ejemplo,
# unitario 5 == {5}
# ---------------------------------------------------------------------
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_cardinal :: Conj Int -> Bool
prop_cardinal c =
cardinal c == cardinal2 c
-- La comprobación es
-- λ> quickCheck prop_cardinal
-- +++ OK, passed 100 tests.
8.10.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# cardinal : (Conj[A]) -> int
# tal que cardinal(c) es el número de elementos del conjunto c. Por
# ejemplo,
# cardinal(inserta(4, inserta(5, vacio()))) == 2
# cardinal(inserta(4, inserta(5, inserta(4, vacio())))) == 2
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
678 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
@given(c=conjuntoAleatorio())
def test_cardinal(c: Conj[int]) -> None:
r = cardinal(c)
assert cardinal2(c) == r
assert cardinal3(c) == r
assert cardinal3(c) == r
# La comprobación es
# src> poetry run pytest -q TAD_Numero_de_elementos_de_un_conjunto.py
# 1 passed in 0.33s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- https://bit.ly/3RexzxH
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_union :: Conj Int -> Conj Int -> Bool
prop_union c1 c2 =
all (== union c1 c2)
[union2 c1 c2,
union3 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_union
-- +++ OK, passed 100 tests.
8.11.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# union : (Conj[A], Conj[A]) -> Conj[A]
# tal (union c1 c2) es la unión de ambos conjuntos. Por ejemplo,
# >>> ej1 = inserta(3, inserta(5, vacio()))
# >>> ej2 = inserta(4, inserta(3, vacio()))
# >>> union(ej1, ej2)
# {3, 4, 5}
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
c1 = elimina(mc1, c1)
return r
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_union(c1: Conj[int], c2: Conj[int]) -> None:
r = union(c1, c2)
assert union2(c1, c2) == r
assert union3(c1, c2) == r
assert union4(c1, c2) == r
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_unionG :: [Conj Int] -> Bool
684 Ejercicios de programación con Python
prop_unionG cs =
unionG cs == unionG2 cs
-- La comprobación es
-- λ> quickCheck prop_unionG
-- +++ OK, passed 100 tests.
8.12.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# unionG : (list[Conj[A]]) -> Conj[A]
# tal unionG(cs) calcule la unión de la lista de conjuntos cd. Por
# ejemplo,
# >>> ej1 = inserta(3, inserta(5, vacio()))
# >>> ej2 = inserta(5, inserta(6, vacio()))
# >>> ej3 = inserta(3, inserta(6, vacio()))
# >>> unionG([ej1, ej2, ej3])
# {3, 5, 6}
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
Capítulo 8. El tipo abstracto de datos de los conjuntos 685
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(conjuntoAleatorio(), max_size=10))
def test_union(cs: list[Conj[int]]) -> None:
r = unionG(cs)
assert unionG2(cs) == r
assert unionG3(cs) == r
686 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_interseccion :: Conj Int -> Conj Int -> Bool
prop_interseccion c1 c2 =
all (== interseccion c1 c2)
[interseccion2 c1 c2,
interseccion3 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_interseccion
-- +++ OK, passed 100 tests.
8.13.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los conjuntos](https://bit.ly/3HbB7fo
# definir la función
# interseccion : (Conj[A], Conj[A]) -> Conj[A]
# tal que interseccion(c1, c2) es la intersección de los conjuntos c1 y
688 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
690 Ejercicios de programación con Python
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_interseccion(c1: Conj[int], c2: Conj[int]) -> None:
r = interseccion(c1, c2)
assert interseccion2(c1, c2) == r
assert interseccion3(c1, c2) == r
assert interseccion4(c1, c2) == r
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_interseccionG :: NonEmptyList (Conj Int) -> Bool
prop_interseccionG (NonEmpty cs) =
interseccionG cs == interseccionG2 cs
-- La comprobación es
-- λ> quickCheck prop_interseccionG1
-- +++ OK, passed 100 tests.
8.14.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# interseccionG : (list[Conj[A]]) -> Conj[A]
# tal que interseccionG(cs) es la intersección de la lista de
# conjuntos cs. Por ejemplo,
# >>> ej1 = inserta(2, inserta(3, inserta(5, vacio())))
# >>> ej2 = inserta(5, inserta(2, inserta(7, vacio())))
# >>> ej3 = inserta(3, inserta(2, inserta(5, vacio())))
692 Ejercicios de programación con Python
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(conjuntoAleatorio(), min_size=1, max_size=10))
def test_interseccionG(cs: list[Conj[int]]) -> None:
r = interseccionG(cs)
assert interseccionG2(cs) == r
assert interseccionG3(cs) == r
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_disjuntos :: Conj Int -> Conj Int -> Bool
prop_disjuntos c1 c2 =
all (== disjuntos c1 c2)
[disjuntos2 c1 c2,
disjuntos3 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_disjuntos
-- +++ OK, passed 100 tests.
8.15.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# disjuntos : (Conj[A], Conj[A]) -> bool
# tal que disjuntos(c1, c2) se verifica si los conjuntos c1 y c2 son
# disjuntos. Por ejemplo,
# >>> ej1 = inserta(2, inserta(5, vacio()))
# >>> ej2 = inserta(4, inserta(3, vacio()))
# >>> ej3 = inserta(5, inserta(3, vacio()))
# >>> disjuntos(ej1, ej2)
# True
# >>> disjuntos(ej1, ej3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 697
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
698 Ejercicios de programación con Python
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_disjuntos(c1: Conj[int], c2: Conj[int]) -> None:
r = disjuntos(c1, c2)
assert disjuntos2(c1, c2) == r
assert disjuntos3(c1, c2) == r
assert disjuntos4(c1, c2) == r
assert disjuntos5(c1, c2) == r
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_diferencia :: Conj Int -> Conj Int -> Bool
prop_diferencia c1 c2 =
diferencia c1 c2 == diferencia2 c1 c2
-- La comprobación es
-- λ> quickCheck prop_diferencia
-- +++ OK, passed 100 tests.
700 Ejercicios de programación con Python
8.16.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# diferencia : (Conj[A], Conj[A]) -> Conj[A]
# tal que diferencia(c1, c2) es el conjunto de los elementos de c1 que
# no son elementos de c2. Por ejemplo,
# >>> ej1 = inserta(5, inserta(3, inserta(2, inserta(7, vacio()))))
# >>> ej2 = inserta(7, inserta(4, inserta(3, vacio())))
# >>> diferencia(ej1, ej2)
# {2, 5}
# >>> diferencia(ej2, ej1)
# {4}
# >>> diferencia(ej1, ej1)
# {}
# ---------------------------------------------------------------------
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 701
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
702 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_diferencia(c1: Conj[int], c2: Conj[int]) -> None:
r = diferencia(c1, c2)
assert diferencia2(c1, c2) == r
assert diferencia3(c1, c2) == r
assert diferencia4(c1, c2) == r
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_diferenciaSimetrica :: Conj Int -> Conj Int -> Bool
prop_diferenciaSimetrica c1 c2 =
diferenciaSimetrica c1 c2 == diferenciaSimetrica2 c2 c1
704 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_diferenciaSimetrica
-- +++ OK, passed 100 tests.
8.17.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# diferenciaSimetrica : (Conj[A], Conj[A]) -> Conj[A]
# tal que diferenciaSimetrica(c1, c2) es la diferencia simétrica de los
# conjuntos c1 y c2. Por ejemplo,
# >>> ej1 = inserta(5, inserta(3, inserta(2, inserta(7, vacio()))))
# >>> ej2 = inserta(7, inserta(4, inserta(3, vacio())))
# >>> diferenciaSimetrica(ej1, ej2)
# {2, 4, 5}
# ---------------------------------------------------------------------
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
Capítulo 8. El tipo abstracto de datos de los conjuntos 705
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
706 Ejercicios de programación con Python
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_diferenciaSimetrica(c1: Conj[int], c2: Conj[int]) -> None:
r = diferenciaSimetrica(c1, c2)
assert diferenciaSimetrica2(c1, c2) == r
assert diferenciaSimetrica3(c1, c2) == r
-- 1ª solución
-- ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 707
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_filtra :: (Int -> Bool) -> [Int] -> Bool
prop_filtra p xs =
filtra p c == filtra2 p c
where c = listaAconjunto xs
-- La comprobación es
-- λ> quickCheck' prop_filtra
-- +++ OK, passed 100 tests.
8.18.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# filtra : (Callable[[A], bool], Conj[A]) -> Conj[A]
# tal (filtra p c) es el conjunto de elementos de c que verifican el
# predicado p. Por ejemplo,
# >>> ej = inserta(5, inserta(4, inserta(7, inserta(2, vacio()))))
# >>> filtra(lambda x: x % 2 == 0, ej)
# {2, 4}
# >>> filtra(lambda x: x % 2 == 1, ej)
708 Ejercicios de programación con Python
# {5, 7}
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 709
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
710 Ejercicios de programación con Python
@given(c=conjuntoAleatorio())
def test_filtra(c: Conj[int]) -> None:
r = filtra(lambda x: x % 2 == 0, c)
assert filtra2(lambda x: x % 2 == 0, c) == r
assert filtra3(lambda x: x % 2 == 0, c) == r
assert filtra4(lambda x: x % 2 == 0, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_Subconjunto_por_propiedad.py
# 1 passed in 0.28s
-- 1ª solución
Capítulo 8. El tipo abstracto de datos de los conjuntos 711
-- ===========
particion :: Ord a => (a -> Bool) -> Conj a -> (Conj a, Conj a)
particion p c = (filtra p c, filtra (not . p) c)
-- 2ª solución
-- ===========
particion2 :: Ord a => (a -> Bool) -> Conj a -> (Conj a, Conj a)
particion2 p c = (listaAconjunto xs, listaAconjunto ys)
where
(xs, ys) = partition p (conjuntoAlista c)
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_particion :: (Int -> Bool) -> [Int] -> Bool
prop_particion p xs =
particion p c == particion2 p c
where c = listaAconjunto xs
-- La comprobación es
-- λ> quickCheck' prop_particion
-- +++ OK, passed 100 tests.
8.19.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# particion : (Callable[[A], bool], Conj[A]) -> tuple[Conj[A], Conj[A]]
# tal que particion(c) es el par formado por dos conjuntos: el de sus
# elementos que verifican p y el de los elementos que no lo
# verifica. Por ejemplo,
# >>> ej = inserta(5, inserta(4, inserta(7, inserta(2, vacio()))))
712 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
r: Conj[A] = vacio()
s: Conj[A] = vacio()
while not esVacio(c):
mc = menor(c)
c = elimina(mc, c)
if p(mc):
r = inserta(mc, r)
else:
s = inserta(mc, s)
return (r, s)
# 3ª solución
# ===========
# La propiedad es
@given(c=conjuntoAleatorio())
def test_particion(c: Conj[int]) -> None:
r = particion(lambda x: x % 2 == 0, c)
assert particion2(lambda x: x % 2 == 0, c) == r
assert particion3(lambda x: x % 2 == 0, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_Particion_por_una_propiedad.py
# 1 passed in 0.28s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_divide :: Int -> Conj Int -> Bool
prop_divide x c =
divide x c == divide2 x c
-- La comprobación es
-- λ> quickCheck prop_divide
-- +++ OK, passed 100 tests.
8.20.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# divide : (A, Conj[A]) -> tuple[Conj[A], Conj[A]]
# tal que (divide x c) es el par formado por dos subconjuntos de c: el
# de los elementos menores o iguales que x y el de los mayores que x.
# Por ejemplo,
# >>> divide(5, inserta(7, inserta(2, inserta(8, vacio()))))
# ({2}, {7, 8})
716 Ejercicios de programación con Python
# ---------------------------------------------------------------------
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(x=st.integers(), c=conjuntoAleatorio())
def test_particion(x: int, c: Conj[int]) -> None:
r = divide(x, c)
assert divide2(x, c) == r
assert divide3(x, c) == r
assert divide4(x, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_Particion_segun_un_numero.py
# 1 passed in 0.30s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_mapC :: (Int -> Int) -> [Int] -> Bool
prop_mapC f xs =
mapC f c == mapC2 f c
where c = listaAconjunto xs
-- La comprobación es
-- λ> quickCheck' prop_mapC
-- +++ OK, passed 100 tests.
8.21.2. En Python
# ---------------------------------------------------------------------
720 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
B = TypeVar('B', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(c=conjuntoAleatorio())
def test_mapPila(c: Conj[int]) -> None:
r = mapC(lambda x: 2 * x, c)
assert mapC2(lambda x: 2 * x, c) == r
assert mapC3(lambda x: 2 * x, c) == r
assert mapC4(lambda x: 2 * x, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_mapC.py
# 1 passed in 0.29s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_todos :: (Int -> Bool) -> [Int] -> Bool
prop_todos p xs =
todos p c == todos2 p c
where c = listaAconjunto xs
-- La comprobación es
-- λ> quickCheck' prop_todos
-- +++ OK, passed 100 tests.
8.22.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# todos : (Callable[[A], bool], Conj[A]) -> bool
# tal que todos(p, c) se verifica si todos los elemsntos de c
724 Ejercicios de programación con Python
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
Capítulo 8. El tipo abstracto de datos de los conjuntos 725
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# ================================================
# La propiedad es
@given(c=conjuntoAleatorio())
def test_todos(c: Conj[int]) -> None:
r = todos(lambda x: x % 2 == 0, c)
assert todos2(lambda x: x % 2 == 0, c) == r
assert todos3(lambda x: x % 2 == 0, c) == r
assert todos4(lambda x: x % 2 == 0, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_TodosVerificanConj.py
# 1 passed in 0.28s
-- 1ª solución
-- ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 727
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_algunos :: (Int -> Bool) -> [Int] -> Bool
prop_algunos p xs =
algunos p c == algunos2 p c
where c = listaAconjunto xs
-- La comprobación es
-- λ> quickCheck' prop_algunos
-- +++ OK, passed 100 tests.
8.23.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# algunos : algunos(Callable[[A], bool], Conj[A]) -> bool
# tal que algunos(p, c) se verifica si algún elemento de c verifica el
# predicado p. Por ejemplo,
# >>> algunos(lambda x: x % 2 == 0, inserta(4, inserta(7, vacio())))
728 Ejercicios de programación con Python
# True
# >>> algunos(lambda x: x % 2 == 0, inserta(3, inserta(7, vacio())))
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 729
# 3ª solución
# ===========
# 4ª solución
# ===========
# La propiedad es
@given(c=conjuntoAleatorio())
def test_algunos(c: Conj[int]) -> None:
r = algunos(lambda x: x % 2 == 0, c)
assert algunos2(lambda x: x % 2 == 0, c) == r
assert algunos3(lambda x: x % 2 == 0, c) == r
assert algunos4(lambda x: x % 2 == 0, c) == r
# La comprobación es
# src> poetry run pytest -q TAD_AlgunosVerificanConj.py
# 1 passed in 0.28s
-- 1ª solución
-- ===========
Capítulo 8. El tipo abstracto de datos de los conjuntos 731
productoC :: (Ord a, Ord b) => Conj a -> Conj b -> Conj (a,b)
productoC c1 c2
| esVacio c1 = vacio
| otherwise = agrega mc1 c2 `union` productoC rc1 c2
where mc1 = menor c1
rc1 = elimina mc1 c1
-- 2ª solución
-- ===========
productoC2 :: (Ord a, Ord b) => Conj a -> Conj b -> Conj (a,b)
productoC2 c1 c2 =
foldr inserta vacio [(x,y) | x <- xs, y <- ys]
where xs = conjuntoAlista c1
ys = conjuntoAlista c2
-- 3ª solución
-- ===========
productoC3 :: (Ord a, Ord b) => Conj a -> Conj b -> Conj (a,b)
productoC3 c1 c2 =
listaAconjunto [(x,y) | x <- xs, y <- ys]
where xs = conjuntoAlista c1
ys = conjuntoAlista c2
732 Ejercicios de programación con Python
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_productoC :: Conj Int -> Conj Int -> Bool
prop_productoC c1 c2 =
all (== productoC c1 c2)
[productoC2 c1 c2,
productoC3 c1 c2]
-- La comprobación es
-- λ> quickCheck prop_productoC
-- +++ OK, passed 100 tests.
8.24.2. En Python
# ---------------------------------------------------------------------
# Utilizando el tipo abstracto de datos de los conjuntos
# (https://bit.ly/3HbB7fo) definir la función
# productoC : (A, Conj[B]) -> Any
# tal que (productoC c1 c2) es el producto cartesiano de los
# conjuntos c1 y c2. Por ejemplo,
# >>> ej1 = inserta(2, inserta(5, vacio()))
# >>> ej2 = inserta(9, inserta(4, inserta(3, vacio())))
# >>> productoC(ej1, ej2)
# {(2, 3), (2, 4), (2, 9), (5, 3), (5, 4), (5, 9)}
# ---------------------------------------------------------------------
listaAconjunto)
from src.TAD_Union_de_dos_conjuntos import union
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
B = TypeVar('B', bound=Comparable)
# 1ª solución
# ===========
# 2ª solución
# ===========
734 Ejercicios de programación con Python
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_productoC(c1: Conj[int], c2: Conj[int]) -> None:
r = productoC(c1, c2)
assert productoC2(c1, c2) == r
assert productoC3(c1, c2) == r
assert productoC4(c1, c2) == r
Relaciones binarias
Contenido
9.1. El tipo de las relaciones binarias . . . . . . . . . . . . . . . .715
9.1.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .715
9.1.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .717
9.2. Universo y grafo de una relación binaria . . . . . . . . . . . .720
9.2.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .720
9.2.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .721
9.3. Relaciones reflexivas . . . . . . . . . . . . . . . . . . . . . . .722
9.3.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .722
9.3.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .723
9.4. Relaciones simétricas . . . . . . . . . . . . . . . . . . . . . . .725
9.4.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .725
9.4.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .726
9.5. Composición de relaciones binarias . . . . . . . . . . . . . .728
9.5.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .728
9.5.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .729
9.6. Reconocimiento de subconjunto . . . . . . . . . . . . . . . . .731
9.6.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .731
9.6.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .733
9.7. Relaciones transitivas . . . . . . . . . . . . . . . . . . . . . . .735
9.7.1. En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .735
9.7.2. En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .737
737
738 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_esRelacionBinaria :: Rel Int -> Bool
prop_esRelacionBinaria r =
esRelacionBinaria r &&
esRelacionBinaria2 r &&
esRelacionBinaria3 r
-- La comprobación es
-- λ> quickCheck prop_esRelacionBinaria
-- +++ OK, passed 100 tests.
9.1.2. En Python
# ---------------------------------------------------------------------
# Una relación binaria R sobre un conjunto A se puede representar
Capítulo 9. Relaciones binarias 741
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
return True
(x, y) = g[0]
return x in u and y in u and esRelacionBinaria2((u, g[1:]))
# 3ª solución
# ===========
# [3, 7]
# >>> sublistaArbitraria(range(10))
# []
# >>> sublistaArbitraria(range(10))
# [4, 1, 0, 9, 8, 7, 5, 6, 2, 3]
def sublistaArbitraria(xs: list[A]) -> list[A]:
n = len(xs)
k = randint(0, n)
return sample(xs, k)
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_esRelacionBinaria(n: int) -> None:
r = relacionArbitraria(n)
assert esRelacionBinaria(r)
assert esRelacionBinaria2(r)
assert esRelacionBinaria3(r)
# La comprobación es
# > poetry run pytest -q Relaciones_binarias.py
# 1 passed in 0.14s
744 Ejercicios de programación con Python
9.2.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir las siguientes funciones
# universo : (Rel[A]) -> list[A]
# grafo : (Rel[A]) -> list[tuple[A, A]]
# tales que
# + universo(r) es el universo de la relación r. Por ejemplo,
# >>> r = (list(range(1, 10)), [(1, 3), (2, 6), (8, 9), (2, 7)])
# >>> universo(r)
Capítulo 9. Relaciones binarias 745
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# + grafo(r) es el grafo de la relación r. Por ejemplo,
# >>> grafo(r)
# [(1, 3), (2, 6), (8, 9), (2, 7)]
# ---------------------------------------------------------------------
A = TypeVar('A')
-- 1ª solución
746 Ejercicios de programación con Python
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_reflexiva :: Rel Int -> Bool
prop_reflexiva r =
all (== reflexiva r)
[reflexiva2 r,
reflexiva3 r,
reflexiva4 r]
-- La comprobación es
-- λ> quickCheck prop_reflexiva
-- +++ OK, passed 100 tests.
Capítulo 9. Relaciones binarias 747
9.3.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# reflexiva : (Rel) -> bool
# tal que reflexiva(r) se verifica si la relación r es reflexiva. Por
# ejemplo,
# >>> reflexiva(([1, 3], [(1, 1),(1, 3),(3, 3)]))
# True
# >>> reflexiva(([1, 2, 3], [(1, 1),(1, 3),(3, 3)]))
# False
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
748 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_reflexiva(n: int) -> None:
r = relacionArbitraria(n)
res = reflexiva(r)
assert reflexiva2(r) == res
assert reflexiva3(r) == res
# La comprobación es
# > poetry run pytest -q Relaciones_reflexivas.py
# 1 passed in 0.41s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_simetrica :: Rel Int -> Bool
prop_simetrica r =
all (== simetrica r)
[simetrica2 r,
simetrica3 r]
-- La comprobación es
-- λ> quickCheck prop_simetrica
-- +++ OK, passed 100 tests.
750 Ejercicios de programación con Python
9.4.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# simetrica : (Rel[A]) -> bool
# tal que simetrica(r) se verifica si la relación r es simétrica. Por
# ejemplo,
# >>> simetrica(([1, 3], [(1, 1), (1, 3), (3, 1)]))
# True
# >>> simetrica(([1, 3], [(1, 1), (1, 3), (3, 2)]))
# False
# >>> simetrica(([1, 3], []))
# True
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
return aux(g)
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_simetrica(n: int) -> None:
r = relacionArbitraria(n)
res = simetrica(r)
assert simetrica2(r) == res
assert simetrica3(r) == res
# La comprobación es
# > poetry run pytest -q Relaciones_simetricas.py
# 1 passed in 0.11s
-- R ([1,2],[(1,1),(2,1)])
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_composicion :: Rel Int -> Rel Int -> Bool
prop_composicion r1 r2 =
composicion r1 r2 == composicion2 r1 r2
-- La comprobación es
-- λ> quickCheck prop_composicion
-- +++ OK, passed 100 tests.
Capítulo 9. Relaciones binarias 753
9.5.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# composicion : (Rel[A], Rel[A]) -> Rel[A]
# tal que composicion(r, s) es la composición de las relaciones r y
# s. Por ejemplo,
# >>> composicion(([1,2],[(1,2),(2,2)]), ([1,2],[(2,1)]))
# ([1, 2], [(1, 1), (2, 1)])
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10),
st.integers(min_value=0, max_value=10))
def test_simetrica(n: int, m: int) -> None:
r1 = relacionArbitraria(n)
r2 = relacionArbitraria(m)
res = composicion(r1, r2)
assert composicion2(r1, r2) == res
assert composicion2(r1, r2) == res
# La comprobación es
# > poetry run pytest -q Composicion_de_relaciones_binarias_v2.py
# 1 passed in 0.19s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Comprobación de equivalencia
756 Ejercicios de programación con Python
-- ============================
-- La propiedad es
prop_subconjunto :: [Int] -> [Int] -> Bool
prop_subconjunto xs ys =
all (== subconjunto1 xs ys)
[subconjunto2 xs ys,
subconjunto3 xs ys,
subconjunto4 xs ys]
-- La comprobación es
-- λ> quickCheck prop_subconjunto
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> subconjunto1 [1..2*10^4] [1..2*10^4]
-- True
-- (1.81 secs, 5,992,448 bytes)
-- λ> subconjunto2 [1..2*10^4] [1..2*10^4]
-- True
-- (1.83 secs, 6,952,200 bytes)
-- λ> subconjunto3 [1..2*10^4] [1..2*10^4]
-- True
-- (1.75 secs, 4,712,304 bytes)
-- λ> subconjunto4 [1..2*10^4] [1..2*10^4]
-- True
-- (0.04 secs, 6,312,056 bytes)
9.6.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# subconjunto : (list[A], list[A]) -> bool
Capítulo 9. Relaciones binarias 757
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
758 Ejercicios de programación con Python
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_filtraAplica(xs: list[int], ys: list[int]) -> None:
r = subconjunto1(xs, ys)
assert subconjunto2(xs, ys) == r
assert subconjunto3(xs, ys) == r
assert subconjunto4(xs, ys) == r
# La comprobación es
# src> poetry run pytest -q Reconocimiento_de_subconjunto.py
# 1 passed in 0.31s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> xs = list(range(2*10**4))
# >>> tiempo(”subconjunto1(xs, xs)”)
# 1.15 segundos
# >>> tiempo(”subconjunto2(xs, xs)”)
# 2.27 segundos
# >>> tiempo(”subconjunto3(xs, xs)”)
# 1.14 segundos
# >>> tiempo(”subconjunto4(xs, xs)”)
# 0.00 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_transitiva :: Rel Int -> Bool
prop_transitiva r =
transitiva1 r == transitiva2 r
-- La comprobación es
-- λ> quickCheck prop_transitiva
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> transitiva1 (R ([1..4001],[(x,x+1) | x <- [1..4000]]))
-- False
-- (3.15 secs, 898,932,776 bytes)
-- λ> transitiva2 (R ([1..4001],[(x,x+1) | x <- [1..4000]]))
-- False
-- (0.01 secs, 1,396,720 bytes)
--
-- λ> transitiva1 (R ([1..60], [(x,y) | x <- [1..60], y <- [1..60]]))
-- True
-- (2.71 secs, 852,578,456 bytes)
-- λ> transitiva2 (R ([1..60], [(x,y) | x <- [1..60], y <- [1..60]]))
-- True
-- (9.13 secs, 777,080,288 bytes)
Capítulo 9. Relaciones binarias 761
9.7.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# transitiva : (Rel[A]) -> bool
# tal que transitiva(r) se verifica si la relación r es transitiva.
# Por ejemplo,
# >>> transitiva(([1, 3, 5], [(1, 1), (1, 3), (3, 1), (3, 3), (5, 5)]))
# True
# >>> transitiva(([1, 3, 5], [(1, 1), (1, 3), (3, 1), (5, 5)]))
# False
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
return aux(g)
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
Capítulo 9. Relaciones binarias 763
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_simetrica(n: int) -> None:
r = relacionArbitraria(n)
res = transitiva1(r)
assert transitiva2(r) == res
assert transitiva3(r) == res
# La comprobación es
# > poetry run pytest -q Relaciones_transitivas.py
# 1 passed in 0.12s
# Comparación de eficiencia
# =========================
# La comparación es
# >>> u1 = range(6001)
# >>> g1 = [(x, x+1) for x in range(6000)]
# >>> tiempo(”transitiva1((u1, g1))”)
# 1.04 segundos
# >>> tiempo(”transitiva2((u1, g1))”)
# 0.00 segundos
# >>> tiempo(”transitiva3((u1, g1))”)
# 0.00 segundos
#
# >>> u2 = range(60)
# >>> g2 = [(x, y) for x in u2 for y in u2]
# >>> tiempo(”transitiva1((u2, g2))”)
# 0.42 segundos
# >>> tiempo(”transitiva2((u2, g2))”)
# 5.24 segundos
# >>> tiempo(”transitiva3((u2, g2))”)
# 4.83 segundos
764 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
Capítulo 9. Relaciones binarias 765
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_irreflexiva :: Rel Int -> Bool
prop_irreflexiva r =
all (== irreflexiva r)
[irreflexiva2 r,
irreflexiva3 r]
-- La comprobación es
-- λ> quickCheck prop_irreflexiva
-- +++ OK, passed 100 tests.
9.8.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# irreflexiva : (Rel[A]) -> bool
# tal que irreflexiva(r) se verifica si la relación r es irreflexiva;
# es decir, si ningún elemento de su universo está relacionado con
# él mismo. Por ejemplo,
# irreflexiva(([1, 2, 3], [(1, 2), (2, 1), (2, 3)])) == True
# irreflexiva(([1, 2, 3], [(1, 2), (2, 1), (3, 3)])) == False
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
return aux(u)
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_irreflexiva(n: int) -> None:
r = relacionArbitraria(n)
res = irreflexiva(r)
assert irreflexiva2(r) == res
Capítulo 9. Relaciones binarias 767
# La comprobación es
# > poetry run pytest -q Relaciones_irreflexivas.py
# 1 passed in 0.12s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
antisimetrica2 (R (_,g)) =
and [(y,x) `notElem` g | (x,y) <- g, x /= y]
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_antisimetrica :: Rel Int -> Bool
prop_antisimetrica r =
all (== antisimetrica r)
[antisimetrica2 r,
antisimetrica3 r,
antisimetrica4 r,
antisimetrica5 r]
Capítulo 9. Relaciones binarias 769
-- La comprobación es
-- λ> quickCheck prop_antisimetrica
-- +++ OK, passed 100 tests.
9.9.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# antisimetrica : (Rel[A]) -> bool
# tal que antisimetrica(r) se verifica si la relación r es
# antisimétrica; es decir, si (x,y) e (y,x) están relacionado, entonces
# x=y. Por ejemplo,
# >>> antisimetrica(([1,2],[(1,2)]))
# True
# >>> antisimetrica(([1,2],[(1,2),(2,1)]))
# False
# >>> antisimetrica(([1,2],[(1,1),(2,1)]))
# True
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
770 Ejercicios de programación con Python
# 3ª solución
# ===========
# 4ª solución
# ===========
return aux(g)
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 9. Relaciones binarias 771
@given(st.integers(min_value=0, max_value=10))
def test_antisimetrica(n: int) -> None:
r = relacionArbitraria(n)
res = antisimetrica(r)
assert antisimetrica2(r) == res
assert antisimetrica3(r) == res
assert antisimetrica4(r) == res
assert antisimetrica5(r) == res
# La comprobación es
# > poetry run pytest -q Relaciones_antisimetricas.py
# 1 passed in 0.13s
-- 1ª solución
-- ===========
total (R (u,g)) =
and [(x,y) `elem` g || (y,x) `elem` g | x <- u, y <- u]
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
Capítulo 9. Relaciones binarias 773
-- La comprobación es
-- λ> quickCheck prop_total
-- +++ OK, passed 100 tests.
9.10.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# total : (Rel[A]) -> bool
# tal que total(r) se verifica si la relación r es total; es decir, si
# para cualquier par x, y de elementos del universo de r, se tiene que
# x está relacionado con y o y está relacionado con x. Por ejemplo,
# total (([1,3],[(1,1),(3,1),(3,3)])) == True
# total (([1,3],[(1,1),(3,1)])) == False
# total (([1,3],[(1,1),(3,3)])) == False
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
if not xs:
return True
return aux2(xs[0], u) and aux1(xs[1:])
return aux1(u)
# 5ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_total(n: int) -> None:
r = relacionArbitraria(n)
res = total(r)
assert total2(r) == res
assert total3(r) == res
assert total4(r) == res
assert total5(r) == res
# La comprobación es
# > poetry run pytest -q Relaciones_totales.py
# 1 passed in 0.11s
-- definir la función
-- clausuraReflexiva :: Eq a => Rel a -> Rel a
-- tal que (clausuraReflexiva r) es la clausura reflexiva de r; es
-- decir, la menor relación reflexiva que contiene a r. Por ejemplo,
-- λ> clausuraReflexiva (R ([1,3],[(1,1),(3,1)]))
-- R ([1,3],[(1,1),(3,1),(3,3)])
-- ---------------------------------------------------------------------
9.11.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# clausuraReflexiva : (Rel[A]) -> Rel[A]
# tal que clausuraReflexiva(r) es la clausura reflexiva de r; es
# decir, la menor relación reflexiva que contiene a r. Por ejemplo,
# >>> clausuraReflexiva (([1,3],[(1,1),(3,1)]))
# ([1, 3], [(3, 1), (1, 1), (3, 3)])
# ---------------------------------------------------------------------
A = TypeVar('A')
-- La propiedad es
prop_ClausuraSimetrica :: Rel Int -> Bool
prop_ClausuraSimetrica r =
simetrica (clausuraSimetrica r)
-- La comprobación es
-- λ> quickCheck prop_ClausuraSimetrica
-- +++ OK, passed 100 tests.
778 Ejercicios de programación con Python
9.12.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# clausuraSimetrica : (Rel[A]) -> Rel[A]
# tal que clausuraSimetrica(r) es la clausura simétrica de r; es
# decir, la menor relación simétrica que contiene a r. Por ejemplo,
# >>> clausuraSimetrica(([1, 3, 5], [(1, 1), (3, 1), (1, 5)]))
# ([1, 3, 5], [(1, 5), (3, 1), (1, 1), (1, 3), (5, 1)])
#
# Comprobar con Hypothesis que clausuraSimetrica es simétrica.
# ---------------------------------------------------------------------
A = TypeVar('A')
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_irreflexiva(n: int) -> None:
r = relacionArbitraria(n)
assert simetrica(clausuraSimetrica(r))
# La comprobación es
# > poetry run pytest -q Clausura_simetrica.py
# 1 passed in 0.12s
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_clausuraTransitiva :: Rel Int -> Bool
prop_clausuraTransitiva r =
clausuraTransitiva r == clausuraTransitiva2 r
-- La comprobación es
-- λ> quickCheck prop_clausuraTransitiva
-- +++ OK, passed 100 tests.
-- Propiedad
-- =========
-- La propiedad es
prop_clausuraTransitivaEsTransitiva :: Rel Int -> Bool
prop_clausuraTransitivaEsTransitiva r =
transitiva (clausuraTransitiva r)
-- La comprobación es
-- λ> quickCheck prop_clausuraTransitivaEsTransitiva
-- +++ OK, passed 100 tests.
Capítulo 9. Relaciones binarias 781
9.13.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo de las relaciones binarias](https://bit.ly/3IVVqOT),
# definir la función
# clausuraTransitiva : (Rel[A]) -> Rel[A]
# tal que clausuraTransitiva(r) es la clausura transitiva de r; es
# decir, la menor relación transitiva que contiene a r. Por ejemplo,
# >>> clausuraTransitiva (([1, 2, 3, 4, 5, 6], [(1, 2), (2, 5), (5, 6)]))
# ([1, 2, 3, 4, 5, 6], [(1, 2), (2, 5), (5, 6), (2, 6), (1, 5), (1, 6)])
#
# Comprobar con Hypothesis que clausuraTransitiva es transitiva.
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
def union(xs: list[tuple[A, A]], ys: list[tuple[A, A]]) -> list[tuple[A, A]]:
return xs + [y for y in ys if y not in xs]
782 Ejercicios de programación con Python
# 2ª solución
# ===========
def union(xs: list[tuple[A, A]], ys: list[tuple[A, A]]) -> list[tuple[A, A]]:
return xs + [y for y in ys if y not in xs]
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 9. Relaciones binarias 783
@given(st.integers(min_value=0, max_value=10))
def test_clausuraTransitiva(n: int) -> None:
r = relacionArbitraria(n)
assert clausuraTransitiva(r) == clausuraTransitiva2(r)
# Propiedad
# =========
# La propiedad es
@given(st.integers(min_value=0, max_value=10))
def test_cla(n: int) -> None:
r = relacionArbitraria(n)
assert transitiva(clausuraTransitiva(r))
# La comprobación es
# > poetry run pytest -q Clausura_transitiva.py
# 2 passed in 0.16s
784 Ejercicios de programación con Python
Capítulo 10
Contenido
10.1. El tipo abstracto de datos de los polinomios . . . . . . . . .764
10.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .764
10.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .765
10.2. El TAD de los polinomios mediante tipos algebraicos . . . .767
10.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .767
10.3. El TAD de los polinomios mediante listas densas . . . . . . .772
10.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .772
10.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .778
10.4. El TAD de los polinomios mediante listas dispersas . . . . .785
10.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .785
10.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .791
10.5. Transformaciones entre las representaciones dispersa y den-
sa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .798
10.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .798
10.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .803
10.6. Transformaciones entre polinomios y listas dispersas . . . .807
10.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .807
10.6.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .810
10.7. Coeficiente del término de grado k . . . . . . . . . . . . . . .813
785
786 Ejercicios de programación con Python
10.28.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .867
10.29. Factorización de un polinomio . . . . . . . . . . . . . . . . . .869
10.29.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .869
10.29.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .871
-- + esPolCero polCero
-- + n > grado p && b /= 0 ==> not (esPolCero (consPol n b p))
-- + consPol (grado p) (coefLider p) (restoPol p) == p
-- + n > grado p && b /= 0 ==> grado (consPol n b p) == n
-- + n > grado p && b /= 0 ==> coefLider (consPol n b p) == b
-- + n > grado p && b /= 0 ==> restoPol (consPol n b p) == p
--
-- Para usar el TAD hay que usar una implementación concreta. En
-- principio, consideraremos las siguientes:
-- + mediante tipo de dato algebraico,
-- + mediante listas densas y
-- + mediante listas dispersas.
-- Hay que elegir la que se desee utilizar, descomentándola y comentando
-- las otras.
module TAD.Polinomio
( Polinomio,
polCero, -- Polinomio a
esPolCero, -- Polinomio a -> Bool
consPol, -- (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a
grado, -- Polinomio a -> Int
coefLider, -- Num a => Polinomio a -> a
restoPol -- (Num a, Eq a) => Polinomio a -> Polinomio a
) where
import TAD.PolRepTDA
-- import TAD.PolRepDensa
-- import TAD.PolRepDispersa
10.1.2. En Python
# Un polinomio es una expresión matemática compuesta por una suma de
# términos, donde cada término es el producto de un coeficiente y una
# variable elevada a una potencia. Por ejemplo, el polinomio 3x^2+2x-1
# tiene un término de segundo grado (3x^2), un término de primer grado
# (2x) y un término constante (-1).
#
# Las operaciones que definen al tipo abstracto de datos (TAD) de los
790 Ejercicios de programación con Python
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
Capítulo 10. El tipo abstracto de datos de los polinomios 791
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
module TAD.PolRepTDA
( Polinomio,
polCero, -- Polinomio a
esPolCero, -- Polinomio a -> Bool
consPol, -- (Num a, Eq a)) => Int -> a -> Polinomio a -> Polinomio a
grado, -- Polinomio a -> Int
coefLider, -- Num t => Polinomio t -> t
restoPol -- Polinomio t -> Polinomio t
) where
import Test.QuickCheck
-- Comprobación de escritura:
-- > ejPol1
-- 3*x^4 + -5*x^2 + 3
-- > ejPol2
-- x^5 + 5*x^2 + 4*x
-- > ejPol3
-- 6*x^4 + 2*x
-- > polCero
-- 0
polCero :: Polinomio a
polCero = PolCero
coefLider (ConsPol _ b _) = b
-- Generador de polinomios --
-- =======================
-- Verificación
796 Ejercicios de programación con Python
-- ============
return []
verificaPol :: IO Bool
verificaPol = $quickCheckAll
-- La verificación es
-- λ> verificaPol
-- === prop_polCero_es_cero from PolPropiedades.hs:53 ===
-- +++ OK, passed 1 test.
--
-- === prop_consPol_no_cero from PolPropiedades.hs:63 ===
-- +++ OK, passed 100 tests; 251 discarded.
--
-- === prop_consPol from PolPropiedades.hs:73 ===
-- +++ OK, passed 100 tests.
--
-- === prop_grado from PolPropiedades.hs:83 ===
-- +++ OK, passed 100 tests; 321 discarded.
--
-- === prop_coefLider from PolPropiedades.hs:94 ===
-- +++ OK, passed 100 tests; 340 discarded.
--
-- === prop_restoPol from PolPropiedades.hs:105 ===
-- +++ OK, passed 100 tests; 268 discarded.
--
-- True
module TAD.PolRepDensa
( Polinomio,
Capítulo 10. El tipo abstracto de datos de los polinomios 797
polCero, -- Polinomio a
esPolCero, -- Polinomio a -> Bool
consPol, -- (Num a, Eq a) => Int -> a -> Polinomio a -> Polinomio a
grado, -- Polinomio a -> Int
coefLider, -- Num a => Polinomio a -> a
restoPol -- (Num a, Eq a) => Polinomio a -> Polinomio a
) where
import Test.QuickCheck
-- Comprobación de escritura:
-- > ejPol1
-- 3*x^4 + -5*x^2 + 3
-- > ejPol2
-- x^5 + 5*x^2 + 4*x
-- > ejPol3
-- 6*x^4 + 2*x
consPol _ 0 p = p
consPol n b p@(Pol xs)
| esPolCero p = Pol (b : replicate n 0)
| n > m = Pol (b : replicate (n-m-1) 0 ++ xs)
| n < m = consPol m c (consPol n b (restoPol p))
| b+c == 0 = Pol (dropWhile (==0) (tail xs))
| otherwise = Pol ((b+c):tail xs)
where
c = coefLider p
m = grado p
-- Generador de polinomios
-- =======================
800 Ejercicios de programación con Python
-- Verificación
-- ============
return []
verificaPol :: IO Bool
verificaPol = $quickCheckAll
-- La verificación es
-- λ> verificaPol
-- === prop_polCero_es_cero from /home/jalonso/alonso/estudio/Exercitium/Exerc
-- +++ OK, passed 1 test.
--
-- === prop_consPol_no_cero from /home/jalonso/alonso/estudio/Exercitium/Exerc
-- +++ OK, passed 100 tests; 274 discarded.
802 Ejercicios de programación con Python
--
-- === prop_consPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/sr
-- +++ OK, passed 100 tests.
--
-- === prop_grado from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/
-- +++ OK, passed 100 tests; 297 discarded.
--
-- === prop_coefLider from /home/jalonso/alonso/estudio/Exercitium/Exercitium/
-- +++ OK, passed 100 tests; 248 discarded.
--
-- === prop_restoPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/s
-- +++ OK, passed 100 tests; 322 discarded.
--
-- True
10.3.2. En Python
# Representaremos un polinomio por la lista de sus coeficientes ordenados
# en orden decreciente según el grado. Por ejemplo, el polinomio
# 6x^4 -5x^2 + 4x -7
# se representa por
# [6,0,-2,4,-7].
#
# En la representación se supone que, si la lista no es vacía, su
# primer elemento es distinto de cero.
#
# Se define la clase Polinomio con los siguientes métodos:
# + esPolCero() se verifica si es el polinomio cero.
# + consPol(n, b) es el polinomio obtenido añadiendo el térmiono bx^n
# + grado() es el grado del polinomio.
# + coefLider() es el coeficiente líder del polinomio.
# + restoPol() es el resto del polinomio.
# Por ejemplo,
# >>> Polinomio()
# 0
# >>> ejPol1 = Polinomio().consPol(0,3).consPol(2,-5).consPol(4,3)
# >>> ejPol1
# 3*x^4 + -5*x^2 + 3
# >>> ejPol2 = Polinomio().consPol(1,4).consPol(2,5).consPol(5,1)
# >>> ejPol2
Capítulo 10. El tipo abstracto de datos de los polinomios 803
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
Capítulo 10. El tipo abstracto de datos de los polinomios 805
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
@dataclass
class Polinomio(Generic[A]):
_coeficientes: list[A] = field(default_factory=list)
if xs[1] == 0:
return Polinomio(list(dropwhile(lambda x: x == 0, xs[2:])))
return Polinomio(xs[1:])
# Generador de polinomios
# =======================
# >>> polinomioAleatorio().example()
# x^2 + 7*x + -1
def polinomioAleatorio() -> st.SearchStrategy[Polinomio[int]]:
return st.lists(st.integers(min_value=-9, max_value=9), max_size=10)\
.map(lambda xs: normal(xs))\
.map(Polinomio)
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_esPolCero2(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert not esPolCero(consPol(n, b, p))
@given(p=polinomioAleatorio())
def test_consPol(p: Polinomio[int]) -> None:
assume(not esPolCero(p))
assert consPol(grado(p), coefLider(p), restoPol(p)) == p
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_grado(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert grado(consPol(n, b, p)) == n
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_coefLider(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert coefLider(consPol(n, b, p)) == b
Capítulo 10. El tipo abstracto de datos de los polinomios 809
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_restoPol(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert restoPol(consPol(n, b, p)) == p
# La comprobación es
# > poetry run pytest -v PolRepDensa.py
#
# PolRepDensa.py::test_esPolCero1 PASSED
# PolRepDensa.py::test_esPolCero2 PASSED
# PolRepDensa.py::test_consPol PASSED
# PolRepDensa.py::test_grado PASSED
# PolRepDensa.py::test_coefLider PASSED
# PolRepDensa.py::test_restoPol PASSED
#
# === 6 passed in 1.64s ===
module TAD.PolRepDispersa
( Polinomio,
polCero, -- Polinomio a
esPolCero, -- Num a => Polinomio a -> Bool
consPol, -- Num a => Int -> a -> Polinomio a -> Polinomio a
grado, -- Polinomio a -> Int
coefLider, -- Num a => Polinomio a -> a
restoPol -- Polinomio a -> Polinomio a
) where
import Test.QuickCheck
810 Ejercicios de programación con Python
-- Comprobación de escritura:
-- > ejPol1
-- 3*x^4 + -5*x^2 + 3
-- > ejPol2
-- x^5 + 5*x^2 + 4*x
-- > ejPol3
-- 6*x^4 + 2*x
c = coefLider p
m = grado p
-- Generador de polinomios --
-- =======================
-- 5*x^10 + 4*x^4 + -3
-- 3*x^3 + -4
-- 10*x
genPol :: (Num a, Arbitrary a, Eq a) => Int -> Gen (Polinomio a)
genPol 0 = return polCero
genPol _ = do
n <- choose (0,10)
b <- arbitrary
p <- genPol (div n 2)
return (consPol n b p)
-- Verificación
-- ============
return []
verificaPol :: IO Bool
verificaPol = $quickCheckAll
-- La verificación es
-- λ> verificaPol
-- === prop_polCero_es_cero from /home/jalonso/alonso/estudio/Exercitium/Exerc
-- +++ OK, passed 1 test.
--
-- === prop_consPol_no_cero from /home/jalonso/alonso/estudio/Exercitium/Exerc
-- +++ OK, passed 100 tests; 264 discarded.
--
-- === prop_consPol from /home/jalonso/alonso/estudio/Exercitium/Exercitium/sr
-- +++ OK, passed 100 tests.
--
-- === prop_grado from /home/jalonso/alonso/estudio/Exercitium/Exercitium/src/
-- +++ OK, passed 100 tests; 266 discarded.
--
-- === prop_coefLider from /home/jalonso/alonso/estudio/Exercitium/Exercitium/
-- +++ OK, passed 100 tests; 251 discarded.
--
Capítulo 10. El tipo abstracto de datos de los polinomios 815
10.4.2. En Python
# Representaremos un polinomio mediante una lista de pares (grado,coef),
# ordenados en orden decreciente según el grado. Por ejemplo, el
# polinomio
# 6x^4 -5x^2 + 4x -7
# se representa por
# [(4,6),(2,-5),(1,4),(0,-7)].
#
# En la representación se supone que los primeros elementos de los
# pares forman una sucesión estrictamente decreciente y que los
# segundos elementos son distintos de cero.
#
# Se define la clase Polinomio con los siguientes métodos:
# + esPolCero() se verifica si es el polinomio cero.
# + consPol(n, b) es el polinomio obtenido añadiendo el térmiono bx^n
# + grado() es el grado del polinomio.
# + coefLider() es el coeficiente líder del polinomio.
# + restoPol() es el resto del polinomio.
# Por ejemplo,
# >>> Polinomio()
# 0
# >>> ejPol1 = Polinomio().consPol(0,3).consPol(2,-5).consPol(4,3)
# >>> ejPol1
# 3*x^4 + -5*x^2 + 3
# >>> ejPol2 = Polinomio().consPol(1,4).consPol(2,5).consPol(5,1)
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> ejPol3 = Polinomio().consPol(1,2).consPol(4,6)
# >>> ejPol3
# 6*x^4 + 2*x
# >>> Polinomio().esPolCero()
# True
# >>> ejPol1.esPolCero()
# False
816 Ejercicios de programación con Python
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> ejPol2.consPol(3,0)
# x^5 + 5*x^2 + 4*x
# >>> Polinomio().consPol(3,2)
# 2*x^3
# >>> ejPol2.consPol(6,7)
# 7*x^6 + x^5 + 5*x^2 + 4*x
# >>> ejPol2.consPol(4,7)
# x^5 + 7*x^4 + 5*x^2 + 4*x
# >>> ejPol2.consPol(5,7)
# 8*x^5 + 5*x^2 + 4*x
# >>> ejPol3
# 6*x^4 + 2*x
# >>> ejPol3.grado()
# 4
# >>> ejPol3.restoPol()
# 2*x
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> ejPol2.restoPol()
# 5*x^2 + 4*x
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> polCero()
# 0
# >>> ejPol1a = consPol(4,3,consPol(2,-5,consPol(0,3,polCero())))
# >>> ejPol1a
# 3*x^4 + -5*x^2 + 3
# >>> ejPol2a = consPol(5,1,consPol(2,5,consPol(1,4,polCero())))
# >>> ejPol2a
# x^5 + 5*x^2 + 4*x
# >>> ejPol3a = consPol(4,6,consPol(1,2,polCero()))
# >>> ejPol3a
# 6*x^4 + 2*x
# >>> esPolCero(polCero())
# True
# >>> esPolCero(ejPol1a)
# False
# >>> ejPol2a
Capítulo 10. El tipo abstracto de datos de los polinomios 817
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
@dataclass
class Polinomio(Generic[A]):
_terminos: list[tuple[int, A]] = field(default_factory=list)
# Generador de polinomios
# =======================
@given(p=polinomioAleatorio(),
Capítulo 10. El tipo abstracto de datos de los polinomios 821
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_esPolCero2(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert not esPolCero(consPol(n, b, p))
@given(p=polinomioAleatorio())
def test_consPol(p: Polinomio[int]) -> None:
assume(not esPolCero(p))
assert consPol(grado(p), coefLider(p), restoPol(p)) == p
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_grado(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert grado(consPol(n, b, p)) == n
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_coefLider(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert coefLider(consPol(n, b, p)) == b
@given(p=polinomioAleatorio(),
n=st.integers(min_value=0, max_value=10),
b=st.integers())
def test_restoPol(p: Polinomio[int], n: int, b: int) -> None:
assume(n > grado(p) and b != 0)
assert restoPol(consPol(n, b, p)) == p
# La comprobación es
# > poetry run pytest -v PolRepDispersa.py
#
# PolRepDispersa.py::test_esPolCero1 PASSED
# PolRepDispersa.py::test_esPolCero2 PASSED
# PolRepDispersa.py::test_consPol PASSED
# PolRepDispersa.py::test_grado PASSED
# PolRepDispersa.py::test_coefLider PASSED
822 Ejercicios de programación con Python
# PolRepDispersa.py::test_restoPol PASSED
#
# === 6 passed in 1.74s ===
-- 1ª definición de densaAdispersa
-- ===============================
where n = length xs
-- 2ª definición de densaAdispersa
-- ===============================
-- La propiedad es
prop_densaAdispersa :: [Int] -> Bool
prop_densaAdispersa xs =
densaAdispersa xs == densaAdispersa2 xs
-- La comprobación es
-- λ> quickCheck prop_densaAdispersa
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> densaAdispersa (5 : replicate (10^7) 0)
-- [(10000000,5)]
-- (4.54 secs, 3,280,572,504 bytes)
-- λ> densaAdispersa2 (5 : replicate (10^7) 0)
-- [(10000000,5)]
-- (7.35 secs, 3,696,968,576 bytes)
-- 1ª definición de dispersaAdensa
-- ===============================
dispersaAdensa ((n,a):(m,b):ps) =
a : replicate (n-m-1) 0 ++ dispersaAdensa ((m,b):ps)
-- 2ª definición de dispersaAdensa
-- ===============================
-- Dis [(17,5),(16,1),(15,-1),(14,10),(13,5),(12,-15),(9,12),(6,14)]
-- Dis [(19,17),(12,7),(8,-3),(7,13),(5,-2),(4,7)]
dispersaArbitraria :: Gen Dispersa
dispersaArbitraria = do
(xs, ys) <- arbitrary
let xs' = nub (reverse (sort (map abs xs)))
ys' = filter (/= 0) ys
return (Dis (zip xs' ys'))
-- La propiedad es
prop_dispersaAdensa :: Dispersa -> Bool
prop_dispersaAdensa (Dis xs) =
dispersaAdensa xs == dispersaAdensa2 xs
-- La comprobación es
-- λ> quickCheck prop_dispersaAdensa
-- +++ OK, passed 100 tests.
-- La comparación es
-- λ> length (dispersaAdensa [(10^7,5)])
-- 10000001
-- (0.11 secs, 560,566,848 bytes)
-- λ> length (dispersaAdensa2 [(10^7,5)])
-- 10000001
-- (2.51 secs, 2,160,567,112 bytes)
-- Propiedad
-- =========
-- La primera propiedad es
prop_dispersaAdensa_densaAdispersa :: Densa -> Bool
prop_dispersaAdensa_densaAdispersa (Den xs) =
dispersaAdensa (densaAdispersa xs) == xs
-- La comprobación es
-- λ> quickCheck prop_dispersaAdensa_densaAdispersa
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_densaAdispersa_dispersaAdensa :: Dispersa -> Bool
prop_densaAdispersa_dispersaAdensa (Dis ps) =
densaAdispersa (dispersaAdensa ps) == ps
-- La comprobación es
-- λ> quickCheck prop_densaAdispersa_dispersaAdensa
Capítulo 10. El tipo abstracto de datos de los polinomios 827
10.5.2. En Python
# ---------------------------------------------------------------------
# Definir las funciones
# densaAdispersa : (list[A]) -> list[tuple[int, A]]
# dispersaAdensa : (list[tuple[int, A]]) -> list[A]
# tales que
# + densaAdispersa(xs) es la representación dispersa del polinomio
# cuya representación densa es xs. Por ejemplo,
# >>> densaAdispersa([9, 0, 0, 5, 0, 4, 7])
# [(6, 9), (3, 5), (1, 4), (0, 7)]
# + dispersaAdensa(ps) es la representación densa del polinomio
# cuya representación dispersa es ps. Por ejemplo,
# >>> dispersaAdensa([(6,9),(3,5),(1,4),(0,7)])
# [9, 0, 0, 5, 0, 4, 7]
#
# Comprobar con Hypothesis que las funciones densaAdispersa y
# dispersaAdensa son inversas.
# ---------------------------------------------------------------------
# 1ª definición de densaAdispersa
# ===============================
# 2ª definición de densaAdispersa
# ===============================
828 Ejercicios de programación con Python
# 3ª definición de densaAdispersa
# ===============================
# La propiedad es
@given(xs=densaAleatoria())
def test_densaADispersa(xs: list[int]) -> None:
r = densaAdispersa(xs)
assert densaAdispersa2(xs) == r
assert densaAdispersa3(xs) == r
# 1ª definición de dispersaAdensa
# ===============================
# 2ª definición de dispersaAdensa
# ===============================
n = ps[0][0]
return [coeficiente(ps, m) for m in range(n, -1, -1)]
# 3ª definición de dispersaAdensa
# ===============================
# La propiedad es
@given(ps=dispersaAleatoria())
def test_dispersaAdensa(ps: list[tuple[int, int]]) -> None:
Capítulo 10. El tipo abstracto de datos de los polinomios 831
r = dispersaAdensa(ps)
assert dispersaAdensa2(ps) == r
assert dispersaAdensa3(ps) == r
# Propiedad
# =========
# La primera propiedad es
@given(xs=densaAleatoria())
def test_dispersaAdensa_densaAdispersa(xs: list[int]) -> None:
assert dispersaAdensa(densaAdispersa(xs)) == xs
# La segunda propiedad es
@given(ps=dispersaAleatoria())
def test_densaAdispersa_dispersaAdensa(ps: list[tuple[int, int]]) -> None:
assert densaAdispersa(dispersaAdensa(ps)) == ps
# La comprobación es
# > poetry run pytest -v Pol_Transformaciones_dispersa_y_densa.py
# test_densaADispersa PASSED
# test_dispersaAdensa PASSED
# test_dispersaAdensa_densaAdispersa PASSED
# test_densaAdispersa_dispersaAdensa PASSED
-- 1ª definición de dispersaApolinomio
-- ===================================
-- 2ª definición de dispersaApolinomio
-- ===================================
-- 3ª definición de dispersaApolinomio
-- ===================================
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_dispersaApolinomio :: Dispersa -> Bool
prop_dispersaApolinomio (Dis ps) =
all (== dispersaApolinomio ps)
[dispersaApolinomio2 ps,
dispersaApolinomio3 ps]
-- Definición de polinomioAdispersa
834 Ejercicios de programación con Python
-- ================================
-- La primera propiedad es
prop_polinomioAdispersa_dispersaApolinomio :: Dispersa -> Bool
prop_polinomioAdispersa_dispersaApolinomio (Dis ps) =
polinomioAdispersa (dispersaApolinomio ps) == ps
-- La comprobación es
-- λ> quickCheck prop_polinomioAdispersa_dispersaApolinomio
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_dispersaApolinomio_polinomioAdispersa :: Polinomio Int -> Bool
prop_dispersaApolinomio_polinomioAdispersa p =
dispersaApolinomio (polinomioAdispersa p) == p
-- La comprobación es
-- λ> quickCheck prop_dispersaApolinomio_polinomioAdispersa
-- +++ OK, passed 100 tests.
10.6.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los polinomios]
# (https://bit.ly/3KwqXYu) definir las funciones
# dispersaApolinomio : (list[tuple[int, A]]) -> Polinomio[A]
# polinomioAdispersa : (Polinomio[A]) -> list[tuple[int, A]]
# tales que
# + dispersaApolinomio(ps) es el polinomiocuya representación dispersa
# es ps. Por ejemplo,
# >>> dispersaApolinomio([(6, 9), (3, 5), (1, 4), (0, 7)])
# 9*x^6 + 5*x^3 + 4*x + 7
Capítulo 10. El tipo abstracto de datos de los polinomios 835
# 1ª definición de dispersaApolinomio
# ===================================
# 2ª definición de dispersaApolinomio
# ===================================
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(ps=dispersaAleatoria())
def test_dispersaApolinomio(ps: list[tuple[int, int]]) -> None:
assert dispersaApolinomio(ps) == dispersaApolinomio2(ps)
# Definición de polinomioAdispersa
# ================================
# La primera propiedad es
@given(ps=dispersaAleatoria())
def test_polinomioAdispersa_dispersaApolinomio(ps: list[tuple[int,
int]]) -> None:
assert polinomioAdispersa(dispersaApolinomio(ps)) == ps
# La segunda propiedad es
@given(p=polinomioAleatorio())
def test_dispersaApolinomio_polinomioAdispersa(p: Polinomio[int]) -> None:
assert dispersaApolinomio(polinomioAdispersa(p)) == p
# La comprobación es
# > poetry run pytest -v Pol_Transformaciones_polinomios_dispersas.py
# test_dispersaApolinomio PASSED
# test_polinomioAdispersa_dispersaApolinomio PASSED
# test_dispersaApolinomio_polinomioAdispersa PASSED
Capítulo 10. El tipo abstracto de datos de los polinomios 837
10.7.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los polinomios]
# (https://bit.ly/3KwqXYu) definir la función
# coeficiente : (int, Polinomio[A]) -> A
# tal que coeficiente(k, p) es el coeficiente del término de grado k
# del polinomio p. Por ejemplo,
838 Ejercicios de programación con Python
# pylint: disable=unused-import
-- 1ª definición de densaApolinomio
-- ================================
-- 2ª definición de densaApolinomio
-- ================================
-- La propiedad es
prop_densaApolinomio :: [Int] -> Bool
prop_densaApolinomio xs =
densaApolinomio xs == densaApolinomio2 xs
-- La comprobación es
-- λ> quickCheck prop_densaApolinomio
-- +++ OK, passed 100 tests.
-- 1ª definición de polinomioAdensa
-- ================================
-- 2ª definición de polinomioAdensa
-- ================================
-- encuentra en https://bit.ly/3GTyIqe
-- La propiedad es
prop_polinomioAdensa :: Polinomio Int -> Bool
prop_polinomioAdensa p =
polinomioAdensa p == polinomioAdensa2 p
-- La comprobación es
-- λ> quickCheck prop_polinomioAdensa
-- +++ OK, passed 100 tests.
-- Propiedades de inversa
-- ======================
-- La primera propiedad es
prop_polinomioAdensa_densaApolinomio :: [Int] -> Bool
prop_polinomioAdensa_densaApolinomio xs =
polinomioAdensa (densaApolinomio xs') == xs'
where xs' = dropWhile (== 0) xs
-- La comprobación es
-- λ> quickCheck prop_polinomioAdensa_densaApolinomio
-- +++ OK, passed 100 tests.
-- La segunda propiedad es
prop_densaApolinomio_polinomioAdensa :: Polinomio Int -> Bool
prop_densaApolinomio_polinomioAdensa p =
densaApolinomio (polinomioAdensa p) == p
-- La comprobación es
-- λ> quickCheck prop_densaApolinomio_polinomioAdensa
-- +++ OK, passed 100 tests.
842 Ejercicios de programación con Python
10.8.2. En Python
# ---------------------------------------------------------------------
# Utilizando el [tipo abstracto de datos de los polinomios]
# (https://bit.ly/3KwqXYu) definir las funciones
# densaApolinomio : (list[A]) -> Polinomio[A]
# polinomioAdensa : (Polinomio[A]) -> list[A]
# tales que
# + densaApolinomio(xs) es el polinomio cuya representación densa es
# xs. Por ejemplo,
# >>> densaApolinomio([9, 0, 0, 5, 0, 4, 7])
# 9*x^6 + 5*x^3 + 4*x + 7
# + polinomioAdensa(c) es la representación densa del polinomio p. Por
# ejemplo,
# >>> ejPol = consPol(6, 9, consPol(3, 5, consPol(1, 4, consPol(0, 7, polCer
# >>> ejPol
# 9*x^6 + 5*x^3 + 4*x + 7
# >>> polinomioAdensa(ejPol)
# [9, 0, 0, 5, 0, 4, 7]
#
# Comprobar con Hypothesis que ambas funciones son inversas.
# ---------------------------------------------------------------------
# pylint: disable=unused-import
# 1ª definición de densaApolinomio
Capítulo 10. El tipo abstracto de datos de los polinomios 843
# ================================
# 2ª definición de densaApolinomio
# ================================
# La propiedad es
@given(xs=densaAleatoria())
def test_densaApolinomio(xs: list[int]) -> None:
assert densaApolinomio(xs) == densaApolinomio2(xs)
# 1ª definición de polinomioAdensa
# ================================
# 2ª definición de polinomioAdensa
# ================================
# La propiedad es
@given(p=polinomioAleatorio())
def test_polinomioAdensa(p: Polinomio[int]) -> None:
assert polinomioAdensa(p) == polinomioAdensa2(p)
# Propiedades de inversa
# ======================
# La primera propiedad es
@given(xs=densaAleatoria())
def test_polinomioAdensa_densaApolinomio(xs: list[int]) -> None:
assert polinomioAdensa(densaApolinomio(xs)) == xs
# La segunda propiedad es
@given(p=polinomioAleatorio())
def test_densaApolinomio_polinomioAdensa(p: Polinomio[int]) -> None:
assert densaApolinomio(polinomioAdensa(p)) == p
Capítulo 10. El tipo abstracto de datos de los polinomios 845
# La comprobación es
# > poetry run pytest -v Pol_Transformaciones_polinomios_densas.py
# test_densaApolinomio PASSED
# test_polinomioAdensa PASSED
# test_polinomioAdensa_densaApolinomio PASSED
# test_densaApolinomio_polinomioAdensa PASSED
10.9.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# creaTermino : (int, A) -> Polinomio[A]
# tal que creaTermino(n, a) es el término a*x^n. Por ejemplo,
# >>> creaTermino(2, 5)
# 5*x^2
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# La propiedad es
@given(st.integers(min_value=0, max_value=9),
st.integers(min_value=-9, max_value=9))
def test_creaTermino(n: int, a: int) -> None:
assert creaTermino(n, a) == creaTermino2(n, a)
# La comprobación es
# > poetry run pytest -q Pol_Crea_termino.py
# 1 passed in 0.21s
-- definir la función
-- termLider :: (Num a, Eq a) => Polinomio a -> Polinomio a
-- tal que (termLider p) es el término líder del polinomio p. Por
-- ejemplo,
-- λ> ejPol = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero))
-- λ> ejPol
-- x^5 + 5*x^2 + 4*x
-- λ> termLider ejPol
-- x^5
-- ---------------------------------------------------------------------
10.10.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# termLider : (Polinomio[A]) -> Polinomio[A]
# tal que termLider(p) es el término líder del polinomio p. Por
# ejemplo,
# >>> ejPol = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol
# x^5 + 5*x^2 + 4*x
# >>> termLider(ejPol)
# x^5
# ---------------------------------------------------------------------
848 Ejercicios de programación con Python
# pylint: disable=unused-import
# 1ª solución
# ===========
# 2ª solución
# ===========
# La propiedad es
@given(p=polinomioAleatorio())
def test_termLider(p: Polinomio[int]) -> None:
assert termLider(p) == termLider2(p)
# La comprobación es
# > poetry run pytest -q Pol_Termino_lider.py
# 1 passed in 0.21s
Capítulo 10. El tipo abstracto de datos de los polinomios 849
-- Comprobación:
-- λ> quickCheck prop_conmutativaSuma
-- OK, passed 100 tests.
10.11.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# sumaPol : (Polinomio[A], Polinomio[A]) -> Polinomio[A]
# tal que sumaPol(p, q) es la suma de los polinomios p y q. Por ejemplo,
# >>> ejPol1 = consPol(4, 3, consPol(2, -5, consPol(0, 3, polCero())))
# >>> ejPol2 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol1
# 3*x^4 + -5*x^2 + 3
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> sumaPol(ejPol1, ejPol2)
# x^5 + 3*x^4 + 4*x + 3
#
# Comprobar con Hypothesis las siguientes propiedades:
# + polCero es el elemento neutro de la suma.
# + la suma es conmutativa.
# ---------------------------------------------------------------------
# pylint: disable=arguments-out-of-order
Capítulo 10. El tipo abstracto de datos de los polinomios 851
# 1ª solución
# ===========
# 2ª solución
# ===========
# La propiedad es
@given(p=polinomioAleatorio(), q=polinomioAleatorio())
def test_sumaPol(p: Polinomio[int], q: Polinomio[int]) -> None:
assert sumaPol(p, q) == sumaPol2(p,q)
# La suma es conmutativa.
@given(p=polinomioAleatorio(), q=polinomioAleatorio())
def test_conmutativaSuma(p: Polinomio[int], q: Polinomio[int]) -> None:
assert sumaPol(p, q) == sumaPol(q, p)
# La comprobación es
# > poetry run pytest -v Pol_Suma_de_polinomios.py
# test_sumaPol PASSED
# test_neutroSumaPol PASSED
# test_conmutativaSuma PASSED
-- 3*x^4 + -5*x^2 + 3
-- λ> ejPol2
-- x^5 + 5*x^2 + 4*x
-- λ> multPol ejPol1 ejPol2
-- 3*x^9 + -5*x^7 + 15*x^6 + 15*x^5 + -25*x^4 + -20*x^3 + 15*x^2 + 12*x
--
-- Comprobar con QuickCheck las siguientes propiedades
-- + El producto de polinomios es conmutativo.
-- + El producto es distributivo respecto de la suma.
-- ---------------------------------------------------------------------
r = restoPol pol
-- La comprobación es
-- λ> quickCheck prop_conmutativaProducto
-- OK, passed 100 tests.
-- Comprobación:
-- λ> quickCheck prop_distributivaProductoSuma
-- OK, passed 100 tests.
10.12.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# multPol : (Polinomio[A], Polinomio[A]) -> Polinomio[A]
# tal que multPol(p, q) es el producto de los polinomios p y q. Por
# ejemplo,
# >>> ejPol1 = consPol(4, 3, consPol(2, -5, consPol(0, 3, polCero())))
# >>> ejPol2 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol1
# 3*x^4 + -5*x^2 + 3
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> multPol(ejPol1, ejPol2)
# 3*x^9 + -5*x^7 + 15*x^6 + 15*x^5 + -25*x^4 + -20*x^3 + 15*x^2 + 12*x
#
# Comprobar con Hypothesis las siguientes propiedades
# + El producto de polinomios es conmutativo.
Capítulo 10. El tipo abstracto de datos de los polinomios 855
# pylint: disable=arguments-out-of-order
# La comprobación es
# > poetry run pytest -v Pol_Producto_polinomios.py
# test_conmutativaProducto PASSED
# test_distributivaProductoSuma PASSED
10.13.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# valor : (Polinomio[A], A) -> A
# tal que valor(p, c) es el valor del polinomio p al sustituir su
# variable por c. Por ejemplo,
# >>> ejPol = consPol(4, 3, consPol(2, -5, consPol(0, 3, polCero())))
# >>> ejPol
# 3*x^4 + -5*x^2 + 3
# >>> valor(ejPol, 0)
# 3
# >>> valor(ejPol, 1)
# 1
# >>> valor(ejPol, -2)
# 31
# --------------------------------------------------------------------
# pylint: disable=unused-import
10.14.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# esRaiz(A, Polinomio[A]) -> bool
# tal que esRaiz(c, p) se verifica si c es una raiz del polinomio p. Por
# ejemplo,
# >>> ejPol = consPol(4, 6, consPol(1, 2, polCero()))
# >>> ejPol
# 6*x^4 + 2*x
# >>> esRaiz(0, ejPol)
# True
# >>> esRaiz(1, ejPol)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
-- Comprobación
-- λ> quickCheck prop_derivada
-- OK, passed 100 tests.
10.15.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# derivada :: (Eq a, Num a) => Polinomio a -> Polinomio a
# tal que (derivada p) es la derivada del polinomio p. Por ejemplo,
# >>> ejPol = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol
Capítulo 10. El tipo abstracto de datos de los polinomios 861
# La comprobación es
# > poetry run pytest -q Pol_Derivada_de_un_polinomio.py
# 1 passed in 0.46s
10.16.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# restaPol : (Polinomio[A], Polinomio[A]) -> Polinomio[A]
# tal que restaPol(p, q) es el polinomio obtenido restándole a p el q. Por
# ejemplo,
# >>> ejPol1 = consPol(5,1,consPol(4,5,consPol(2,5,consPol(0,9,polCero()))))
# >>> ejPol2 = consPol(4,3,consPol(2,5,consPol(0,3,polCero())))
# >>> ejPol1
# x^5 + 5*x^4 + 5*x^2 + 9
# >>> ejPol2
# 3*x^4 + 5*x^2 + 3
Capítulo 10. El tipo abstracto de datos de los polinomios 863
# pylint: disable=unused-import
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_potencia :: Polinomio Int -> NonNegative Int -> Bool
prop_potencia p (NonNegative n) =
potencia p n == potencia2 p n
-- La comprobación es
-- λ> quickCheck prop_potencia
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> import TAD.Polinomio (grado)
-- λ> ejPol = consPol 1 2 (consPol 0 3 polCero)
Capítulo 10. El tipo abstracto de datos de los polinomios 865
10.17.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# potencia : (Polinomio[A], int) -> Polinomio[A]
# tal que potencia(p, n) es la potencia n-ésima del polinomio p. Por
# ejemplo,
# >>> ejPol = consPol(1, 2, consPol(0, 3, polCero()))
# >>> ejPol
# 2*x + 3
# >>> potencia(ejPol, 2)
# 4*x^2 + 12*x + 9
# >>> potencia(ejPol, 3)
# 8*x^3 + 36*x^2 + 54*x + 27
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
866 Ejercicios de programación con Python
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(p=polinomioAleatorio(),
n=st.integers(min_value=1, max_value=10))
def test_potencia(p: Polinomio[int], n: int) -> None:
r = potencia(p, n)
assert potencia2(p, n) == r
assert potencia3(p, n) == r
# La comprobación es
# src> poetry run pytest -q Pol_Potencia_de_un_polinomio.py
# 1 passed in 0.89s
Capítulo 10. El tipo abstracto de datos de los polinomios 867
# Comparación de eficiencia
# =========================
# La comparación es
# >>> from src.TAD.Polinomio import grado
# >>> ejPol = consPol(1, 2, consPol(0, 3, polCero()))
# >>> tiempo('grado(potencia(ejPol, 1000))')
# 8.58 segundos
# >>> tiempo('grado(potencia2(ejPol, 1000))')
# 8.75 segundos
coefLider, restoPol)
import Data.Ratio
10.18.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# integral : (Polinomio[float]) -> Polinomio[float]
# tal que integral(p) es la integral del polinomio p cuyos coefientes
# son números decimales. Por ejemplo,
# >>> ejPol = consPol(7, 2, consPol(4, 5, consPol(2, 5, polCero())))
# >>> ejPol
# 2*x^7 + 5*x^4 + 5*x^2
# >>> integral(ejPol)
# 0.25*x^8 + x^5 + 1.6666666666666667*x^3
# ---------------------------------------------------------------------
10.19.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los polinomios](https://bit.ly/3KwqXYu)
# definir la función
# integralDef : (Polinomio[float], float, float) -> float
# tal que integralDef(p, a, b) es la integral definida del polinomio p
# entre a y b. Por ejemplo,
# >>> ejPol = consPol(7, 2, consPol(4, 5, consPol(2, 5, polCero())))
870 Ejercicios de programación con Python
# >>> ejPol
# 2*x^7 + 5*x^4 + 5*x^2
# >>> integralDef(ejPol, 0, 1)
# 2.916666666666667
# ---------------------------------------------------------------------
# pylint: disable=unused-import
10.20.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# multEscalar : (A, Polinomio[A]) -> Polinomio[A]
# tal que multEscalar(c, p) es el polinomio obtenido multiplicando el
# número c por el polinomio p. Por ejemplo,
# >>> ejPol = consPol(1, 2, consPol(0, 3, polCero()))
# >>> ejPol
# 2*x + 3
# >>> multEscalar(4, ejPol)
# 8*x + 12
# >>> from fractions import Fraction
# >>> multEscalar(Fraction('1/4'), ejPol)
# 1/2*x + 3/4
# ---------------------------------------------------------------------
if esPolCero(p):
return polCero()
n = grado(p)
b = coefLider(p)
r = restoPol(p)
return consPol(n, c * b, multEscalar(c, r))
10.21.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir las funciones
# cociente : (Polinomio[float], Polinomio[float]) -> Polinomio[float]
# resto : (Polinomio[float], Polinomio[float]) -> Polinomio[float]
# tales que
# + cociente(p, q) es el cociente de la división de p entre q. Por
# ejemplo,
# >>> pol1 = consPol(3, 2, consPol(2, 9, consPol(1, 10, consPol(0, 4, polCer
# >>> pol1
# 2*x^3 + 9*x^2 + 10*x + 4
# >>> pol2 = consPol(2, 1, consPol(1, 3, polCero()))
# >>> pol2
# x^2 + 3*x
# >>> cociente(pol1, pol2)
# 2.0*x + 3.0
# + resto(p, q) es el resto de la división de p entre q. Por ejemplo,
# >>> resto(pol1, pol2)
874 Ejercicios de programación con Python
# 1.0*x + 4
# ---------------------------------------------------------------------
-- 8*x^2 + 14*x + 3
-- λ> pol2 = consPol 1 2 (consPol 0 3 polCero)
-- λ> pol2
-- 2*x + 3
-- λ> pol3 = consPol 2 6 (consPol 1 2 polCero)
-- λ> pol3
-- 6*x^2 + 2*x
-- λ> divisiblePol pol1 pol2
-- True
-- λ> divisiblePol pol1 pol3
-- False
-- ---------------------------------------------------------------------
10.22.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# divisiblePol : (Polinomio[float], Polinomio[float]) -> bool
# tal que divisiblePol(p, q) se verifica si el polinomio p es divisible
# por el polinomio q. Por ejemplo,
# >>> pol1 = consPol(2, 8, consPol(1, 14, consPol(0, 3, polCero())))
# >>> pol1
# 8*x^2 + 14*x + 3
# >>> pol2 = consPol(1, 2, consPol(0, 3, polCero()))
# >>> pol2
# 2*x + 3
# >>> pol3 = consPol(2, 6, consPol(1, 2, polCero()))
# >>> pol3
876 Ejercicios de programación con Python
# 6*x^2 + 2*x
# >>> divisiblePol(pol1, pol2)
# True
# >>> divisiblePol(pol1, pol3)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
10.23.2. En Python
# ---------------------------------------------------------------------
# El método de Horner para calcular el valor de un polinomio se basa
# en representarlo de una forma forma alernativa. Por ejemplo, para
# calcular el valor de
# a*x^5 + b*x^4 + c*x^3 + d*x^2 + e*x + f
# se representa como
# (((((0 * x + a) * x + b) * x + c) * x + d) * x + e) * x + f
# y se evalúa de dentro hacia afuera; es decir,
# v(0) = 0
# v(1) = v(0)*x+a = 0*x+a = a
# v(2) = v(1)*x+b = a*x+b
# v(3) = v(2)*x+c = (a*x+b)*x+c = a*x^2+b*x+c
# v(4) = v(3)*x+d = (a*x^2+b*x+c)*x+d = a*x^3+b*x^2+c*x+d
# v(5) = v(4)*x+e = (a*x^3+b*x^2+c*x+d)*x+e = a*x^4+b*x^3+c*x^2+d*x+e
# v(6) = v(5)*x+f = (a*x^4+b*x^3+c*x^2+d*x+e)*x+f = a*x^5+b*x^4+c*x^3+d*x^2+e*x
#
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# horner : (Polinomio[float], float) -> float
# tal que horner(p, x) es el valor del polinomio p al sustituir su
# variable por el número x. Por ejemplo,
# >>> pol1 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> pol1
# x^5 + 5*x^2 + 4*x
# >>> horner(pol1, 0)
# 0
# >>> horner(pol1, 1)
# 10
# >>> horner(pol1, 1.5)
# 24.84375
Capítulo 10. El tipo abstracto de datos de los polinomios 879
# pylint: disable=unused-import
# 1ª solución
# ===========
return hornerAux(polinomioAdensa(p), 0)
# 2ª solución
# ===========
10.24.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu)
# definir la función
# terminoIndep : (Polinomio[A]) -> A
# tal que terminoIndep(p) es el término independiente del polinomio
# p. Por ejemplo,
Capítulo 10. El tipo abstracto de datos de los polinomios 881
# pylint: disable=unused-import
-- ya que
-- | 1 2 -1 -2 | 1 2 -1 -2
-- 2 | 2 8 14 1 | 1 3 2
-- --+-------------- --+-------------
-- | 1 4 7 12 | 1 3 2 0
-- ---------------------------------------------------------------------
import Test.QuickCheck
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_ruffiniDensa :: Int -> [Int] -> Bool
prop_ruffiniDensa r cs =
ruffiniDensa r cs == ruffiniDensa2 r cs
-- La comprobación es
-- λ> quickCheck prop_ruffiniDensa
-- +++ OK, passed 100 tests.
Capítulo 10. El tipo abstracto de datos de los polinomios 883
10.25.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu)
# definir la función
# ruffiniDensa : (int, list[int]) -> list[int]
# tal que ruffiniDensa(r, cs) es la lista de los coeficientes del
# cociente junto con el rsto que resulta de aplicar la regla de Ruffini
# para dividir el polinomio cuya representación densa es cs entre
# x-r. Por ejemplo,
# ruffiniDensa(2, [1, 2, -1, -2]) == [1, 4, 7, 12]
# ruffiniDensa(1, [1, 2, -1, -2]) == [1, 3, 2, 0]
# ya que
# | 1 2 -1 -2 | 1 2 -1 -2
# 2 | 2 8 14 1 | 1 3 2
# --+-------------- --+-------------
# | 1 4 7 12 | 1 3 2 0
# ---------------------------------------------------------------------
-- λ> ejPol
-- x^3 + 2*x^2 + -1*x + -2
-- λ> cocienteRuffini 2 ejPol
-- x^2 + 4*x + 7
-- λ> cocienteRuffini (-2) ejPol
-- x^2 + -1
-- λ> cocienteRuffini 3 ejPol
-- x^2 + 5*x + 14
-- + (restoRuffini r p) es el resto de dividir el polinomio p por el
-- polinomio x-r. Por ejemplo,
-- λ> restoRuffini 2 ejPol
-- 12
-- λ> restoRuffini (-2) ejPol
-- 0
-- λ> restoRuffini 3 ejPol
-- 40
--
-- Comprobar con QuickCheck que, dado un polinomio p y un número entero
-- r, las funciones anteriores verifican la propiedad de la división
-- euclídea.
-- ---------------------------------------------------------------------
-- 1ª definición de cocienteRuffini
-- ================================
-- 2ª definición de cocienteRuffini
-- ================================
-- 1ª definición de restoRuffini
-- =============================
-- 2ª definición de restoRuffini
-- =============================
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_diviEuclidea :: Int -> Polinomio Int -> Bool
prop_diviEuclidea r p =
p == sumaPol (multPol coci divi) rest
where coci = cocienteRuffini r p
divi = densaApolinomio [1,-r]
rest = creaTermino 0 (restoRuffini r p)
-- La comprobación es
-- λ> quickCheck prop_diviEuclidea
-- +++ OK, passed 100 tests.
10.26.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir las funciones
# cocienteRuffini : (int, Polinomio[int]) -> Polinomio[int]
886 Ejercicios de programación con Python
# pylint: disable=unused-import
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(r=st.integers(), p=polinomioAleatorio())
def test_diviEuclidea (r: int, p: Polinomio[int]) -> None:
coci = cocienteRuffini(r, p)
divi = densaApolinomio([1, -r])
rest = creaTermino(0, restoRuffini(r, p))
assert p == sumaPol(multPol(coci, divi), rest)
# La comprobación es
# src> poetry run pytest -q Pol_Regla_de_Ruffini.py
# 1 passed in 0.32s
-- λ> ejPol
-- 6*x^4 + 2*x
-- λ> esRaizRuffini 0 ejPol
-- True
-- λ> esRaizRuffini 1 ejPol
-- False
-- ---------------------------------------------------------------------
10.27.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# esRaizRuffini : (int, Polinomio[int]) -> bool
# tal que esRaizRuffini(r, p) se verifica si r es una raiz de p, usando
# para ello el regla de Ruffini. Por ejemplo,
# >>> ejPol = consPol(4, 6, consPol(1, 2, polCero()))
# >>> ejPol
# 6*x^4 + 2*x
# >>> esRaizRuffini(0, ejPol)
# True
# >>> esRaizRuffini(1, ejPol)
# False
# ---------------------------------------------------------------------
# pylint: disable=unused-import
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
raicesRuffini ejPol1 `shouldBe` []
it ”e2” $
raicesRuffini ejPol2 `shouldBe` [0,-1]
it ”e3” $
raicesRuffini ejPol3 `shouldBe` [0]
it ”e4” $
raicesRuffini ejPol4 `shouldBe` [1,-1,-2]
where
ejPol1 = consPol 4 3 (consPol 2 (-5) (consPol 0 3 polCero))
ejPol2 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero))
ejPol3 = consPol 4 6 (consPol 1 2 polCero)
Capítulo 10. El tipo abstracto de datos de los polinomios 891
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0013 seconds
-- 4 examples, 0 failures
10.28.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# raicesRuffini : (Polinomio[int]) -> list[int]
# tal que raicesRuffini(p) es la lista de las raices enteras de p,
# calculadas usando el regla de Ruffini. Por ejemplo,
# >>> ejPol1 = consPol(4, 3, consPol(2, -5, consPol(0, 3, polCero())))
# >>> ejPol1
# 3*x^4 + -5*x^2 + 3
# >>> raicesRuffini(ejPol1)
# []
# >>> ejPol2 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> raicesRuffini(ejPol2)
# [0, -1]
# >>> ejPol3 = consPol(4, 6, consPol(1, 2, polCero()))
# >>> ejPol3
# 6*x^4 + 2*x
# >>> raicesRuffini(ejPol3)
# [0]
# >>> ejPol4 = consPol(3, 1, consPol(2, 2, consPol(1, -1, consPol(0, -2, polCe
# >>> ejPol4
# x^3 + 2*x^2 + -1*x + -2
# >>> raicesRuffini(ejPol4)
892 Ejercicios de programación con Python
# Verificación
# ============
# La verificación es
# >>> test_raicesRuffini()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
map show (factorizacion ejPol1)
`shouldBe` [”1*x”,”1*x + 1”,”x^3 + -1*x^2 + 1*x + 4”]
it ”e2” $
map show (factorizacion ejPol2)
`shouldBe` [”1*x + -1”,”1*x + 1”,”1*x + 2”,”1”]
where
ejPol1 = consPol 5 1 (consPol 2 5 (consPol 1 4 polCero))
ejPol2 = consPol 3 1 (consPol 2 2 (consPol 1 (-1) (consPol 0 (-2) polCero)))
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0015 seconds
-- 2 examples, 0 failures
Capítulo 10. El tipo abstracto de datos de los polinomios 895
10.29.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de los polinomios](https://bit.ly/3KwqXYu),
# definir la función
# factorizacion : (Polinomio[int]) -> list[Polinomio[int]]
# tal que factorizacion(p) es la lista de la descomposición del
# polinomio p en factores obtenida mediante el regla de Ruffini. Por
# ejemplo,
# >>> ejPol1 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol1
# x^5 + 5*x^2 + 4*x
# >>> factorizacion(ejPol1)
# [1*x, 1*x + 1, x^3 + -1*x^2 + 1*x + 4]
# >>> ejPol2 = consPol(3, 1, consPol(2, 2, consPol(1, -1, consPol(0, -2, polCe
# >>> ejPol2
# x^3 + 2*x^2 + -1*x + -2
# >>> factorizacion(ejPol2)
# [1*x + -1, 1*x + 1, 1*x + 2, 1]
# ---------------------------------------------------------------------
if esPolCero(p):
return [p]
896 Ejercicios de programación con Python
# Verificación
# ============
# La verificación es
# >>> test_factorizacion()
# Verificado
Capítulo 11
Contenido
11.1. El tipo abstracto de datos de los grafos . . . . . . . . . . . .876
11.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .876
11.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .878
11.2. El TAD de los grafos mediante listas de adyacencia . . . . .881
11.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .881
11.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .885
11.3. Grafos completos . . . . . . . . . . . . . . . . . . . . . . . . .892
11.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .892
11.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .894
11.4. Grafos ciclos . . . . . . . . . . . . . . . . . . . . . . . . . . . .895
11.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .895
11.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .896
11.5. Número de vértices . . . . . . . . . . . . . . . . . . . . . . . .897
11.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .897
11.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .898
11.6. Incidentes de un vértice . . . . . . . . . . . . . . . . . . . . .899
11.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .899
11.6.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .900
11.7. Contiguos de un vértice . . . . . . . . . . . . . . . . . . . . . .902
897
898 Ejercicios de programación con Python
-- 5 (peso 44).
-- + El vértice 4 está conectado con el vértice 5 (peso 93).
--
-- Las operaciones del tipo abstracto de datos (TAD) de los grafos son
-- creaGrafo :: (Ix v, Num p, Ord v, Ord p) =>
-- Orientacion -> (v,v) -> [(v,v,p)] -> Grafo v p
-- creaGrafo' :: (Ix v, Num p, Ord v, Ord p) =>
-- Orientacion -> (v,v) -> [(v,v)] -> Grafo v p
-- dirigido :: (Ix v,Num p) => (Grafo v p) -> Bool
-- adyacentes :: (Ix v,Num p) => (Grafo v p) -> v -> [v]
-- nodos :: (Ix v,Num p) => (Grafo v p) -> [v]
-- aristas :: (Ix v,Num p) => (Grafo v p) -> [(v,v,p)]
-- aristaEn :: (Ix v,Num p) => (Grafo v p) -> (v,v) -> Bool
-- peso :: (Ix v,Num p) => v -> v -> (Grafo v p) -> p
-- tales que
-- + (creaGrafo o cs as) es un grafo (dirigido o no, según el valor
-- de o), con el par de cotas cs y listas de aristas as (cada
-- arista es un trío formado por los dos vértices y su peso).
-- + creaGrafo' es la versión de creaGrafo para los grafos sin pesos.
-- + (dirigido g) se verifica si g es dirigido.
-- + (nodos g) es la lista de todos los nodos del grafo g.
-- + (aristas g) es la lista de las aristas del grafo g.
-- + (adyacentes g v) es la lista de los vértices adyacentes al nodo
-- v en el grafo g.
-- + (aristaEn g a) se verifica si a es una arista del grafo g.
-- + (peso v1 v2 g) es el peso de la arista que une los vértices v1 y
-- v2 en el grafo g.
--
-- Usando el TAD de los grafos, el grafo anterior se puede definir por
-- creaGrafo ND (1,5) [(1,2,12),(1,3,34),(1,5,78),
-- (2,4,55),(2,5,32),
-- (3,4,61),(3,5,44),
-- (4,5,93)]
-- con los siguientes argumentos:
-- + ND: Es un parámetro de tipo Orientacion que indica si el grafo
-- es dirigido o no. En este caso, se utiliza ND, lo que significa
-- ”no dirigido”. Por lo tanto, el grafo creado será no dirigido,
-- lo que implica que las aristas no tienen una dirección
-- específica.
-- + (1,5): Es el par de cotas que define los vértices del grafo. En
902 Ejercicios de programación con Python
module TAD.Grafo
(Orientacion (..),
Grafo,
creaGrafo,
creaGrafo',
dirigido,
adyacentes,
nodos,
aristas,
aristaEn,
peso
) where
import TAD.GrafoConListaDeAdyacencia
-- import TAD.GrafoConVectorDeAdyacencia
-- import TAD.GrafoConMatrizDeAdyacencia
11.1.2. En Python
# Un grafo es una estructura que consta de un conjunto de vértices y un
# conjunto de aristas que conectan los vértices entre sí. Cada vértice
# representa una entidad o un elemento, y cada arista representa una
# relación o conexión entre dos vértices.
#
# Por ejemplo,
Capítulo 11. El tipo abstracto de datos de los grafos 903
#
# 12
# 1 -------- 2
# | \78 /|
# | \ 32/ |
# | \ / |
# 34| 5 |55
# | / \ |
# | /44 \ |
# | / 93\|
# 3 -------- 4
# 61
#
# representa un grafo no dirigido, lo que significa que las aristas no
# tienen una dirección específica. Cada arista tiene un peso asociado,
# que puede representar una medida o una valoración de la relación
# entre los vértices que conecta.
#
# El grafo consta de cinco vértices numerados del 1 al 5. Las aristas
# especificadas en la lista indican las conexiones entre los vértices y
# sus respectivos pesos. Por ejemplo, la arista (1,2,12) indica que
# existe una conexión entre el vértice 1 y el vértice 2 con un peso de
# 12.
#
# En el grafo representado, se pueden observar las conexiones entre los
# vértices de la siguiente manera:
# + El vértice 1 está conectado con el vértice 2 (peso 12), el vértice
# 3 (peso 34) y el vértice 5 (peso 78).
# + El vértice 2 está conectado con el vértice 4 (peso 55) y el vértice
# 5 (peso 32).
# + El vértice 3 está conectado con el vértice 4 (peso 61) y el vértice
# 5 (peso 44).
# + El vértice 4 está conectado con el vértice 5 (peso 93).
#
# Las operaciones del tipo abstracto de datos (TAD) de los grafos son
# creaGrafo
# creaGrafo_
# dirigido
# adyacentes
# nodos
904 Ejercicios de programación con Python
# aristas
# aristaEn
# peso
# tales que
# + creaGrafo(o, cs, as) es un grafo (dirigido o no, según el valor
# de o), con el par de cotas cs y listas de aristas as (cada
# arista es un trío formado por los dos vértices y su peso). Ver
# un ejemplo en el siguiente apartado.
# + creaGrafo_ es la versión de creaGrafo para los grafos sin pesos.
# + dirigido(g) se verifica si g es dirigido.
# + nodos(g) es la lista de todos los nodos del grafo g.
# + aristas(g) es la lista de las aristas del grafo g.
# + adyacentes(g, v) es la lista de los vértices adyacentes al nodo
# v en el grafo g.
# + aristaEn(g, a) se verifica si a es una arista del grafo g.
# + peso(v1, v2, g) es el peso de la arista que une los vértices v1 y
# v2 en el grafo g.
#
# Usando el TAD de los grafos, el grafo anterior se puede definir por
# creaGrafo ND (1,5) [(1,2,12),(1,3,34),(1,5,78),
# (2,4,55),(2,5,32),
# (3,4,61),(3,5,44),
# (4,5,93)]
# con los siguientes argumentos:
# + ND: Es un parámetro de tipo Orientacion que indica si el grafo
# es dirigido o no. En este caso, se utiliza ND, lo que significa
# ”no dirigido”. Por lo tanto, el grafo creado será no dirigido,
# lo que implica que las aristas no tienen una dirección
# específica.
# + (1,5): Es el par de cotas que define los vértices del grafo. En
# este caso, el grafo tiene vértices numerados desde 1 hasta 5.
# + [(1,2,12),(1,3,34),(1,5,78),(2,4,55),(2,5,32),(3,4,61),(3,5,44),(4,5,93)]:
# Es una lista de aristas, donde cada arista está representada por
# un trío de valores. Cada trío contiene los dos vértices que
# están conectados por la arista y el peso de dicha arista.
#
# Para usar el TAD hay que usar una implementación concreta. En
# principio, consideraremos sólo la siguiente:
# + mediante lista de adyacencia.
Capítulo 11. El tipo abstracto de datos de los grafos 905
# pylint: disable=unused-import
__all__ = [
'Orientacion',
'Grafo',
'Vertice',
'Peso',
'creaGrafo',
'creaGrafo_',
'dirigido',
'adyacentes',
'nodos',
'aristas',
'aristaEn',
'peso'
]
module TAD.GrafoConListaDeAdyacencia
(Orientacion (..),
Grafo,
creaGrafo,
creaGrafo',
dirigido,
adyacentes,
906 Ejercicios de programación con Python
nodos,
aristas,
aristaEn,
peso
) where
-- Librerías auxiliares --
import Data.Ix (Ix, range)
import Data.List (sort, nub)
-- ejGrafoND es el grafo
-- 12
-- 1 -------- 2
908 Ejercicios de programación con Python
-- | \78 /|
-- | \ 32/ |
-- | \ / |
-- 34| 5 |55
-- | / \ |
-- | /44 \ |
-- | / 93\|
-- 3 -------- 4
-- 61
-- Se define por
ejGrafoND :: Grafo Int Int
ejGrafoND = creaGrafo ND (1,5) [(1,2,12),(1,3,34),(1,5,78),
(2,4,55),(2,5,32),
(3,4,61),(3,5,44),
(4,5,93)]
11.2.2. En Python
# Se define la clase Grafo con los siguientes métodos:
# + dirigido() se verifica si el grafo es dirigido.
# + nodos() es la lista de todos los nodos del grafo.
# + aristas() es la lista de las aristas del grafo.
# + adyacentes(v) es la lista de los vértices adyacentes al vértice
# v en el grafo.
910 Ejercicios de programación con Python
# >>> ejGrafoND.nodos()
# [1, 2, 3, 4, 5]
# >>> ejGrafoD.nodos()
# [1, 2, 3, 4, 5]
# >>> ejGrafoND.adyacentes(4)
# [2, 3, 5]
# >>> ejGrafoD.adyacentes(4)
# [5]
# >>> ejGrafoND.aristaEn((5, 1))
# True
# >>> ejGrafoND.aristaEn((4, 1))
# False
# >>> ejGrafoD.aristaEn((5, 1))
# False
# >>> ejGrafoD.aristaEn((1, 5))
# True
# >>> ejGrafoND.peso(1, 5)
# 78
# >>> ejGrafoD.peso(1, 5)
# 78
# >>> ejGrafoD._aristas
# [((1, 2), 12), ((1, 3), 34), ((1, 5), 78),
# ((2, 4), 55), ((2, 5), 32),
# ((3, 4), 61), ((3, 5), 44),
# ((4, 5), 93)]
# >>> ejGrafoND._aristas
# [((1, 2), 12), ((1, 3), 34), ((1, 5), 78),
# ((2, 1), 12), ((2, 4), 55), ((2, 5), 32),
# ((3, 1), 34), ((3, 4), 61), ((3, 5), 44),
# ((4, 2), 55), ((4, 3), 61), ((4, 5), 93),
# ((5, 1), 78), ((5, 2), 32), ((5, 3), 44),
# ((5, 4), 93)]
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> creaGrafo(Orientacion.ND, (1,3), [((1,2),12),((1,3),34)])
# G ND ([1, 2, 3], [((1, 2), 12), ((1, 3), 34), ((2, 1), 12), ((3, 1), 34)])
# >>> creaGrafo(Orientacion.D, (1,3), [((1,2),12),((1,3),34)])
# G D ([1, 2, 3], [((1, 2), 12), ((1, 3), 34)])
# >>> creaGrafo(Orientacion.D, (1,4), [((1,2),12),((1,3),34)])
# G D ([1, 2, 3, 4], [((1, 2), 12), ((1, 3), 34)])
912 Ejercicios de programación con Python
# pylint: disable=protected-access
Vertice = int
Cotas = tuple[Vertice, Vertice]
Peso = float
Arista = tuple[tuple[Vertice, Vertice], Peso]
class Grafo:
def __init__(self,
_orientacion: Orientacion,
_cotas: Cotas,
_aristas: list[Arista]):
914 Ejercicios de programación con Python
self._orientacion = _orientacion
self._cotas = _cotas
if _orientacion == Orientacion.ND:
simetricas = [((v2, v1), p) for ((v1, v2), p)
in _aristas
if v1 != v2]
self._aristas = sorted(_aristas + simetricas)
else:
self._aristas = sorted(_aristas)
# 34| 5 |55
# | / \ |
# | /44 \ |
# | / 93\|
# 3 -------- 4
# 61
# definidos por
ejGrafoND: Grafo = Grafo(Orientacion.ND,
(1, 5),
[((1, 2), 12), ((1, 3), 34), ((1, 5), 78),
((2, 4), 55), ((2, 5), 32),
((3, 4), 61), ((3, 5), 44),
((4, 5), 93)])
ejGrafoD: Grafo = Grafo(Orientacion.D,
(1,5),
[((1, 2), 12), ((1, 3), 34), ((1, 5), 78),
((2, 4), 55), ((2, 5), 32),
((3, 4), 61), ((3, 5), 44),
((4, 5), 93)])
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
show (completo 4) `shouldBe`
”G ND ([1,2,3,4],[(1,2),(1,3),(1,4),(2,3),(2,4),(3,4)])”
-- La verificación es
-- λ> verifica
--
-- e1
--
-- Finished in 0.0004 seconds
-- 1 example, 0 failures
918 Ejercicios de programación con Python
11.3.2. En Python
# ---------------------------------------------------------------------
# El grafo completo de orden n, K(n), es un grafo no dirigido cuyos
# conjunto de vértices es {1,..n} y tiene una arista entre cada par de
# vértices distintos.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# completo : (int) -> Grafo
# tal que completo(n) es el grafo completo de orden n. Por ejemplo,
# >>> completo(4)
# G ND ([1, 2, 3, 4],
# [((1, 2), 0), ((1, 3), 0), ((1, 4), 0),
# ((2, 1), 0), ((2, 3), 0), ((2, 4), 0),
# ((3, 1), 0), ((3, 2), 0), ((3, 4), 0),
# ((4, 1), 0), ((4, 2), 0), ((4, 3), 0)])
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_completo()
# Verificado
Capítulo 11. El tipo abstracto de datos de los grafos 919
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
show (grafoCiclo 3) `shouldBe`
”G ND ([1,2,3],[(1,2),(1,3),(2,3)])”
-- La verificación es
-- λ> verifica
--
920 Ejercicios de programación con Python
-- e1
--
-- Finished in 0.0006 seconds
-- 1 example, 0 failures
11.4.2. En Python
# ---------------------------------------------------------------------
# El ciclo de orden n, C(n), es un grafo no dirigido cuyo conjunto de
# vértices es {1,...,n} y las aristas son
# (1,2), (2,3), ..., (n-1,n), (n,1)
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# grafoCiclo : (Int) -> Grafo
# tal que grafoCiclo(n) es el grafo ciclo de orden n. Por ejemplo,
# >>> grafoCiclo(3)
# G ND ([1, 2, 3], [(1, 2), (1, 3), (2, 3)])
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_grafoCiclo()
# Verificado
Capítulo 11. El tipo abstracto de datos de los grafos 921
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
nVertices (creaGrafo' D (1,5) [(1,2),(3,1)] :: Grafo Int Int) `shouldBe` 5
it ”e2” $
nVertices (creaGrafo' ND (0,5) [(1,2),(3,1)] :: Grafo Int Int) `shouldBe` 6
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
922 Ejercicios de programación con Python
--
-- Finished in 0.0002 seconds
-- 2 examples, 0 failures
11.5.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# nVertices : (Grafo) -> int
# tal que nVertices(g) es el número de vértices del grafo g. Por
# ejemplo,
# >>> nVertices(creaGrafo_(Orientacion.D, (1,5), [(1,2),(3,1)]))
# 5
# >>> nVertices(creaGrafo_(Orientacion.ND, (2,4), [(1,2),(3,1)]))
# 3
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_nVertices()
# Verificado
Capítulo 11. El tipo abstracto de datos de los grafos 923
-- Verificación
-- ============
924 Ejercicios de programación con Python
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
incidentes g1 1 `shouldBe` [3]
it ”e2” $
incidentes g1 2 `shouldBe` [1,2,3]
it ”e3” $
incidentes g1 3 `shouldBe` []
it ”e4” $
incidentes g2 1 `shouldBe` [2,3]
it ”e5” $
incidentes g2 2 `shouldBe` [1,2,3]
it ”e6” $
incidentes g2 3 `shouldBe` [1,2]
where
g1, g2 :: Grafo Int Int
g1 = creaGrafo' D (1,3) [(1,2),(2,2),(3,1),(3,2)]
g2 = creaGrafo' ND (1,3) [(1,2),(2,2),(3,1),(3,2)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0005 seconds
-- 6 examples, 0 failures
11.6.2. En Python
# ---------------------------------------------------------------------
# En un un grafo g, los incidentes de un vértice v es el conjuntos de
Capítulo 11. El tipo abstracto de datos de los grafos 925
# Verificación
# ============
# La verificación es
# >>> test_incidentes()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
contiguos g1 1 `shouldBe` [2,3]
it ”e2” $
contiguos g1 2 `shouldBe` [2,1,3]
it ”e3” $
contiguos g1 3 `shouldBe` [1,2]
it ”e4” $
contiguos g2 1 `shouldBe` [2,3]
it ”e5” $
contiguos g2 2 `shouldBe` [1,2,3]
it ”e6” $
contiguos g2 3 `shouldBe` [1,2]
where
g1, g2 :: Grafo Int Int
g1 = creaGrafo' D (1,3) [(1,2),(2,2),(3,1),(3,2)]
g2 = creaGrafo' ND (1,3) [(1,2),(2,2),(3,1),(3,2)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
928 Ejercicios de programación con Python
-- e4
-- e5
-- e6
--
-- Finished in 0.0005 seconds
-- 6 examples, 0 failures
11.7.2. En Python
# ---------------------------------------------------------------------
# En un un grafo g, los contiguos de un vértice v es el conjuntos de
# vértices x de g tales que x es adyacente o incidente con v.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# contiguos : (Grafo, Vertice) -> list[Vertice]
# tal que (contiguos g v) es el conjunto de los vértices de g contiguos
# con el vértice v. Por ejemplo,
# >>> g1 = creaGrafo_(Orientacion.D, (1,3), [(1,2),(2,2),(3,1),(3,2)])
# >>> contiguos(g1, 1)
# [2, 3]
# >>> contiguos(g1, 2)
# [1, 2, 3]
# >>> contiguos(g1, 3)
# [1, 2]
# >>> g2 = creaGrafo_(Orientacion.ND, (1,3), [(1,2),(2,2),(3,1),(3,2)])
# >>> contiguos(g2, 1)
# [2, 3]
# >>> contiguos(g2, 2)
# [1, 2, 3]
# >>> contiguos(g2, 3)
# [1, 2]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_contiguos()
# Verificado
-- 2
-- λ> nLazos ej2
-- 0
-- ---------------------------------------------------------------------
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
lazos ej1 `shouldBe` [(1,1),(3,3)]
it ”e2” $
lazos ej2 `shouldBe` []
it ”e3” $
nLazos ej1 `shouldBe` 2
it ”e4” $
nLazos ej2 `shouldBe` 0
where
ej1, ej2 :: Grafo Int Int
ej1 = creaGrafo' D (1,3) [(1,1),(2,3),(3,2),(3,3)]
ej2 = creaGrafo' ND (1,3) [(2,3),(3,1)]
-- La verificación es
-- λ> verifica
Capítulo 11. El tipo abstracto de datos de los grafos 931
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0005 seconds
-- 4 examples, 0 failures
11.8.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir las funciones,
# lazos : (Grafo) -> list[tuple[Vertice, Vertice]]
# nLazos : (Grafo) -> int
# tales que
# + lazos(g) es el conjunto de los lazos (es decir, aristas cuyos
# extremos son iguales) del grafo g. Por ejemplo,
# >>> ej1 = creaGrafo_(Orientacion.D, (1,3), [(1,1),(2,3),(3,2),(3,3)])
# >>> ej2 = creaGrafo_(Orientacion.ND, (1,3), [(2,3),(3,1)])
# >>> lazos(ej1)
# [(1,1),(3,3)]
# >>> lazos(ej2)
# []
# + nLazos(g) es el número de lazos del grafo g. Por ejemplo,
# >>> nLazos(ej1)
# 2
# >>> nLazos(ej2)
# 0
# ---------------------------------------------------------------------
return len(lazos(g))
# Verificación
# ============
# La verificación es
# >>> test_lazos()
# Verificado
-- 3
-- λ> nAristas (completo 4)
-- 6
-- λ> nAristas (completo 5)
-- 10
--
-- Definir la función
-- prop_nAristasCompleto :: Int -> Bool
-- tal que (prop_nAristasCompleto n) se verifica si el número de aristas
-- del grafo completo de orden n es n*(n-1)/2 y, usando la función,
-- comprobar que la propiedad se cumple para n de 1 a 20.
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Propiedad
-- =========
-- La comprobación es
-- λ> and [prop_nAristasCompleto n | n <- [1..20]]
-- True
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG nAristas
describe ”def. 2” $ specG nAristas2
-- La verificación es
-- λ> verifica
Capítulo 11. El tipo abstracto de datos de los grafos 935
--
-- def. 1
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- def. 2
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0013 seconds
-- 12 examples, 0 failures
11.9.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# nAristas : (Grafo) -> int
# tal que nAristas(g) es el número de aristas del grafo g. Si g es no
# dirigido, las aristas de v1 a v2 y de v2 a v1 sólo se cuentan una
# vez. Por ejemplo,
# g1 = creaGrafo_(Orientacion.ND, (1,5), [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4)
# g2 = creaGrafo_(Orientacion.D, (1,5), [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),
# g3 = creaGrafo_(Orientacion.ND, (1,3), [(1,2),(1,3),(2,3),(3,3)])
# g4 = creaGrafo_(Orientacion.ND, (1,4), [(1,1),(1,2),(3,3)])
# >>> nAristas(g1)
# 8
# >>> nAristas(g2)
# 7
# >>> nAristas(g3)
# 4
# >>> nAristas(g4)
# 3
936 Ejercicios de programación con Python
# >>> nAristas(completo(4))
# 6
# >>> nAristas(completo(5))
# 10
#
# Definir la función
# prop_nAristasCompleto : (int) -> bool
# tal que prop_nAristasCompleto(n) se verifica si el número de aristas
# del grafo completo de orden n es n*(n-1)/2 y, usando la función,
# comprobar que la propiedad se cumple para n de 1 a 20.
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Propiedad
# =========
# La comprobación es
# >>> all(prop_nAristasCompleto(n) for n in range(1, 21))
Capítulo 11. El tipo abstracto de datos de los grafos 937
# True
# Verificación
# ============
# La verificación es
# >>> test_nAristas()
# Verificado
-- Por ejemplo,
-- λ> g1 = creaGrafo' ND (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4
-- λ> g2 = creaGrafo' D (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),(4,5)]
-- λ> gradoPos g1 5
-- 4
-- λ> gradoPos g2 5
-- 0
-- λ> gradoPos g2 1
-- 3
-- + (gradoNeg g v) es el grado negativo del vértice v en el grafo g.
-- Por ejemplo,
-- λ> gradoNeg g1 5
-- 4
-- λ> gradoNeg g2 5
-- 3
-- λ> gradoNeg g2 1
-- 0
-- ---------------------------------------------------------------------
-- 1ª definición de gradoPos
gradoPos :: (Ix v,Num p) => Grafo v p -> v -> Int
gradoPos g v = length (adyacentes g v)
-- 2ª definición de gradoPos
gradoPos2 :: (Ix v,Num p) => Grafo v p -> v -> Int
gradoPos2 g = length . adyacentes g
-- 1ª definición de gradoNeg
gradoNeg :: (Ix v,Num p) => Grafo v p -> v -> Int
gradoNeg g v = length (incidentes g v)
-- 2ª definición de gradoNeg
gradoNeg2 :: (Ix v,Num p) => Grafo v p -> v -> Int
Capítulo 11. El tipo abstracto de datos de los grafos 939
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
gradoPos g1 5 `shouldBe` 4
it ”e2” $
gradoPos g2 5 `shouldBe` 0
it ”e3” $
gradoPos g2 1 `shouldBe` 3
it ”e4” $
gradoNeg g1 5 `shouldBe` 4
it ”e5” $
gradoNeg g2 5 `shouldBe` 3
it ”e6” $
gradoNeg g2 1 `shouldBe` 0
it ”e7” $
gradoPos2 g1 5 `shouldBe` 4
it ”e8” $
gradoPos2 g2 5 `shouldBe` 0
it ”e9” $
gradoPos2 g2 1 `shouldBe` 3
it ”e10” $
gradoNeg2 g1 5 `shouldBe` 4
it ”e11” $
gradoNeg2 g2 5 `shouldBe` 3
it ”e12” $
gradoNeg2 g2 1 `shouldBe` 0
where
g1, g2 :: Grafo Int Int
g1 = creaGrafo' ND (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5)]
g2 = creaGrafo' D (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),(4,5)]
-- La verificación es
940 Ejercicios de programación con Python
-- λ> verifica
--
-- def. 1
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- def. 2
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0013 seconds
-- 12 examples, 0 failures
11.10.2. En Python
# ---------------------------------------------------------------------
# El grado positivo de un vértice v de un grafo g es el número de
# vértices de g adyacentes con v y su grado negativo es el número de
# vértices de g incidentes con v.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir las funciones,
# gradoPos : (Grafo, Vertice) -> int
# gradoNeg : (Grafo, Vertice) -> int
# tales que
# + gradoPos(g, v) es el grado positivo del vértice v en el grafo g.
# Por ejemplo,
# g1 = creaGrafo_(Orientacion.ND, (1,5),
# [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5)])
# g2 = creaGrafo_(Orientacion.D, (1,5),
# [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),(4,5)])
# λ> gradoPos(g1, 5)
# 4
Capítulo 11. El tipo abstracto de datos de los grafos 941
# λ> gradoPos(g2, 5)
# 0
# λ> gradoPos(g2, 1)
# 3
# + gradoNeg(g, v) es el grado negativo del vértice v en el grafo g.
# Por ejemplo,
# λ> gradoNeg(g1, 5)
# 4
# λ> gradoNeg(g2, 5)
# 3
# λ> gradoNeg(g2, 1)
# 0
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_GradoPosNeg()
# Verificado
-- ((2,1),3),((2,2),7),((2,3),9),
-- ((3,1),8),((3,2),6)])
generaGD :: Int -> [Int] -> Grafo Int Int
generaGD n ps = creaGrafo D (1,n) l3
where l1 = [(x,y) | x <- [1..n], y <- [1..n]]
l2 = zip l1 ps
l3 = [(x,y,z) | ((x,y),z) <- l2, z > 0]
11.11.2. En Python
# ---------------------------------------------------------------------
# Definir un generador de grafos para comprobar propiedades de grafos
# con Hypothesis.
# ---------------------------------------------------------------------
# >>> gen_aristas(5).example()
# [(5, 3), (3, 2), (1, 3), (5, 2)]
@composite
def gen_aristas(draw: Any, n: int) -> list[tuple[int, int]]:
as_ = draw(st.lists(st.tuples(st.integers(1,n),
st.integers(1,n)),
unique=True))
return as_
# >>> gen_grafo().example()
# G D ([1], [])
# >>> gen_grafo().example()
# G D ([1, 2, 3, 4, 5, 6, 7], [(1, 3), (3, 4), (5, 5)])
@composite
def gen_grafo(draw: Any) -> Grafo:
o = draw(st.sampled_from([Orientacion.D, Orientacion.ND]))
if o == Orientacion.ND:
return draw(gen_grafoND())
return draw(gen_grafoD())
-- La propiedad es
prop_sumaGrados :: Grafo Int Int -> Bool
prop_sumaGrados g =
sum [gradoPos g v | v <- vs] == sum [gradoNeg g v | v <- vs]
where vs = nodos g
-- La comprobación es
-- λ> quickCheck prop_sumaGrados
Capítulo 11. El tipo abstracto de datos de los grafos 947
11.12.2. En Python
# ---------------------------------------------------------------------
# Comprobar con Hypothesis que para cualquier grafo g, las sumas de los
# grados positivos y la de los grados negativos de los vértices de g son
# iguales
# ---------------------------------------------------------------------
# La propiedad es
@given(gen_grafo())
def test_sumaGrados(g: Grafo) -> None:
vs = nodos(g)
assert sum((gradoPos(g, v) for v in vs)) == sum((gradoNeg(g, v) for v in vs))
# La comprobación es
# src> poetry run pytest -q Grafo_Propiedades_de_grados_positivos_y_negativos.
# 1 passed in 0.31s
-- La propiedad es
prop_numNodosGradoImpar :: Grafo Int Int -> Bool
prop_numNodosGradoImpar g =
Capítulo 11. El tipo abstracto de datos de los grafos 949
-- La comprobación es
-- λ> quickCheck prop_numNodosGradoImpar
-- +++ OK, passed 100 tests.
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
grado g1 5 `shouldBe` 4
it ”e2” $
grado g2 5 `shouldBe` 3
it ”e3” $
grado g2 1 `shouldBe` 3
it ”e4” $
grado g3 2 `shouldBe` 4
it ”e5” $
grado g3 1 `shouldBe` 2
it ”e6” $
grado g3 3 `shouldBe` 2
it ”e7” $
grado g4 1 `shouldBe` 2
it ”e8” $
grado g5 3 `shouldBe` 4
it ”e9” $
grado g6 3 `shouldBe` 4
where
g1, g2, g3, g4, g5, g6 :: Grafo Int Int
g1 = creaGrafo' ND (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5)]
g2 = creaGrafo' D (1,5) [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),(4,5)]
g3 = creaGrafo' D (1,3) [(1,2),(2,2),(3,1),(3,2)]
g4 = creaGrafo' D (1,1) [(1,1)]
g5 = creaGrafo' ND (1,3) [(1,2),(1,3),(2,3),(3,3)]
g6 = creaGrafo' D (1,3) [(1,2),(1,3),(2,3),(3,3)]
950 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- e7
-- e8
-- e9
--
-- Finished in 0.0015 seconds
-- 9 examples, 0 failures
11.13.2. En Python
# ---------------------------------------------------------------------
# El grado de un vértice v de un grafo dirigido g, es el número de
# aristas de g que contiene a v. Si g es no dirigido, el grado de un
# vértice v es el número de aristas incidentes en v, teniendo en cuenta
# que los lazos se cuentan dos veces.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir las funciones,
# grado : (Grafo, Vertice) -> int
# tal que grado(g, v) es el grado del vértice v en el grafo g. Por
# ejemplo,
# >>> g1 = creaGrafo_(Orientacion.ND, (1,5),
# [(1,2),(1,3),(1,5),(2,4),(2,5),(3,4),(3,5),(4,5)])
# >>> g2 = creaGrafo_(Orientacion.D, (1,5),
# [(1,2),(1,3),(1,5),(2,4),(2,5),(4,3),(4,5)])
# >>> g3 = creaGrafo_(Orientacion.D, (1,3),
# [(1,2),(2,2),(3,1),(3,2)])
# >>> g4 = creaGrafo_(Orientacion.D, (1,1),
# [(1,1)])
# >>> g5 = creaGrafo_(Orientacion.ND, (1,3),
# [(1,2),(1,3),(2,3),(3,3)])
Capítulo 11. El tipo abstracto de datos de los grafos 951
# La propiedad es
@given(gen_grafo())
def test_grado1(g: Grafo) -> None:
assert len([v for v in nodos(g) if grado(g, v) % 2 == 1]) % 2 == 0
# La comprobación es
# src> poetry run pytest -q Grafo_Grado_de_un_vertice.py
# 1 passed in 0.36s
# Verificación
# ============
# La verificación es
# >>> test_grado()
Capítulo 11. El tipo abstracto de datos de los grafos 953
# Verificado
-- La comprobación es
-- λ> quickCheck prop_apretonManos
-- +++ OK, passed 100 tests.
11.14.2. En Python
# ---------------------------------------------------------------------
# En la teoría de grafos, se conoce como ”Lema del apretón de manos” la
# siguiente propiedad: la suma de los grados de los vértices de g es el
# doble del número de aristas de g.
954 Ejercicios de programación con Python
#
# Comprobar con Hypothesis que para cualquier grafo g, se verifica
# dicha propiedad.
# ---------------------------------------------------------------------
# La propiedad es
@given(gen_grafo())
def test_apreton(g: Grafo) -> None:
assert sum((grado(g, v) for v in nodos(g))) == 2 * nAristas(g)
# La comprobación es
# src> poetry run pytest -q Grafo_Lema_del_apreton_de_manos.py
# 1 passed in 0.32s
--
-- Comprobar que los grafos completos son regulares.
-- ---------------------------------------------------------------------
-- La comprobación es
-- λ> prop_CompletoRegular 1 30
-- True
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
regular g1 `shouldBe` True
it ”e2” $
regular g2 `shouldBe` False
it ”e3” $
956 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0006 seconds
-- 3 examples, 0 failures
11.15.2. En Python
# ---------------------------------------------------------------------
# Un grafo es regular si todos sus vértices tienen el mismo
# grado.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# regular : (Grafo) -> bool
# tal que regular(g) se verifica si el grafo g es regular. Por ejemplo,
# >>> regular(creaGrafo_(Orientacion.D, (1,3), [(1,2),(2,3),(3,1)]))
# True
# >>> regular(creaGrafo_(Orientacion.ND, (1,3), [(1,2),(2,3)]))
# False
# >>> regular(completo(4))
# True
#
# Comprobar que los grafos completos son regulares.
# ---------------------------------------------------------------------
# La comprobación es
# >>> prop_CompletoRegular(1, 30)
# True
# Verificación
# ============
# La verificación es
# >>> test_regular()
# Verificado
-- La comprobación es
-- λ> and [prop_completoRegular n | n <- [1..20]]
-- True
-- La comprobación es
-- λ> and [prop_cicloRegular n | n <- [3..20]]
-- True
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
regularidad g1 `shouldBe` Just 1
it ”e2” $
regularidad g2 `shouldBe` Nothing
it ”e3” $
regularidad (completo 4) `shouldBe` Just 3
it ”e4” $
regularidad (completo 5) `shouldBe` Just 4
it ”e5” $
regularidad (grafoCiclo 4) `shouldBe` Just 2
it ”e6” $
regularidad (grafoCiclo 5) `shouldBe` Just 2
where
g1, g2 :: Grafo Int Int
g1 = creaGrafo' ND (1,2) [(1,2),(2,3)]
g2 = creaGrafo' D (1,2) [(1,2),(2,3)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
960 Ejercicios de programación con Python
11.16.2. En Python
# ---------------------------------------------------------------------
# Un grafo es k-regular si todos sus vértices son de grado k.
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# regularidad : (Grafo) -> Optional[int]
# tal que regularidad(g) es la regularidad de g. Por ejemplo,
# regularidad(creaGrafo_(Orientacion.ND, (1,2), [(1,2),(2,3)]) == 1
# regularidad(creaGrafo_(Orientacion.D, (1,2), [(1,2),(2,3)]) == None
# regularidad(completo(4)) == 3
# regularidad(completo(5)) == 4
# regularidad(grafoCiclo(4)) == 2
# regularidad(grafoCiclo(5)) == 2
#
# Comprobar que el grafo completo de orden n es (n-1)-regular (para
# n de 1 a 20) y el grafo ciclo de orden n es 2-regular (para n de
# 3 a 20).
# ---------------------------------------------------------------------
# La comprobación es
# >>> all(prop_completoRegular(n) for n in range(1, 21))
# True
# La comprobación es
# >>> all(prop_cicloRegular(n) for n in range(3, 21))
# True
# Verificación
# ============
# La verificación es
# >>> test_k_regularidad()
# Verificado
-- encuentra conectado con todos los otros y los recorridos pasan por
-- todos los vértices una vez y terminan en el vértice inicial. Por
-- ejemplo,
-- λ> recorridos [2,5,3]
-- [[2,5,3,2],[5,2,3,5],[3,5,2,3],[5,3,2,5],[3,2,5,3],[2,3,5,2]]
-- Indicación: No importa el orden de los recorridos en la lista.
-- ---------------------------------------------------------------------
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
recorridos [2 :: Int,5,3] `shouldBe`
[[2,5,3,2],[5,2,3,5],[3,5,2,3],[5,3,2,5],[3,2,5,3],[2,3,5,2]]
-- La verificación es
-- λ> verifica
--
-- e1
--
-- Finished in 0.0007 seconds
-- 1 example, 0 failures
11.17.2. En Python
# ---------------------------------------------------------------------
# Definir la función
Capítulo 11. El tipo abstracto de datos de los grafos 963
A = TypeVar('A')
# Verificación
# ============
# La verificación es
# >>> test_recorridos()
# Verificado
-- 1ª solución
-- ===========
-- anchuraN g 4 == 2
-- anchuraN g 5 == 4
anchuraN :: Grafo Int Int -> Int -> Int
anchuraN g x = maximum (0 : [abs (x-v) | v <- adyacentes g x])
-- 2ª solución
-- ===========
-- La conjetura
conjetura :: Int -> Bool
conjetura n = anchura (grafoCiclo n) == n-1
-- La comprobación es
-- λ> and [conjetura n | n <- [2..10]]
-- True
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
anchura grafo1 `shouldBe` 4
it ”e2” $
anchura g2 `shouldBe` 2
where
g2 :: Grafo Int Int
g2 = creaGrafo' ND (1,3) [(1,2),(1,3),(2,3),(3,3)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
966 Ejercicios de programación con Python
--
-- Finished in 0.0004 seconds
-- 2 examples, 0 failures
11.18.2. En Python
# ---------------------------------------------------------------------
# En un grafo, la anchura de un nodo es el máximo de los valores
# absolutos de la diferencia entre el valor del nodo y los de sus
# adyacentes; y la anchura del grafo es la máxima anchura de sus
# nodos. Por ejemplo, en el grafo
# grafo1: Grafo = creaGrafo_(Orientacion.D, (1,5), [(1,2),(1,3),(1,5),
# (2,4),(2,5),
# (3,4),(3,5),
# (4,5)])
# su anchura es 4 y el nodo de máxima anchura es el 5.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# anchura : (Grafo) -> int
# tal que anchuraG(g) es la anchura del grafo g. Por ejemplo,
# anchura(grafo1) == 4
#
# Comprobar experimentalmente que la anchura del grafo ciclo de orden
# n es n-1.
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# La conjetura
def conjetura(n: int) -> bool:
return anchura(grafoCiclo(n)) == n - 1
# La comprobación es
# >>> all(conjetura(n) for n in range(2, 11))
# True
# Verificación
# ============
# La verificación es
# >>> test_anchura()
# Verificado
968 Ejercicios de programación con Python
-- 1ª solución
-- ===========
rp [] vis = vis
rp (c:cs) vis
| c `elem` vis = rp cs vis
| otherwise = rp (adyacentes g c ++ cs) (vis ++ [c])
-- 2ª solución
-- ===========
-- = reverse [4,5,6,3,2,1]
-- = [1,2,3,6,5,4]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
recorridoEnProfundidad1 1 grafo1 `shouldBe` [1,2,3,6,5,4]
it ”e2” $
recorridoEnProfundidad 1 grafo1 `shouldBe` [1,2,3,6,5,4]
it ”e3” $
recorridoEnProfundidad1 1 grafo2 `shouldBe` [1,2,6,3,5,4]
it ”e4” $
recorridoEnProfundidad 1 grafo2 `shouldBe` [1,2,6,3,5,4]
where
grafo2 :: Grafo Int Int
grafo2 = creaGrafo' ND (1,6) [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0022 seconds
-- 4 examples, 0 failures
11.19.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# recorridoEnProfundidad : (Vertice, Grafo) -> list[Vertice]
Capítulo 11. El tipo abstracto de datos de los grafos 971
# 1ª solución
# ===========
# = rp([1], [])
# = rp([2,3,4], [1])
# = rp([3,4], [1,2])
# = rp([6,4], [1,2,3])
# = rp([2,5,4], [1,2,3,6])
# = rp([5,4], [1,2,3,6])
# = rp([4,4], [1,2,3,6,5])
# = rp([4], [1,2,3,6,5,4])
# = rp([], [1,2,3,6,5,4])
# = [1,2,3,6,5,4]
# 2ª solución
# ===========
# Verificación
# ============
Capítulo 11. El tipo abstracto de datos de los grafos 973
# La verificación es
# >>> test_recorridoEnProfundidad()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
Capítulo 11. El tipo abstracto de datos de los grafos 975
it ”e1” $
recorridoEnAnchura 1 grafo1 `shouldBe` [1,2,3,4,6,5]
it ”e2” $
recorridoEnAnchura 1 grafo2 `shouldBe` [1,2,3,4,6,5]
where
grafo2 :: Grafo Int Int
grafo2 = creaGrafo' ND (1,6) [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0010 seconds
-- 2 examples, 0 failures
11.20.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# recorridoEnAnchura : (Vertice, Grafo) -> list[Vertice]
# tal que recorridoEnAnchura(i, g) es el recorrido en anchura
# del grafo g desde el vértice i. Por ejemplo, en el grafo
#
# +---> 2 <---+
# | |
# | |
# 1 --> 3 --> 6 --> 5
# | |
# | |
# +---> 4 <---------+
#
# definido por
# grafo1: Grafo = creaGrafo_(Orientacion.D,
# (1,6),
# [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)])
# entonces
# recorridoEnAnchura(1, grafo1) == [1,2,3,4,6,5]
976 Ejercicios de programación con Python
# -----------------------------------------------------------
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_recorridoEnAnchura()
# Verificado
-- Verificación
-- ============
978 Ejercicios de programación con Python
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
conexo g1 `shouldBe` True
it ”e2” $
conexo g2 `shouldBe` True
it ”e3” $
conexo g3 `shouldBe` False
where
g1, g2, g3 :: Grafo Int Int
g1 = creaGrafo' ND (1,3) [(1,2),(3,2)]
g2 = creaGrafo' ND (1,4) [(1,2),(3,2),(4,1)]
g3 = creaGrafo' ND (1,4) [(1,2),(3,4)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0003 seconds
-- 3 examples, 0 failures
11.21.2. En Python
# ---------------------------------------------------------------------
# Un grafo no dirigido G se dice conexo, si para cualquier par de
# vértices u y v en G, existe al menos una trayectoria (una sucesión
# de vértices adyacentes) de u a v.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# conexo :: (Grafo) -> bool
# tal que (conexo g) se verifica si el grafo g es conexo. Por ejemplo,
# conexo (creaGrafo_(Orientacion.ND, (1,3), [(1,2),(3,2)])) == True
# conexo (creaGrafo_(Orientacion.ND, (1,4), [(1,2),(3,2),(4,1)])) == True
Capítulo 11. El tipo abstracto de datos de los grafos 979
# Verificación
# ============
# La verificación es
# >>> test_conexo()
# Verificado
-- | | | |
-- | 3 | 4 | 5 |
-- | | | |
-- +----+-----+-----+----+
-- | 6 | 7 |
-- +----------+----------+
-- se pueden representar por
-- mapa :: Grafo Int Int
-- mapa = creaGrafo' ND (1,7)
-- [(1,2),(1,3),(1,4),(2,4),(2,5),(3,4),
-- (3,6),(4,5),(4,6),(4,7),(5,7),(6,7)]
--
-- Para colorear el mapa se dispone de 4 colores definidos por
-- data Color = A | B | C | D
-- deriving (Eq, Show)
--
-- Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
-- definir la función,
-- correcta :: [(Int,Color)] -> Grafo Int Int -> Bool
-- tal que (correcta ncs m) se verifica si ncs es una coloración del
-- mapa m tal que todos las regiones vecinas tienen colores distintos.
-- Por ejemplo,
-- correcta [(1,A),(2,B),(3,B),(4,C),(5,A),(6,A),(7,B)] mapa == True
-- correcta [(1,A),(2,B),(3,A),(4,C),(5,A),(6,A),(7,B)] mapa == False
-- ---------------------------------------------------------------------
data Color = A | B | C | E
deriving (Eq, Show)
correcta ncs g =
and [color x /= color y | ((x,y),_) <- aristas g]
where color x = head [c | (y,c) <- ncs, y == x]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
correcta [(1,A),(2,B),(3,B),(4,C),(5,A),(6,A),(7,B)] mapa `shouldBe` True
it ”e2” $
correcta [(1,A),(2,B),(3,A),(4,C),(5,A),(6,A),(7,B)] mapa `shouldBe` False
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0004 seconds
-- 2 examples, 0 failures
11.22.2. En Python
# ---------------------------------------------------------------------
# Un mapa se puede representar mediante un grafo donde los vértices
# son las regiones del mapa y hay una arista entre dos vértices si las
# correspondientes regiones son vecinas. Por ejemplo, el mapa siguiente
# +----------+----------+
# | 1 | 2 |
# +----+-----+-----+----+
# | | | |
# | 3 | 4 | 5 |
# | | | |
# +----+-----+-----+----+
# | 6 | 7 |
982 Ejercicios de programación con Python
# +----------+----------+
# se pueden representar por
# mapa: Grafo = creaGrafo_(Orientacion.ND,
# (1,7),
# [(1,2),(1,3),(1,4),(2,4),(2,5),(3,4),
# (3,6),(4,5),(4,6),(4,7),(5,7),(6,7)])
#
# Para colorear el mapa se dispone de 4 colores definidos por
# Color = Enum('Color', ['A', 'B', 'C', 'E'])
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# correcta : (list[tuple[int, Color]], Grafo) -> bool
# tal que (correcta ncs m) se verifica si ncs es una coloración del
# mapa m tal que todos las regiones vecinas tienen colores distintos.
# Por ejemplo,
# correcta [(1,A),(2,B),(3,B),(4,C),(5,A),(6,A),(7,B)] mapa == True
# correcta [(1,A),(2,B),(3,A),(4,C),(5,A),(6,A),(7,B)] mapa == False
# ---------------------------------------------------------------------
# Verificación
# ============
Capítulo 11. El tipo abstracto de datos de los grafos 983
# La verificación es
# >>> test_correcta()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
aislados grafo1 `shouldBe` [1,2,4]
-- La verificación es
-- λ> verifica
--
-- e1
--
-- Finished in 0.0008 seconds
-- 1 example, 0 failures
Capítulo 11. El tipo abstracto de datos de los grafos 985
11.23.2. En Python
# ---------------------------------------------------------------------
# Dado un grafo dirigido G, diremos que un nodo está aislado si o bien
# de dicho nodo no sale ninguna arista o bien no llega al nodo ninguna
# arista. Por ejemplo, en el siguiente grafo
# grafo1: Grafo = creaGrafo_(Orientacion.D,
# (1,6),
# [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)])
# podemos ver que del nodo 1 salen 3 aristas pero no llega ninguna, por
# lo que lo consideramos aislado. Así mismo, a los nodos 2 y 4 llegan
# aristas pero no sale ninguna, por tanto también estarán aislados.
#
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# aislados :: (Ix v, Num p) => Grafo v p -> [v]
# tal que (aislados g) es la lista de nodos aislados del grafo g. Por
# ejemplo,
# aislados grafo1 == [1,2,4]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
986 Ejercicios de programación con Python
# >>> test_aislados()
# Verificado
conectados :: Grafo Int Int -> Int -> Int -> Bool
conectados g v1 v2 = v2 `elem` conectadosAux g [] [v1]
conectadosAux :: Grafo Int Int -> [Int] -> [Int] -> [Int]
conectadosAux _ vs [] = vs
conectadosAux g vs (w:ws)
| w `elem` vs = conectadosAux g vs ws
| otherwise = conectadosAux g ([w] `union` vs) (ws `union` adyacentes g w)
-- Verificación
Capítulo 11. El tipo abstracto de datos de los grafos 987
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
conectados grafo1 1 3 `shouldBe` True
it ”e2” $
conectados grafo1 1 4 `shouldBe` False
it ”e3” $
conectados grafo1 6 2 `shouldBe` False
it ”e4” $
conectados grafo1 3 1 `shouldBe` True
it ”e5” $
conectados grafo2 1 3 `shouldBe` True
it ”e6” $
conectados grafo2 1 4 `shouldBe` False
it ”e7” $
conectados grafo2 6 2 `shouldBe` True
it ”e8” $
conectados grafo2 3 1 `shouldBe` True
where
grafo1, grafo2 :: Grafo Int Int
grafo1 = creaGrafo' D (1,6) [(1,3),(1,5),(3,5),(5,1),(5,50),
(2,4),(2,6),(4,6),(4,4),(6,4)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
988 Ejercicios de programación con Python
-- e7
-- e8
--
-- Finished in 0.0032 seconds
-- 8 examples, 0 failures
11.24.2. En Python
# ---------------------------------------------------------------------
# Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
# definir la función,
# conectados : (Grafo, Vertice, Vertice) -> bool
# tal que conectados(g, v1, v2) se verifica si los vértices v1 y v2
# están conectados en el grafo g. Por ejemplo, si grafo1 es el grafo
# definido por
# grafo1 = creaGrafo_(Orientacion.D,
# (1,6),
# [(1,3),(1,5),(3,5),(5,1),(5,50),
# (2,4),(2,6),(4,6),(4,4),(6,4)])
# entonces,
# conectados grafo1 1 3 == True
# conectados grafo1 1 4 == False
# conectados grafo1 6 2 == False
# conectados grafo1 3 1 == True
# ----------------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_conectados()
# Verificado
--
-- El algoritmo de Kruskal funciona de la siguiente manera:
-- + se crea un bosque B (un conjunto de árboles), donde cada vértice
-- del grafo es un árbol separado
-- + se crea un conjunto C que contenga a todas las aristas del grafo
-- + mientras C es no vacío,
-- + eliminar una arista de peso mínimo de C
-- + si esa arista conecta dos árboles diferentes se añade al bosque,
-- combinando los dos árboles en un solo árbol
-- + en caso contrario, se desecha la arista
-- Al acabar el algoritmo, el bosque tiene un solo componente, el cual
-- forma un árbol de expansión mínimo del grafo.
--
-- Usando el [tipo abstracto de datos de los grafos](https://bit.ly/45cQ3Fo),
-- definir la función,
-- kruskal :: (Ix v, Num p, Ord p) => Grafo v p -> [(p,v,v)]
-- tal que (kruskal g) es el árbol de expansión mínimo del grafo g calculado
-- mediante el algoritmo de Kruskal. Por ejemplo, si g1, g2, g3 y g4 son
-- los grafos definidos por
-- g1, g2, g3, g4 :: Grafo Int Int
-- g1 = creaGrafo ND (1,5) [(1,2,12),(1,3,34),(1,5,78),
-- (2,4,55),(2,5,32),
-- (3,4,61),(3,5,44),
-- (4,5,93)]
-- g2 = creaGrafo ND (1,5) [(1,2,13),(1,3,11),(1,5,78),
-- (2,4,12),(2,5,32),
-- (3,4,14),(3,5,44),
-- (4,5,93)]
-- g3 = creaGrafo ND (1,7) [(1,2,5),(1,3,9),(1,5,15),(1,6,6),
-- (2,3,7),
-- (3,4,8),(3,5,7),
-- (4,5,5),
-- (5,6,3),(5,7,9),
-- (6,7,11)]
-- g4 = creaGrafo ND (1,7) [(1,2,5),(1,3,9),(1,5,15),(1,6,6),
-- (2,3,7),
-- (3,4,8),(3,5,1),
-- (4,5,5),
-- (5,6,3),(5,7,9),
-- (6,7,11)]
Capítulo 11. El tipo abstracto de datos de los grafos 991
-- entonces
-- kruskal g1 == [(55,2,4),(34,1,3),(32,2,5),(12,1,2)]
-- kruskal g2 == [(32,2,5),(13,1,2),(12,2,4),(11,1,3)]
-- kruskal g3 == [(9,5,7),(7,2,3),(6,1,6),(5,4,5),(5,1,2),(3,5,6)]
-- kruskal g4 == [(9,5,7),(6,1,6),(5,4,5),(5,1,2),(3,5,6),(1,3,5)]
-- ---------------------------------------------------------------------
(length (nodos g) - 1)
where aux _ _ ae 0 = ae
aux [] _ _ _ = error ”Imposible”
aux ((p,x,y):as) d ae n
| actualizado = aux as d' ((p,x,y):ae) (n-1)
| otherwise = aux as d ae n
where (actualizado,d') = buscaActualiza (x,y) d
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
kruskal g1 `shouldBe` [(55,2,4),(34,1,3),(32,2,5),(12,1,2)]
it ”e2” $
kruskal g2 `shouldBe` [(32,2,5),(13,1,2),(12,2,4),(11,1,3)]
it ”e3” $
kruskal g3 `shouldBe` [(9,5,7),(7,2,3),(6,1,6),(5,4,5),(5,1,2),(3,5,6)]
it ”e4” $
kruskal g4 `shouldBe` [(9,5,7),(6,1,6),(5,4,5),(5,1,2),(3,5,6),(1,3,5)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0044 seconds
-- 4 examples, 0 failures
11.25.2. En Python
# ---------------------------------------------------------------------
# El [algoritmo de Kruskal]()https://bit.ly/3N8bOOg) calcula un árbol
Capítulo 11. El tipo abstracto de datos de los grafos 995
# ((4,5),5),
# ((5,6),3),((5,7),9),
# ((6,7),11)])
# g4 = creaGrafo (Orientacion.ND,
# (1,7),
# [((1,2),5),((1,3),9),((1,5),15),((1,6),6),
# ((2,3),7),
# ((3,4),8),((3,5),1),
# ((4,5),5),
# ((5,6),3),((5,7),9),
# ((6,7),11)])
# entonces
# kruskal(g1) == [(55,2,4),(34,1,3),(32,2,5),(12,1,2)]
# kruskal(g2) == [(32,2,5),(13,1,2),(12,2,4),(11,1,3)]
# kruskal(g3) == [(9,5,7),(7,2,3),(6,1,6),(5,4,5),(5,1,2),(3,5,6)]
# kruskal(g4) == [(9,5,7),(6,1,6),(5,4,5),(5,1,2),(3,5,6),(1,3,5)]
# ---------------------------------------------------------------------
g1 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),12),((1,3),34),((1,5),78),
((2,4),55),((2,5),32),
((3,4),61),((3,5),44),
((4,5),93)])
g2 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),13),((1,3),11),((1,5),78),
((2,4),12),((2,5),32),
((3,4),14),((3,5),44),
((4,5),93)])
g3 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),7),
((4,5),5),
((5,6),3),((5,7),9),
Capítulo 11. El tipo abstracto de datos de los grafos 997
((6,7),11)])
g4 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),1),
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
return tb
cs = list(d.keys())
ds = [c for c in cs if c > x]
tb = aux1(cs, d, y)
tb = aux2(ds, tb, y_)
return tb
if x_ == y_:
return False, d
if y_ < x_:
return True, modificaR(x, d[x], y_, d)
return True, modificaR(y, d[y], x_, d)
if n == 0:
return ae
p, x, y = as_[0]
actualizado, d = buscaActualiza((x, y), d)
if actualizado:
return aux(as_[1:], d, [(p, x, y)] + ae, n - 1)
return aux(as_[1:], d, ae, n)
return aux(list(sorted([(p, x, y) for ((x, y), p) in aristas(g)])),
{x: x for x in nodos(g)},
[],
len(nodos(g)) - 1)
# Verificación
# ============
# La verificación es
# >>> test_kruskal()
# Vefificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
prim g1 `shouldBe` [(55,2,4),(34,1,3),(32,2,5),(12,1,2)]
it ”e2” $
prim g2 `shouldBe` [(32,2,5),(12,2,4),(13,1,2),(11,1,3)]
it ”e3” $
prim g3 `shouldBe` [(9,5,7),(7,2,3),(5,5,4),(3,6,5),(6,1,6),(5,1,2)]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0026 seconds
-- 3 examples, 0 failures
11.26.2. En Python
# ---------------------------------------------------------------------
# El [algoritmo de Prim](https://bit.ly/466fwRe) calcula un árbol
# recubridor mínimo en un grafo conexo y ponderado. Es decir, busca un
# subconjunto de aristas que, formando un árbol, incluyen todos los
# vértices y donde el valor de la suma de todas las aristas del árbol
# es el mínimo.
#
# El algoritmo de Prim funciona de la siguiente manera:
# + Inicializar un árbol con un único vértice, elegido arbitrariamente,
# del grafo.
# + Aumentar el árbol por un lado. Llamamos lado a la unión entre dos
# vértices: de las posibles uniones que pueden conectar el árbol a los
1004 Ejercicios de programación con Python
# prim(g1) == [(55,2,4),(34,1,3),(32,2,5),(12,1,2)]
# prim(g2) == [(32,2,5),(12,2,4),(13,1,2),(11,1,3)]
# prim(g3) == [(9,5,7),(7,2,3),(5,5,4),(3,6,5),(6,1,6),(5,1,2)]
# ---------------------------------------------------------------------
g1 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),12),((1,3),34),((1,5),78),
((2,4),55),((2,5),32),
((3,4),61),((3,5),44),
((4,5),93)])
g2 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),13),((1,3),11),((1,5),78),
((2,4),12),((2,5),32),
((3,4),14),((3,5),44),
((4,5),93)])
g3 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),7),
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
g4 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),1),
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
r: list[Vertice],
ae: list[tuple[Peso, Vertice, Vertice]],
as_: list[tuple[tuple[Vertice, Vertice], Peso]]) \
-> list[tuple[Peso, Vertice, Vertice]]:
if not as_:
return []
if not r:
return ae
e = min(((c,u,v)
for ((u,v),c) in as_
if u in t and v in r))
(_,_, v_) = e
return prim_([v_] + t, [x for x in r if x != v_], [e] + ae, as_)
return prim_([n], ns, [], aristas(g))
# Verificación
# ============
# La verificación es
# >>> test_prim()
# Verificado
Capítulo 12
Divide y vencerás
Contenido
12.1. Algoritmo divide y vencerás . . . . . . . . . . . . . . . . . . .983
12.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .983
12.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .986
12.2. Rompecabeza del triominó mediante divide y vencerás . .989
12.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . .989
12.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . .996
1007
1008 Ejercicios de programación con Python
-> s
divideVenceras ind resuelve divide combina = dv'
where
dv' pb
| ind pb = resuelve pb
| otherwise = combina pb [dv' sp | sp <- divide pb]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
1010 Ejercicios de programación con Python
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
12.1.2. En Python
# ---------------------------------------------------------------------
# La técnica [divide y vencerás](https://bit.ly/46afaca) 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(Callable[[P], bool],
# Callable[[P], S],
# Callable[[P], list[P]],
# Callable[[P, list[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 problema pb es indivisible
# + resuelve(pb) es la solución del problema indivisible pb
# + divide(pb) es la lista de subproblemas de pb
# + combina(pb, ss) es la combinación de las soluciones ss de los
# subproblemas del problema pb.
Capítulo 12. Divide y vencerás 1011
P = TypeVar('P')
S = TypeVar('S')
# Verificación
# ============
# La verificación es
Capítulo 12. Divide y vencerás 1013
# >>> test_divideVenceras()
# Verificado
-- X
--- X X
-- X XX
--
-- El rompecabeza del triominó consiste en cubrir un tablero cuadrado
-- con 2^n filas y 2^n columnas, en el que se ha eliminado una casilla,
-- con L-triominós de formas que cubran todas las casillas excepto la
-- eliminada y los triominós no se solapen.
--
-- La casilla eliminada se representará con -1 y los L-triominós con
-- sucesiones de tres números consecutivos en forma de L. Con esta
-- representación una solución del rompecabeza del triominó con 4 filas
-- y la fila eliminada en la posición (4,4) es
-- ( 3 3 2 2 )
-- ( 3 1 1 2 )
-- ( 4 1 5 5 )
-- ( 4 4 5 -1 )
--
-- Definir la función
1014 Ejercicios de programación con Python
pbInicial n p = (1,tablero n p)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
toLists (triomino 4 (4,4)) `shouldBe`
[[3,3,2,2],
[3,1,1,2],
[4,1,5,5],
[4,4,5,-1]]
it ”e2” $
toLists (triomino 4 (2,3)) `shouldBe`
[[3,3,2,2],
[3,1,-1,2],
[4,1,1,5],
[4,4,5,5]]
it ”e3” $
toLists (triomino 16 (5,6)) `shouldBe`
[[7,7,6,6,6,6,5,5,6,6,5,5,5,5,4,4],
[7,5,5,6,6,4,4,5,6,4,4,5,5,3,3,4],
[8,5,9,9,7,7,4,8,7,4,8,8,6,6,3,7],
[8,8,9,3,3,7,8,8,7,7,8,2,2,6,7,7],
[8,8,7,3,9,-1,8,8,7,7,6,6,2,8,7,7],
[8,6,7,7,9,9,7,8,7,5,5,6,8,8,6,7],
[9,6,6,10,10,7,7,11,8,8,5,9,9,6,6,10],
[9,9,10,10,10,10,11,11,1,8,9,9,9,9,10,10],
[8,8,7,7,7,7,6,1,1,9,8,8,8,8,7,7],
1020 Ejercicios de programación con Python
[8,6,6,7,7,5,6,6,9,9,7,8,8,6,6,7],
[9,6,10,10,8,5,5,9,10,7,7,11,9,9,6,10],
[9,9,10,4,8,8,9,9,10,10,11,11,5,9,10,10],
[9,9,8,4,4,10,9,9,10,10,9,5,5,11,10,10],
[9,7,8,8,10,10,8,9,10,8,9,9,11,11,9,10],
[10,7,7,11,11,8,8,12,11,8,8,12,12,9,9,13],
[10,10,11,11,11,11,12,12,11,11,12,12,12,12,13,13]]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0018 seconds
-- 3 examples, 0 failures
12.2.2. En Python
# ---------------------------------------------------------------------
# Un poliominó es una figura geométrica plana formada conectando dos o
# más cuadrados por alguno de sus lados. Los cuadrados se conectan lado
# con lado, pero no se pueden conectar ni por sus vértices, ni juntando
# solo parte de un lado de un cuadrado con parte de un lado de otro. Si
# unimos dos cuadrados se obtiene un dominó, si se juntan tres
# cuadrados se construye un triominó.
#
# Sólo existen dos triominós, el I-triomino (por tener forma de I) y el
# L-triominó (por su forma de L) como se observa en las siguientes
# figuras
#
# X
#- X X
# X XX
#
# El rompecabeza del triominó consiste en cubrir un tablero cuadrado
# con 2^n filas y 2^n columnas, en el que se ha eliminado una casilla,
# con L-triominós de formas que cubran todas las casillas excepto la
# eliminada y los triominós no se solapen.
Capítulo 12. Divide y vencerás 1021
#
# La casilla eliminada se representará con -1 y los L-triominós con
# sucesiones de tres números consecutivos en forma de L. Con esta
# representación una solución del rompecabeza del triominó con 4 filas
# y la fila eliminada en la posición (4,4) es
# ( 3 3 2 2 )
# ( 3 1 1 2 )
# ( 4 1 5 5 )
# ( 4 4 5 -1 )
#
# Definir la función
# triomino : (int, Posicion) -> Tablero
# tal que triomino(n, p) es la solución, mediante divide y vencerás,
# del rompecabeza del triominó en un cuadrado nxn en el que se ha
# eliminado la casilla de la posición p. Por ejemplo,
# >>> triomino(4, (4,4))
# array([[ 3, 3, 2, 2],
# [ 3, 1, 1, 2],
# [ 4, 1, 5, 5],
# [ 4, 4, 5, -1]])
# >>> triomino(4, (2,3))
# array([[ 3, 3, 2, 2],
# [ 3, 1, -1, 2],
# [ 4, 1, 1, 5],
# [ 4, 4, 5, 5]])
# >>> triomino(16, (5,6))
# array([[ 7, 7, 6, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 4, 4],
# [ 7, 5, 5, 6, 6, 4, 4, 5, 6, 4, 4, 5, 5, 3, 3, 4],
# [ 8, 5, 9, 9, 7, 7, 4, 8, 7, 4, 8, 8, 6, 6, 3, 7],
# [ 8, 8, 9, 3, 3, 7, 8, 8, 7, 7, 8, 2, 2, 6, 7, 7],
# [ 8, 8, 7, 3, 9, -1, 8, 8, 7, 7, 6, 6, 2, 8, 7, 7],
# [ 8, 6, 7, 7, 9, 9, 7, 8, 7, 5, 5, 6, 8, 8, 6, 7],
# [ 9, 6, 6, 10, 10, 7, 7, 11, 8, 8, 5, 9, 9, 6, 6, 10],
# [ 9, 9, 10, 10, 10, 10, 11, 11, 1, 8, 9, 9, 9, 9, 10, 10],
# [ 8, 8, 7, 7, 7, 7, 6, 1, 1, 9, 8, 8, 8, 8, 7, 7],
# [ 8, 6, 6, 7, 7, 5, 6, 6, 9, 9, 7, 8, 8, 6, 6, 7],
# [ 9, 6, 10, 10, 8, 5, 5, 9, 10, 7, 7, 11, 9, 9, 6, 10],
# [ 9, 9, 10, 4, 8, 8, 9, 9, 10, 10, 11, 11, 5, 9, 10, 10],
# [ 9, 9, 8, 4, 4, 10, 9, 9, 10, 10, 9, 5, 5, 11, 10, 10],
# [ 9, 7, 8, 8, 10, 10, 8, 9, 10, 8, 9, 9, 11, 11, 9, 10],
1022 Ejercicios de programación con Python
import numpy as np
import numpy.typing as npt
# Verificación
# ============
[8,8,9,3,3,7,8,8,7,7,8,2,2,6,7,7],
[8,8,7,3,9,-1,8,8,7,7,6,6,2,8,7,7],
[8,6,7,7,9,9,7,8,7,5,5,6,8,8,6,7],
[9,6,6,10,10,7,7,11,8,8,5,9,9,6,6,10],
[9,9,10,10,10,10,11,11,1,8,9,9,9,9,10,10],
[8,8,7,7,7,7,6,1,1,9,8,8,8,8,7,7],
[8,6,6,7,7,5,6,6,9,9,7,8,8,6,6,7],
[9,6,10,10,8,5,5,9,10,7,7,11,9,9,6,10],
[9,9,10,4,8,8,9,9,10,10,11,11,5,9,10,10],
[9,9,8,4,4,10,9,9,10,10,9,5,5,11,10,10],
[9,7,8,8,10,10,8,9,10,8,9,9,11,11,9,10],
[10,7,7,11,11,8,8,12,11,8,8,12,12,9,9,13],
[10,10,11,11,11,11,12,12,11,11,12,12,12,12,13,13]]
print(”Verificado”)
# La verificación es
# >>> test_triomino()
# Verificado
1028 Ejercicios de programación con Python
Capítulo 13
Búsqueda en espacios de
estados
Contenido
13.1. Búsqueda en espacios de estados por profundidad . . . .1007
.
13.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1007
.
13.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1008
.
13.2. El problema de las n reinas (mediante búsqueda por pro-
fundidad en espacios de estados) . . . . . . . . . . . . . . .1010
.
13.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1010
.
13.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1013
.
13.3. Búsqueda en espacios de estados por anchura . . . . . . .1015
.
13.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
.
13.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
.
13.4. El problema de las n reinas (mediante búsqueda en espa-
cios de estados por anchura) . . . . . . . . . . . . . . . . .1018
.
13.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1018
.
13.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1021
.
13.5. El problema de la mochila (mediante espacio de estados) 1024
.
13.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1024
.
13.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1027
.
13.6. El tipo abstracto de datos de las colas de prioridad . . . .1029
.
13.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1029
.
1029
1030 Ejercicios de programación con Python
13.16.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
.
13.16.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1099
.
13.17. Problema de las jarras . . . . . . . . . . . . . . . . . . . . . .1100
.
13.17.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1100
.
13.17.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1104
.
buscaProfundidad :: Eq nodo => (nodo -> [nodo]) -> (nodo -> Bool)
-> nodo -> [nodo]
buscaProfundidad sucesores esFinal inicial =
1032 Ejercicios de programación con Python
13.1.2. En Python
# ---------------------------------------------------------------------
# Las características de los problemas de espacios de estados son:
# + un conjunto de las posibles situaciones o nodos que constituye el
# espacio de estados (estos son las potenciales soluciones que se
# necesitan explorar),
# + un conjunto de movimientos de un nodo a otros nodos, llamados los
# sucesores del nodo,
# + un nodo inicial y
# + un nodo objetivo que es la solución.
#
# Definir las funciones
# buscaProfundidad(Callable[[A], list[A]], Callable[[A], bool], A) -> list[A]
# buscaProfundidad1(Callable[[A], list[A]], Callable[[A], bool], A) -> Optiona
# tales que
# + buscaProfundidad(s, o, e) es la lista de soluciones del
# problema de espacio de estado definido por la función sucesores s,
# el objetivo o y estado inicial e obtenidas mediante búsqueda en
# profundidad.
# + buscaProfundidad1(s, o, e) es la orimera solución del
# problema de espacio de estado definido por la función sucesores s,
# el objetivo o y estado inicial e obtenidas mediante búsqueda en
# profundidad.
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
es = sucesores(cp)
p = reduce(lambda x, y: apila(y, x), es, desapila(p))
return None
1034 Ejercicios de programación con Python
-- Los nodos del problema de las n reinas son ternas formadas por la
-- columna de la última reina colocada, el número de columnas del
-- tablero y la solución parcial de las reinas colocadas anteriormente.
type NodoNR = (Columna,Columna,SolNR)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
take 3 (solucionesNR 8) `shouldBe`
[[(1,1),(2,5),(3,8),(4,6),(5,3),(6,7),(7,2),(8,4)],
[(1,1),(2,6),(3,8),(4,3),(5,7),(6,4),(7,2),(8,5)],
[(1,1),(2,7),(3,4),(4,6),(5,8),(6,2),(7,5),(8,3)]]
it ”e2” $
nSolucionesNR 8 `shouldBe` 92
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.1173 seconds
-- 2 examples, 0 failures
Capítulo 13. Búsqueda en espacios de estados 1037
13.2.2. En Python
# ---------------------------------------------------------------------
# El problema de las n reinas consiste en colocar n reinas en un
# tablero cuadrado de dimensiones n por n de forma que no se encuentren
# más de una en la misma línea: horizontal, vertical o diagonal.
#
# Las posiciones de las reinas en el tablero se representan por su
# columna y su fila.
# Columna = int
# Fila = int
#
# Una solución del problema de las n reinas es una lista de
# posiciones.
# SolNR = list[tuple[Columna, Fila]]
#
# Usando el procedimiento de búsqueda en profundidad, definir las
# funciones
# solucionesNR : (int) -> list[SolNR]
# primeraSolucionNR : (int) -> SolNR
# nSolucionesNR : (int) -> int
# tales que
# + solucionesNR(n) es la lista de las soluciones del problema de las n
# reinas, por búsqueda de espacio de estados en profundidad. Por
# ejemplo,
# >>> solucionesNR(8)[:3]
# [[(1, 8), (2, 4), (3, 1), (4, 3), (5, 6), (6, 2), (7, 7), (8, 5)],
# [(1, 8), (2, 3), (3, 1), (4, 6), (5, 2), (6, 5), (7, 7), (8, 4)],
# [(1, 8), (2, 2), (3, 5), (4, 3), (5, 1), (6, 7), (7, 4), (8, 6)]]
# + primeraSolucionNR(n) es la primera solución del problema de las n
# reinas, por búsqueda en espacio de estados por profundidad. Por
# ejemplo,
# >>> primeraSolucionNR(8)
# [(1, 8), (2, 4), (3, 1), (4, 3), (5, 6), (6, 2), (7, 7), (8, 5)]
# + nSolucionesNR(n) es el número de soluciones del problema de las n
# reinas, por búsqueda en espacio de estados. Por ejemplo,
# >>> nSolucionesNR(8)
# 92
# ---------------------------------------------------------------------
Columna = int
Fila = int
SolNR = list[tuple[Columna, Fila]]
# Los nodos del problema de las n reinas son ternas formadas por la
# columna de la última reina colocada, el número de columnas del
# tablero y la solución parcial de las reinas colocadas anteriormente.
NodoNR = tuple[Columna, Columna, SolNR]
# Verificación
# ============
# La verificación es
#
-- Definir la función
-- buscaAnchura :: Eq nodo => (nodo -> [nodo]) -> (nodo -> Bool)
-- -> nodo -> [nodo]
-- tal que (buscaAnchura s o e) es la lista de soluciones del
-- problema de espacio de estado definido por la función sucesores s, el
-- objetivo o y estado inicial e obtenidas mediante búsqueda en
-- anchura.
-- ---------------------------------------------------------------------
buscaAnchura :: Eq nodo => (nodo -> [nodo]) -> (nodo -> Bool)
-> nodo -> [nodo]
buscaAnchura sucesores esFinal inicial =
aux (inserta inicial vacia)
where
aux p
| esVacia p = []
| esFinal (primero p) = primero p : aux (resto p)
| otherwise = aux (foldr
inserta
(resto p)
(sucesores (primero p)))
13.3.2. En Python
# ---------------------------------------------------------------------
# Las características de los problemas de espacios de estados son:
# + un conjunto de las posibles situaciones o nodos que constituye el
# espacio de estados (estos son las potenciales soluciones que se
# necesitan explorar),
# + un conjunto de movimientos de un nodo a otros nodos, llamados los
# sucesores del nodo,
# + un nodo inicial y
# + un nodo objetivo que es la solución.
#
# Definir las funciones
# buscaAnchura(Callable[[A], list[A]], Callable[[A], bool], A) -> list[A]
Capítulo 13. Búsqueda en espacios de estados 1041
A = TypeVar('A')
setrecursionlimit(10**6)
es = sucesores(pc)
c = reduce(lambda x, y: inserta(y, x), es, resto(c))
return None
-- ejemplo,
-- take 3 (solucionesNR 8)
-- [[(1,8),(2,4),(3,1),(4,3),(5,6),(6,2),(7,7),(8,5)],
-- [(1,8),(2,3),(3,1),(4,6),(5,2),(6,5),(7,7),(8,4)],
-- [(1,8),(2,2),(3,5),(4,3),(5,1),(6,7),(7,4),(8,6)]]
-- + (primeraSolucionNR n) es la primera solución del problema de las n
-- reinas, por búsqueda en espacio de estados por anchura. Por
-- ejemplo,
-- λ> primeraSolucionNR 8
-- [(1,8),(2,4),(3,1),(4,3),(5,6),(6,2),(7,7),(8,5)]
-- + (nSolucionesNR n) es el número de soluciones del problema de las n
-- reinas, por búsqueda en espacio de estados. Por ejemplo,
-- nSolucionesNR 8 == 92
-- ---------------------------------------------------------------------
-- Los nodos del problema de las n reinas son ternas formadas por la
-- columna de la última reina colocada, el número de columnas del
-- tablero y la solución parcial de las reinas colocadas anteriormente.
type NodoNR = (Columna,Columna,SolNR)
nSolucionesNR =
length . solucionesNR
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
take 3 (solucionesNR 8) `shouldBe`
[[(1,8),(2,4),(3,1),(4,3),(5,6),(6,2),(7,7),(8,5)],
[(1,8),(2,3),(3,1),(4,6),(5,2),(6,5),(7,7),(8,4)],
[(1,8),(2,2),(3,5),(4,3),(5,1),(6,7),(7,4),(8,6)]]
it ”e2” $
Capítulo 13. Búsqueda en espacios de estados 1045
nSolucionesNR 8 `shouldBe` 92
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.2116 seconds
-- 2 examples, 0 failures
13.4.2. En Python
# ---------------------------------------------------------------------
# El problema de las n reinas consiste en colocar n reinas en un
# tablero cuadrado de dimensiones n por n de forma que no se encuentren
# más de una en la misma línea: horizontal, vertical o diagonal.
#
# Las posiciones de las reinas en el tablero se representan por su
# columna y su fila.
# Columna = int
# Fila = int
#
# Una solución del problema de las n reinas es una lista de
# posiciones.
# SolNR = list[tuple[Columna, Fila]]
#
# Usando el procedimiento de búsqueda en profundidad, definir las
# funciones
# solucionesNR : (int) -> list[SolNR]
# primeraSolucionNR : (int) -> SolNR
# nSolucionesNR : (int) -> int
# tales que
# + solucionesNR(n) es la lista de las soluciones del problema de las n
# reinas, por búsqueda de espacio de estados en profundidad. Por
# ejemplo,
# >>> solucionesNR(8)[:3]
# [[(1,1),(2,5),(3,8),(4,6),(5,3),(6,7),(7,2),(8,4)],
# [(1,1),(2,6),(3,8),(4,3),(5,7),(6,4),(7,2),(8,5)],
# [(1,1),(2,7),(3,4),(4,6),(5,8),(6,2),(7,5),(8,3)]]
1046 Ejercicios de programación con Python
Columna = int
Fila = int
SolNR = list[tuple[Columna, Fila]]
# Los nodos del problema de las n reinas son ternas formadas por la
# columna de la última reina colocada, el número de columnas del
# tablero y la solución parcial de las reinas colocadas anteriormente.
NodoNR = tuple[Columna, Columna, SolNR]
# Verificación
# ============
# La verificación es
# >>> test_nReinas()
# Verificado
1048 Ejercicios de programación con Python
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
mochila [(2,3),(3,5),(4,6),(5,10)] 8
`shouldBe` ([(5,10.0),(3,5.0)],15.0)
it ”e2” $
mochila [(2,3),(3,5),(5,6)] 10
`shouldBe` ([(3,5.0),(3,5.0),(2,3.0),(2,3.0)],16.0)
it ”e3” $
mochila [(8,15),(15,10),(3,6),(6,13),(2,4),(4,8),(5,6),(7,7)] 35
`shouldBe` ([(6,13.0),(6,13.0),(6,13.0),(6,13.0),(6,13.0),(3,6.0),(2,4.0)],75
it ”e4” $
mochila [(2,2.8),(3,4.4),(5,6.1)] 10
`shouldBe` ([(3,4.4),(3,4.4),(2,2.8),(2,2.8)],14.4)
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0424 seconds
Capítulo 13. Búsqueda en espacios de estados 1051
-- 4 examples, 0 failures
13.5.2. En Python
# ---------------------------------------------------------------------
# Se tiene una mochila de capacidad de peso p y una lista de n objetos
# para colocar en la mochila. Cada objeto i tiene un peso w(i) y un
# valor v(i). Considerando la posibilidad de colocar el mismo objeto
# varias veces en la mochila, el problema consiste en determinar la
# forma de colocar los objetos en la mochila sin sobrepasar la
# capacidad de la mochila colocando el máximo valor posible.
#
# Para solucionar el problema se definen los siguientes tipos:
# + Una solución del problema de la mochila es una lista de objetos.
# SolMoch = list[Objeto]
# + Los objetos son pares formado por un peso y un valor
# Objeto = tuple[Peso, Valor]
# + Los pesos son número enteros
# Peso = int
# + Los valores son números reales.
# Valor = float
# + Los estados del problema de la mochila son 5-tupla de la forma
# (v,p,l,o,s) donde v es el valor de los objetos colocados, p es el
# peso de los objetos colocados, l es el límite de la capacidad de la
# mochila, o es la lista de los objetos colocados (ordenados de forma
# creciente según sus pesos) y s es la solución parcial.
# NodoMoch = tuple[Valor, Peso, Peso, list[Objeto], SolMoch]
#
# Usando el procedimiento de [búsqueda en profundidad](http://bit.ly/2sqPtGs),
# definir la función
# mochila : (list[Objeto], Peso) -> tuple[SolMoch, Valor]
# tal que mochila(os, l) es la solución del problema de la mochila para
# la lista de objetos os y el límite de capacidad l. Por ejemplo,
# >>> mochila([(2,3),(3,5),(4,6),(5,10)], 8)
# ([(5, 10), (3, 5)], 15)
# >>> mochila([(2,3),(3,5),(5,6)], 10)
# ([(3, 5), (3, 5), (2, 3), (2, 3)], 16)
# >>> mochila([(8,15),(15,10),(3,6),(6,13), (2,4),(4,8),(5,6),(7,7)], 35)
# ([(6, 13), (6, 13), (6, 13), (6, 13), (6, 13), (3, 6), (2, 4)], 75)
# >>> mochila([(2,2.8),(3,4.4),(5,6.1)], 10)
1052 Ejercicios de programación con Python
Peso = int
Valor = float
Objeto = tuple[Peso, Valor]
SolMoch = list[Objeto]
NodoMoch = tuple[Valor, Peso, Peso, list[Objeto], SolMoch]
# Verificación
# ============
# La verificación es
# >>> test_Mochila()
# Verificado
module TAD.ColaDePrioridad
(CPrioridad,
vacia, -- Ord a => CPrioridad a
inserta, -- Ord a => a -> CPrioridad a -> CPrioridad a
primero, -- Ord a => CPrioridad a -> a
resto, -- Ord a => CPrioridad a -> CPrioridad a
esVacia, -- Ord a => CPrioridad a -> Bool
) where
import TAD.ColaDePrioridadConListas
13.6.2. En Python
# Una cola de prioridad es una cola en la que cada elemento tiene
# asociada una prioridad. La operación de extracción siempre elige el
# elemento de menor prioridad.
#
# Las operaciones que definen a tipo abstracto de datos (TAD) de las
# colas de prioridad (cuyos elementos son del tipo a) son las
# siguientes:
# vacia :: Ord a => CPrioridad a
# inserta :: Ord a => a -> CPrioridad a -> CPrioridad a
# primero :: Ord a => CPrioridad a -> a
# resto :: Ord a => CPrioridad a -> CPrioridad a
Capítulo 13. Búsqueda en espacios de estados 1055
__all__ = [
'CPrioridad',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
]
module TAD.ColaDePrioridadConListas
(CPrioridad,
vacia, -- Ord a => CPrioridad a
inserta, -- Ord a => a -> CPrioridad a -> CPrioridad a
primero, -- Ord a => CPrioridad a -> a
resto, -- Ord a => CPrioridad a -> CPrioridad a
esVacia, -- Ord a => CPrioridad a -> Bool
) where
import Test.QuickCheck
-- -7 | -6 | -2 | 0
-- -10 | -10 | -5 | 1 | 4 | 6 | 6 | 9 | 10
-- -
-- -13 | -11 | -9 | -5 | -2 | -1 | 0 | 1 | 2 | 2 | 13 | 14
-- -15 | -13 | -13 | -5 | -3 | -1 | 3 | 5 | 7 | 9 | 9 | 14 | 16
-- -
-- -17 | -15 | -14 | -5 | -2 | 1 | 1 | 2 | 5 | 7
genCPrioridad :: (Arbitrary a, Num a, Ord a) => Gen (CPrioridad a)
genCPrioridad = do
xs <- listOf arbitrary
return (foldr inserta vacia xs)
-- Comprobación.
-- λ> quickCheck prop_inserta_conmuta
-- +++ OK, passed 100 tests.
-- Comprobación.
-- λ> quickCheck prop_primero_inserta_vacia
-- +++ OK, passed 100 tests.
Capítulo 13. Búsqueda en espacios de estados 1059
-- Comprobación.
-- λ> quickCheck prop_primero_inserta
-- +++ OK, passed 100 tests.
-- Comprobación.
-- λ> quickCheck prop_resto_inserta_vacia
-- +++ OK, passed 100 tests.
-- Comprobación:
-- λ> quickCheck prop_resto_inserta
-- +++ OK, passed 100 tests.
-- Comprobación.
-- λ> quickCheck prop_vacia_es_vacia
-- +++ OK, passed 100 tests.
1060 Ejercicios de programación con Python
-- Comprobación.
-- λ> quickCheck prop_inserta_no_es_vacia
-- +++ OK, passed 100 tests.
13.7.2. En Python
# Se define la clase CPrioridad con los siguientes métodos:
# + inserta(x) añade x a la cola.
# + primero() es el primero de la cola.
# + resto() elimina el primero de la cola.
# + esVacia() se verifica si la cola es vacía.
# Por ejemplo,
# >>> c = CPrioridad()
# >>> c
# -
# >>> c.inserta(5)
# >>> c.inserta(2)
# >>> c.inserta(3)
# >>> c.inserta(4)
# >>> c
# 2 | 3 | 4 | 5
# >>> c.primero()
# 2
# >>> c.resto()
# >>> c
# 3 | 4 | 5
# >>> c.esVacia()
# False
# >>> c = CPrioridad()
# >>> c.esVacia()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
Capítulo 13. Búsqueda en espacios de estados 1061
# >>> vacia()
# -
# >>> inserta(4, inserta(3, inserta(2, inserta(5, vacia()))))
# 2 | 3 | 4 | 5
# >>> primero (inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 2
# >>> resto (inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 3 | 4 | 5
# >>> esVacia(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# False
# >>> esVacia(vacia())
# True
#
# Finalmente, se define un generador aleatorio de colas de prioridad y
# se comprueba que las colas de prioridad cumplen las propiedades de su
# especificación.
__all__ = [
'CPrioridad',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
]
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
1062 Ejercicios de programación con Python
pass
A = TypeVar('A', bound=Comparable)
@dataclass
class CPrioridad(Generic[A]):
_elementos: list[A] = field(default_factory=list)
# La comprobación es
# > poetry run pytest -q ColaDePrioridadConListas.py
# 2 passed in 0.54s
buscaPM :: Ord n => (n -> [n]) -> (n -> Bool) -> n -> [n]
buscaPM sucesores esFinal x = busca' (inserta x vacia) where
busca' c
| esVacia c = []
| esFinal (primero c) = primero c : busca' (resto c)
| otherwise = busca' (foldr inserta (resto c) (sucesores (primero c)))
13.8.2. En Python
# ---------------------------------------------------------------------
# En la búsqueda por primero el mejos se supone que los estados están
# ordenados mediante una función, la heurística, que es una rstimación
# de su coste para llegar a un estado final.
#
# Definir la función
1066 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
es = sucesores(primero(c))
c = reduce(lambda x, y: inserta(y, x), es, resto(c))
return None
Capítulo 13. Búsqueda en espacios de estados 1067
-- Estado inicial
-- ==============
-- Estado final
-- ============
-- Sucesores
-- =========
-- │ 7 6 5 │ │ 7 0 5 │ │ 7 6 5 │ │ 7 6 5 │
-- └ ┘, └ ┘, └ ┘, └ ┘]
tablerosSucesores :: Tablero -> [Tablero]
tablerosSucesores t =
[intercambia t p q | q <- posicionesVecinas p]
where p = posicionHueco t
-- Heurística
-- ==========
-- Comparación de estados
-- ======================
1072 Ejercicios de programación con Python
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
map toLists (solucion_8puzzle (fromLists [[0,1,3],[8,2,4],[7,6,5]]))
`shouldBe` [[[0,1,3],
[8,2,4],
[7,6,5]],
[[1,0,3],
[8,2,4],
[7,6,5]],
[[1,2,3],
[8,0,4],
[7,6,5]]]
it ”e2” $
length (solucion_8puzzle (fromLists [[2,6,3],[5,0,4],[1,7,8]]))
`shouldBe` 21
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.1361 seconds
-- 2 examples, 0 failures
Capítulo 13. Búsqueda en espacios de estados 1073
13.9.2. En Python
# ---------------------------------------------------------------------
# Para el 8-puzzle se usa un cajón cuadrado en el que hay situados 8
# bloques cuadrados. El cuadrado restante está sin rellenar. Cada
# bloque tiene un número. Un bloque adyacente al hueco puede deslizarse
# hacia él. El juego consiste en transformar la posición inicial en la
# posición final mediante el deslizamiento de los bloques. En
# particular, consideramos el estado inicial y final siguientes:
#
# +---+---+---+ +---+---+---+
# | | 1 | 3 | | 1 | 2 | 3 |
# +---+---+---+ +---+---+---+
# | 8 | 2 | 4 | | 8 | | 4 |
# +---+---+---+ +---+---+---+
# | 7 | 5 | 5 | | 7 | 6 | 5 |
# +---+---+---+ +---+---+---+
# Estado inicial Estado final
#
# Para solucionar el problema se definen los siguientes tipos:
# + Tablero es lista de listas de números enteros (que representan las
# piezas en cada posición y el 0 representa el hueco):
# Tablero = list[list[int]]
# + Estado es una tupla (h, n, ts), donde ts es una listas de tableros
# [t_n,...,t_1] tal que t_i es un sucesor de t_(i-1) y h es la
# heurística de t_n.
# Estado = tuple[int, int, list[Tablero]]
#
# Usando el procedimiento de [búsqueda por primero el mejor](???),
# definir la función
# solucion_8puzzle : (Tablero) -> Tablero
# tal que solucion_8puzzle(t) es la solución del problema del problema
# del 8 puzzle a partir del tablero t. Por ejemplo,
# >>> solucion_8puzzle([[0,1,3],[8,2,4],[7,6,5]])
# [[[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 2, 3],
1074 Ejercicios de programación con Python
# [8, 0, 4],
# [7, 6, 5]]]
# >>> solucion_8puzzle([[8,1,3],[0,2,4],[7,6,5]])
# [[[8, 1, 3],
# [0, 2, 4],
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 2, 3],
# [8, 0, 4],
# [7, 6, 5]]]
# >>> len(solucion_8puzzle([[2,6,3],[5,0,4],[1,7,8]]))
# 21
# ---------------------------------------------------------------------
Tablero = list[list[int]]
# Tablero final
# =============
# Posiciones
# ==========
# Heurística
# ==========
# Estados
# =======
# Estado inicial
# ==============
# Estado final
# ============
# Sucesores
# =========
vecinas.append((i - 1, j))
if i < 2:
vecinas.append((i + 1, j))
if j > 0:
vecinas.append((i, j - 1))
if j < 2:
vecinas.append((i, j + 1))
return vecinas
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]]]),
# (2, 2, [[[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]]])]
# >>> sucesores(es[1])
# [(0, 3, [[[1, 2, 3],
# [8, 0, 4],
# [7, 6, 5]],
# [[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]]]),
# (4, 3, [[[1, 3, 0],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]]])]
def sucesores(e: Estado) -> list[Estado]:
(_, n, ts) = e
return [(heuristica(t1), n+1, [t1] + ts)
for t1 in tablerosSucesores(ts[0])
if t1 not in ts]
# Solución
# ========
if r is None:
return None
(_, _, ts) = r
ts.reverse()
return ts
# Verificación
# ============
# La verificación es
# src> poetry run pytest -q BPM_8Puzzle.py
# 1 passed in 0.10s
buscaEscalada :: Ord n => (n -> [n]) -> (n -> Bool) -> n -> [n]
buscaEscalada sucesores esFinal x = busca' (inserta x vacia) where
busca' c
| esVacia c = []
| esFinal (primero c) = [primero c]
| otherwise = busca' (foldr inserta vacia (sucesores (primero c)))
13.10.2. En Python
# ---------------------------------------------------------------------
# En la búsqueda en escalada se supone que los estados están ordenados
# mediante una función, la heurística, que es una estimación de su
# coste para llegar a un estado final.
#
# Definir la función
# buscaEscalada(Callable[[A], list[A]], Callable[[A], bool], A) -> list[A]
# tal que buscaEscalada(s, o, e) es la lista de soluciones del problema de
# espacio de estado definido por la función sucesores s, el objetivo
# o y estado inicial e, obtenidas buscando en escalada.
# ---------------------------------------------------------------------
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
return None
-- Una arista esta formada por dos vértices junto con su peso.
type Arista a b = (a,a,b)
esFinal _ = False
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
prim g1 `shouldBe` [(2,4,55),(1,3,34),(2,5,32),(1,2,12)]
it ”e2” $
prim g2 `shouldBe` [(2,5,32),(2,4,12),(1,2,13),(1,3,11)]
it ”e3” $
prim g3 `shouldBe` [(5,7,9),(2,3,7),(5,4,5),(6,5,3),(1,6,6),(1,2,5)]
it ”e4” $
prim g4 `shouldBe` [(5,7,9),(5,4,5),(5,3,1),(6,5,3),(1,6,6),(1,2,5)]
-- La verificación es
-- λ> verifica
--
Capítulo 13. Búsqueda en espacios de estados 1085
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0043 seconds
-- 4 examples, 0 failures
13.11.2. En Python
# ---------------------------------------------------------------------
# El [algoritmo de Prim](https://bit.ly/466fwRe) calcula un árbol
# recubridor mínimo en un grafo conexo y ponderado. Es decir, busca un
# subconjunto de aristas que, formando un árbol, incluyen todos los
# vértices y donde el valor de la suma de todas las aristas del árbol
# es el mínimo.
#
# El algoritmo de Prim funciona de la siguiente manera:
# + Inicializar un árbol con un único vértice, elegido arbitrariamente,
# del grafo.
# + Aumentar el árbol por un lado. Llamamos lado a la unión entre dos
# vértices: de las posibles uniones que pueden conectar el árbol a los
# vértices que no están aún en el árbol, encontrar el lado de menor
# distancia y unirlo al árbol.
# + Repetir el paso 2 (hasta que todos los vértices pertenezcan al
# árbol)
#
# Usando la [búsqueda en escalada](https://bit.ly/3Kk4A99) el [tipo
# abstracto de datos de los grafos](https://bit.ly/45cQ3Fo), definir la
# función
# prim : (Grafo) -> list[tuple[Peso, Vertice, Vertice]]
# tal que prim(g) es el árbol de expansión mínimo del grafo g
# calculado mediante el algoritmo de Prim con bñusqueda en
# escalada. Por ejemplo, si g1, g2, g3 y g4 son los grafos definidos
# por
# g1 = creaGrafo (Orientacion.ND,
# (1,5),
# [((1,2),12),((1,3),34),((1,5),78),
# ((2,4),55),((2,5),32),
# ((3,4),61),((3,5),44),
1086 Ejercicios de programación con Python
# ((4,5),93)])
# g2 = creaGrafo (Orientacion.ND,
# (1,5),
# [((1,2),13),((1,3),11),((1,5),78),
# ((2,4),12),((2,5),32),
# ((3,4),14),((3,5),44),
# ((4,5),93)])
# g3 = creaGrafo (Orientacion.ND,
# (1,7),
# [((1,2),5),((1,3),9),((1,5),15),((1,6),6),
# ((2,3),7),
# ((3,4),8),((3,5),7),
# ((4,5),5),
# ((5,6),3),((5,7),9),
# ((6,7),11)])
# g4 = creaGrafo (Orientacion.ND,
# (1,7),
# [((1,2),5),((1,3),9),((1,5),15),((1,6),6),
# ((2,3),7),
# ((3,4),8),((3,5),1),
# ((4,5),5),
# ((5,6),3),((5,7),9),
# ((6,7),11)])
# entonces
# prim(g1) == [((2,4),55),((1,3),34),((2,5),32),((1,2),12)]
# prim(g2) == [((2,5),32),((2,4),12),((1,2),13),((1,3),11)]
# prim(g3) == [((5,7),9),((2,3),7),((5,4),5),((6,5),3),((1,6),6),((1,2),5)]
# prim(g4) == [((5,7),9),((5,4),5),((5,3),1),((6,5),3),((1,6),6),((1,2),5)]
# ---------------------------------------------------------------------
g1 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),12),((1,3),34),((1,5),78),
((2,4),55),((2,5),32),
Capítulo 13. Búsqueda en espacios de estados 1087
((3,4),61),((3,5),44),
((4,5),93)])
g2 = creaGrafo (Orientacion.ND,
(1,5),
[((1,2),13),((1,3),11),((1,5),78),
((2,4),12),((2,5),32),
((3,4),14),((3,5),44),
((4,5),93)])
g3 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),7),
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
g4 = creaGrafo (Orientacion.ND,
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),1),
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
# expansión mínimo.
def esFinal(e: Estado) -> bool:
return e[2] == []
# Verificación
# ============
# La verificación es
# >>> test_prim()
# Verificado
Capítulo 13. Búsqueda en espacios de estados 1089
data Orilla = I | D
deriving (Eq, Show)
granjero :: [[Estado]]
granjero =
[reverse es | (Nodo es) <- buscaProfundidad sucesores esFinal inicial]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
head granjero `shouldBe`
[(I,I,I,I),(D,I,D,I),(I,I,D,I),(D,D,D,I),
(I,D,I,I),(D,D,I,D),(I,D,I,D),(D,D,D,D)]
it ”e2” $
1092 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0008 seconds
-- 2 examples, 0 failures
13.12.2. En Python
# ---------------------------------------------------------------------
# Un granjero está parado en un lado del río y con él tiene un lobo,
# una cabra y una repollo. En el río hay un barco pequeño. El granjero
# desea cruzar el río con sus tres posesiones. No hay puentes y en el
# barco hay solamente sitio para el granjero y un artículo. Si deja
# la cabra con la repollo sola en un lado del río la cabra comerá la
# repollo. Si deja el lobo y la cabra en un lado, el lobo se comerá a
# la cabra. ¿Cómo puede cruzar el granjero el río con los tres
# artículos, sin que ninguno se coma al otro?
#
# Para representar el problema se definen los siguientes tipos de dato:
# + Orilla con dos constructores I y D que representan las orillas
# izquierda y derecha, respectivamente.
# + Estado que es una tupla que representa en qué orilla se encuentra
# cada uno de los elementos (granjero, lobo, cabra, repollo). Por
# ejemplo, (I,D,D,I) representa que el granjero está en la izquierda,
# que el lobo está en la derecha, que la cabra está en la derecha y
# el repollo está en la izquierda.
#
# Usando el [procedimiento de búsqueda en profundidad](https://bit.ly/3NPI4qV),
# definir la función
# granjero : () -> list[list[Estado]]
# tal que granjero() son las soluciones del problema del granjero
# mediante el patrón de búsqueda en espacio de estados. Por ejemplo,
# >>> granjero()
# [[(I,I,I,I),(D,I,D,I),(I,I,D,I),(D,I,D,D),(I,I,I,D),(D,D,I,D),(I,D,I,D),(D,D
# [(I,I,I,I),(D,I,D,I),(I,I,D,I),(D,D,D,I),(I,D,I,I),(D,D,I,D),(I,D,I,D),(D,D
Capítulo 13. Búsqueda en espacios de estados 1093
# ---------------------------------------------------------------------
class Orilla(Enum):
I = 0
D = 1
I = Orilla.I
D = Orilla.D
# sucesoresE((D,I,D,I)) == [(I,I,D,I),(I,I,I,I)]
def sucesoresE(e: Estado) -> list[Estado]:
def mov(n: int, e: Estado) -> Estado:
(g,l,c,r) = e
if n == 1:
return (opuesta(g), l, c, r)
if n == 2:
return (opuesta(g), opuesta(l), c, r)
if n == 3:
return (opuesta(g), l, opuesta(c), r)
return (opuesta(g), l, c, opuesta(r))
return [mov(n, e) for n in range(1, 5) if seguro(mov(n, e))]
# # Verificación
# # ============
# La verificación es
# >>> test_granjero()
# Verificado
-- [[B,B,H,V,V],[B,H,B,V,V],[H,B,B,V,V],[V,B,B,H,V],[V,B,H,B,V],[V,H,B,B,V],
-- [H,V,B,B,V],[B,V,H,B,V],[B,H,V,B,V],[H,B,V,B,V],[B,B,V,H,V],[B,B,V,V,H],
-- [B,H,V,V,B],[H,B,V,V,B],[V,B,H,V,B],[V,H,B,V,B],[H,V,B,V,B],[B,V,H,V,B],
-- [B,V,V,H,B],[H,V,V,B,B],[V,H,V,B,B],[V,V,H,B,B]]
-- λ> head (fichas buscaAnchura 2 2)
-- [[B,B,H,V,V],[B,B,V,V,H],[B,H,V,V,B],[B,V,V,H,B],[H,V,V,B,B],
-- [V,V,H,B,B]]
-- λ> head (fichas buscaPM 2 2)
-- [[B,B,H,V,V],[B,H,B,V,V],[B,V,B,H,V],[H,V,B,B,V],[V,H,B,B,V],
-- [V,V,B,B,H],[V,V,B,H,B],[V,V,H,B,B]]
-- λ> head (fichas buscaEscalada 2 2)
-- [[B,B,H,V,V],[B,H,B,V,V],[B,V,B,H,V],[H,V,B,B,V],[V,H,B,B,V],
-- [V,V,B,B,H],[V,V,B,H,B],[V,V,H,B,B]]
-- ---------------------------------------------------------------------
data Ficha = B | V | H
deriving (Eq, Show)
-- Heurística
-- ==========
1100 Ejercicios de programación con Python
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
Capítulo 13. Búsqueda en espacios de estados 1101
spec = do
it ”e1” $
head (fichas buscaProfundidad 2 2) `shouldBe`
[[B,B,H,V,V],[B,H,B,V,V],[H,B,B,V,V],[V,B,B,H,V],[V,B,H,B,V],[V,H,B,B,V],
[H,V,B,B,V],[B,V,H,B,V],[B,H,V,B,V],[H,B,V,B,V],[B,B,V,H,V],[B,B,V,V,H],
[B,H,V,V,B],[H,B,V,V,B],[V,B,H,V,B],[V,H,B,V,B],[H,V,B,V,B],[B,V,H,V,B],
[B,V,V,H,B],[H,V,V,B,B],[V,H,V,B,B],[V,V,H,B,B]]
it ”e2” $
head (fichas buscaAnchura 2 2) `shouldBe`
[[B,B,H,V,V],[B,B,V,V,H],[B,H,V,V,B],[B,V,V,H,B],[H,V,V,B,B],[V,V,H,B,B]]
it ”e3” $
head (fichas buscaPM 2 2) `shouldBe`
[[B,B,H,V,V],[B,H,B,V,V],[B,V,B,H,V],[H,V,B,B,V],[V,H,B,B,V],[V,V,B,B,H],
[V,V,B,H,B],[V,V,H,B,B]]
it ”e4” $
head (fichas buscaEscalada 2 2) `shouldBe`
[[B,B,H,V,V],[B,H,B,V,V],[B,V,B,H,V],[H,V,B,B,V],[V,H,B,B,V],[V,V,B,B,H],
[V,V,B,H,B],[V,V,H,B,B]]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0055 seconds
-- 4 examples, 0 failures
13.13.2. En Python
# ---------------------------------------------------------------------
# Para el problema de las fichas de orden (m,n) se considera un tablero
# con m+n+1 cuadrados consecutivos.
#
# Inicialmente, en cada uno de los m primeros cuadrados hay una ficha
# blanca, a continuación un hueco y en cada uno de los n últimos
# cuadrados hay una ficha verde. El objetivo consiste en tener las
# fichas verdes al principio y las blancas al final.
1102 Ejercicios de programación con Python
#
# Por ejemplo, en el problema de las fichas de orden (3,3) el tablero
# inicial es
# +---+---+---+---+---+---+---+
# | B | B | B | | V | V | V |
# +---+---+---+---+---+---+---+
# y el final es
# +---+---+---+---+---+---+---+
# | V | V | V | | B | B | B |
# +---+---+---+---+---+---+---+
#
# Los movimientos permitidos consisten en desplazar una ficha al hueco
# saltando, como máximo, sobre otras dos.
#
# Además, se considera la heurística que para cada tablero vale la suma
# de piezas blancas situadas a la izquierda de cada una de las piezas
# verdes. Por ejemplo, para el estado
# +---+---+---+---+---+---+---+
# | B | V | B | | V | V | B |
# +---+---+---+---+---+---+---+
# su valor es 1+2+2 = 5. La heurística de un estado es la del primero
# de sus tableros.
#
# Para representar el problema se definen los siguientes tipos de
# datos:
# + Ficha con tres constructores B, V y H que representan las fichas
# blanca, verde y hueco, respectivamente.
# class Ficha(Enum):
# B = 0
# V = 1
# H = 2
#
# def __repr__(self) -> str:
# return self.name
#
# B = Ficha.B
# V = Ficha.V
# H = Ficha.H
# + Tablero que es una lista de fichas que representa las fichas
# colocadas en el tablero.
Capítulo 13. Búsqueda en espacios de estados 1103
# Tablero = list[Ficha]
# + Estado representa los estados del espacio de búsqueda, donde un
# estado es una lista de tableros [t_n, ..., t_2, t_1] tal que t_1 es
# el tablero inicial y para cada i (2 <= i <= n), t_i es un sucesor
# de t_(i-1).
# class Estado(list[Tablero]):
# def __lt__(self, other):
# return heuristicaT(self[0]) < heuristicaT(other[0])
# + Busqueda es un procedimiento de búsqueda
# Busqueda = Callable[[Callable[[Estado], list[Estado]],
# Callable[[Estado], bool],
# Estado],
# Optional[Estado]]
#
# Usando los métodos de búsqueda estudiado en los ejercicios
# anteriores, definir la función
# fichas :: Busqueda -> Int -> Int -> [[Tablero]]
# tal que fichas(b, m, n) es la lista de las soluciones del problema de
# las fichas de orden (m,n) obtenidas mediante el procedimiento de
# búsqueda b. Por ejemplo,
# >>> fichas(buscaProfundidad1, 2, 2)
# [[B,B,H,V,V],[H,B,B,V,V],[B,H,B,V,V],[B,V,B,H,V],[H,V,B,B,V].
# [B,V,H,B,V],[B,V,V,B,H],[B,H,V,B,V],[B,B,V,H,V],[H,B,V,B,V],
# [V,B,H,B,V],[V,B,V,B,H],[V,H,V,B,B],[V,B,V,H,B],[H,B,V,V,B],
# [B,H,V,V,B],[B,V,V,H,B],[H,V,V,B,B],[V,V,H,B,B]]
# >>> fichas(buscaAnchura1, 2, 2)
# [[B,B,H,V,V],[H,B,B,V,V],[V,B,B,H,V],[V,H,B,B,V],[V,V,B,B,H],
# [V,V,H,B,B]]
# >>> fichas(buscaPM, 2, 2)
# [[B,B,H,V,V],[H,B,B,V,V],[V,B,B,H,V],[V,B,B,V,H],[V,H,B,V,B],
# [V,V,B,H,B],[V,V,H,B,B]]
# >>> fichas(buscaEscalada, 2, 2)
# [[B,B,H,V,V],[B,H,B,V,V],[H,B,B,V,V],[V,B,B,H,V],[V,B,B,V,H],
# [V,H,B,V,B],[V,V,B,H,B],[V,V,H,B,B]]
# ---------------------------------------------------------------------
class Ficha(Enum):
B = 0
V = 1
H = 2
B = Ficha.B
V = Ficha.V
H = Ficha.H
Tablero = list[Ficha]
return t.index(H)
# Heurística
# ==========
class Estado(list[Tablero]):
def __lt__(self, e: list[Tablero]) -> bool:
1106 Ejercicios de programación con Python
# Verificación
# ============
# La verificación es
# >>> test_fichas()
# Verificado
-- 3 | 4 1 2 7 8 5 6
-- 4 | 3 2 1 8 7 6 5
-- 5 | 6 7 8 1 2 3 4
-- 6 | 5 8 7 2 1 4 3
-- 7 | 8 5 6 3 4 1 2
-- 8 | 7 6 5 4 3 2 1
-- donde las filas indican los jugadores y las columnas los días; es
-- decir, el elemento (i,j) indica el adversario del jugador i el día j;
-- por ejemplo, el adversario del jugador 2 el 4ª día es el jugador 6.
--
-- Para representar el problema se define el tipo Calendario como
-- matrices de enteros
-- type Calendario = Matrix Int
--
-- Usando el [procedimiento de búsqueda en profundidad](https://bit.ly/3NPI4qV),
-- definir la función
-- calendario :: Int -> [Calendario]
-- tal que (calendario n) son las soluciones del problema del calendario,
-- con n participantes, mediante el patrón de búsqueda em
-- profundidad. Por ejemplo,
-- λ> head (calendario 6)
-- ┌ ┐
-- │ 2 3 4 5 6 │
-- │ 1 4 5 6 3 │
-- │ 5 1 6 4 2 │
-- │ 6 2 1 3 5 │
-- │ 3 6 2 1 4 │
-- │ 4 5 3 2 1 │
-- └ ┘
--
-- λ> length (calendario 6)
-- 720
-- λ> length (calendario 5)
-- 0
-- ---------------------------------------------------------------------
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
toLists (head (calendario 6)) `shouldBe`
Capítulo 13. Búsqueda en espacios de estados 1111
[[2,3,4,5,6],[1,4,5,6,3],[5,1,6,4,2],[6,2,1,3,5],[3,6,2,1,4],[4,5,3,2,1]]
it ”e2” $
length (calendario 6) `shouldBe` 720
it ”e3” $
length (calendario 5) `shouldBe` 0
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.2580 seconds
-- 3 examples, 0 failures
13.14.2. En Python
# ---------------------------------------------------------------------
# El problema del calendario, para una competición deportiva en la que
# se enfrentan n participantes, consiste en elaborar un calendario de
# forma que:
# + el campeonato dure n-1 días,
# + cada participante juegue exactamente un partido diario y
# + cada participante juegue exactamente una vez con cada adversario.
# Por ejemplo, con 8 participantes una posible solución es
# | 1 2 3 4 5 6 7
# --+--------------
# 1 | 2 3 4 5 6 7 8
# 2 | 1 4 3 6 5 8 7
# 3 | 4 1 2 7 8 5 6
# 4 | 3 2 1 8 7 6 5
# 5 | 6 7 8 1 2 3 4
# 6 | 5 8 7 2 1 4 3
# 7 | 8 5 6 3 4 1 2
# 8 | 7 6 5 4 3 2 1
# donde las filas indican los jugadores y las columnas los días; es
# decir, el elemento (i,j) indica el adversario del jugador i el día j;
# por ejemplo, el adversario del jugador 2 el 4ª día es el jugador 6.
#
1112 Ejercicios de programación con Python
import numpy as np
import numpy.typing as npt
Calendario = npt.NDArray[np.int_]
# Verificación
# ============
assert filas(calendario(6)[0]) == \
[[6, 5, 4, 3, 2],
[5, 4, 3, 6, 1],
[4, 6, 2, 1, 5],
[3, 2, 1, 5, 6],
[2, 1, 6, 4, 3],
[1, 3, 5, 2, 4]]
assert len(calendario(6)) == 720
assert len(calendario(5)) == 0
print(”Verificado”)
-- ---------------------------------------------------------------------
-- Un problema está definido por la lista de fichas que hay que colocar
type Problema = [Ficha]
-- Los estados son los pares formados por la listas sin colocar y las
-- colocadas.
type Estado = ([Ficha],[Ficha])
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
domino [(1,2),(2,3),(1,4)] `shouldBe`
[[(4,1),(1,2),(2,3)],[(3,2),(2,1),(1,4)]]
it ”e2” $
domino [(1,2),(1,1),(1,4)] `shouldBe`
[[(4,1),(1,1),(1,2)],[(2,1),(1,1),(1,4)]]
it ”e3” $
domino [(1,2),(3,4),(2,3)] `shouldBe`
[[(1,2),(2,3),(3,4)],[(4,3),(3,2),(2,1)]]
1118 Ejercicios de programación con Python
it ”e4” $
domino [(1,2),(2,3),(5,4)] `shouldBe`
[]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0013 seconds
-- 4 examples, 0 failures
13.15.2. En Python
# ---------------------------------------------------------------------
# Las fichas del dominó se pueden representar por pares de números
# enteros. El problema del dominó consiste en colocar todas las fichas
# de una lista dada de forma que el segundo número de cada ficha
# coincida con el primero de la siguiente.
#
# Definir, mediante búsqueda en espacio de estados, la función
# domino : (Problema) -> list[list[Ficha]]
# tal que domino(fs) es la lista de las soluciones del problema del
# dominó correspondiente a las fichas fs. Por ejemplo,
# >>> domino([(1,2),(2,3),(1,4)])
# [[(3, 2), (2, 1), (1, 4)], [(4, 1), (1, 2), (2, 3)]]
# >>> domino([(1,2),(1,1),(1,4)])
# [[(2, 1), (1, 1), (1, 4)], [(4, 1), (1, 1), (1, 2)]]
# >>> domino([(1,2),(3,4),(2,3)])
# [[(4, 3), (3, 2), (2, 1)], [(1, 2), (2, 3), (3, 4)]]
# >>> domino([(1,2),(2,3),(5,4)])
# []
# ---------------------------------------------------------------------
# Un problema está definido por la lista de fichas que hay que colocar
Problema = list[Ficha]
# Los estados son los pares formados por la listas sin colocar y las
# colocadas.
Estado = tuple[list[Ficha], list[Ficha]]
# >>> sucesores(([(2,3),(1,4)],[(2,1)]))
# [([(1,4)],[(3,2),(2,1)])]
def sucesores(e: Estado) -> list[Estado]:
if not e[1]:
return [(elimina((a,b), e[0]), [(a,b)]) for (a,b) in e[0] if a != b] + \
[(elimina((a,b), e[0]), [(b,a)]) for (a,b) in e[0]]
return [(elimina((u,v),e[0]),[(u,v)]+e[1]) for (u,v) in e[0] if u != v and v
[(elimina((u,v),e[0]),[(v,u)]+e[1]) for (u,v) in e[0] if u != v and u
[(elimina((u,v),e[0]),[(u,v)]+e[1]) for (u,v) in e[0] if u == v and u
# # Verificación
# # ============
# La verificación es
# >>> test_domino()
# Verificado
Capítulo 13. Búsqueda en espacios de estados 1121
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
suma0 [-7,-3,-2,5,8] `shouldBe`
[[-3,-2,5]]
it ”e2” $
suma0 [-7,-3,-2,5,8,-1] `shouldBe`
[[-7,-3,-2,-1,5,8],[-7,-1,8],[-3,-2,5]]
it ”e3” $
suma0 [-7,-3,1,5,8] `shouldBe`
[]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0098 seconds
-- 3 examples, 0 failures
Capítulo 13. Búsqueda en espacios de estados 1123
13.16.2. En Python
# ---------------------------------------------------------------------
# El problema de suma cero consiste en, dado el conjunto de números
# enteros, encontrar sus subconjuntos no vacío cuyos elementos sumen
# cero.
#
# Usando el [procedimiento de búsqueda en profundidad](https://bit.ly/3NPI4qV),
# definir la función
# suma0 : (list[int]) -> list[list[int]]
# tal que suma0(ns) es la lista de las soluciones del problema de suma
# cero para ns. Por ejemplo,
# λ> suma0([-7,-3,-2,5,8])
# [[-3,-2,5]]
# λ> suma0([-7,-3,-2,5,8,-1])
# [[-7,-3,-2,-1,5,8],[-7,-1,8],[-3,-2,5]]
# λ> suma0([-7,-3,1,5,8])
# []
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_suma0()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
take 3 (jarras (4,3,2)) `shouldBe`
[[(0,0),(0,3),(3,0),(3,3),(4,2),(0,2),(2,0)],
[(0,0),(4,0),(1,3),(1,0),(0,1),(4,1),(2,3)],
[(0,0),(0,3),(3,0),(4,0),(1,3),(1,0),(0,1),(4,1),(2,3)]]
it ”e2” $
length (jarras (15,10,5)) `shouldBe` 8
it ”e3” $
map length (jarras (15,10,5)) `shouldBe`
[3,5,5,7,7,7,8,9]
it ”e4” $
jarras (15,10,4) `shouldBe` []
1128 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
--
-- Finished in 0.0080 seconds
-- 4 examples, 0 failures
13.17.2. En Python
# ---------------------------------------------------------------------
# En el problema de las jarras (A,B,C) se tienen dos jarras sin marcas
# de medición, una de A litros de capacidad y otra de B. También se
# dispone de una bomba que permite llenar las jarras de agua.
#
# El problema de las jarras (A,B,C) consiste en determinar cómo se
# puede lograr tener exactamente C litros de agua en la jarra de A
# litros de capacidad.
#
# Usando el [procedimiento de búsqueda en anchura](https://bit.ly/3XBlqG7),
# definir la función
# jarras: tuple[int,int,int] -> list[list[tuple[int,int]]]
# tal jarras((a,b,c)) es la lista de las soluciones del problema de las
# jarras (a,b,c). Por ejemplo,
# >>> jarras((4,3,2))[:3]
# [[(0, 0), (4, 0), (1, 3), (1, 0), (0, 1), (4, 1), (2, 3)],
# [(0, 0), (0, 3), (3, 0), (3, 3), (4, 2), (0, 2), (2, 0)],
# [(0, 0), (4, 0), (4, 3), (0, 3), (3, 0), (3, 3), (4, 2), (0, 2), (2, 0)]]
#
# La interpretación [(0,0),(4,0),(1,3),(1,0),(0,1),(4,1),(2,3)] es:
# (0,0) se inicia con las dos jarras vacías,
# (4,0) se llena la jarra de 4 con el grifo,
# (1,3) se llena la de 3 con la de 4,
# (1,0) se vacía la de 3,
# (0,1) se pasa el contenido de la primera a la segunda,
# (4,1) se llena la primera con el grifo,
# (2,3) se llena la segunda con la primera.
Capítulo 13. Búsqueda en espacios de estados 1129
#
# Otros ejemplos
# >>> len(jarras((15,10,5)))
# 8
# >>> [len(e) for e in jarras((15,10,5))]
# [3, 5, 5, 7, 7, 7, 8, 9]
# >>> jarras((15,10,4))
# []
# ---------------------------------------------------------------------
r.append((a, y))
if y < b:
r.append((x, b))
if x > 0:
r.append((0, y))
if y > 0:
r.append((x, 0))
if x < a and y > 0 and x + y > a:
r.append((a, y - (a - x)))
if x > 0 and y < b and x + y > b:
r.append((x - (b - y), b))
if y > 0 and x + y <= a:
r.append((x + y, 0))
if x > 0 and x + y <= b:
r.append((0, x + y))
return r
return [[c] + e
for c in sucesorasConfiguracion(p, e[0])
if c not in e]
# Verificación
# ============
# La verificación es
# >>> test_jarras()
# Verificado
1132 Ejercicios de programación con Python
Capítulo 14
Programación dinámica
Contenido
14.1. La función de Fibonacci . . . . . . . . . . . . . . . . . . . . .1110
.
14.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1110
.
14.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1112
.
14.2. Coeficientes binomiales . . . . . . . . . . . . . . . . . . . . .1115
.
14.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1115
.
14.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1117
.
14.3. Longitud de la subsecuencia común máxima . . . . . . . .1120
.
14.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
.
14.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1123
.
14.4. Subsecuencia común máxima . . . . . . . . . . . . . . . . .1127
.
14.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1127
.
14.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1130
.
14.5. La distancia Levenshtein (con programación dinámica) . .1133
.
14.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1133
.
14.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1137
.
14.6. Caminos en una retícula (con programación dinámica) . .1140
.
14.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1140
.
14.6.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1143
.
14.7. Caminos en una matriz (con programación dinámica) . . .1146
.
14.7.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
.
1133
1134 Ejercicios de programación con Python
import Data.Array
import Test.Hspec (Spec, hspec, it, shouldBe)
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> fib1 34
-- 5702887
-- (11.82 secs, 3,504,944,704 bytes)
-- λ> fib2 34
-- 5702887
-- (0.01 secs, 587,808 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
fib1 6 `shouldBe` 8
it ”e2” $
1136 Ejercicios de programación con Python
fib2 6 `shouldBe` 8
it ”e3” $
map fib1 [0..9] `shouldBe` map fib2 [0..9]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
--
-- Finished in 0.0007 seconds
-- 3 examples, 0 failures
-- (0.01 secs, 788,952 bytes)
14.1.2. En Python
# ---------------------------------------------------------------------
# Los primeros términos de la sucesión de Fibonacci son
# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, ...
#
# Escribir dos definiciones (una recursiva y otra con programación
# dinámica) de la función
# fib :: Integer -> Integer
# tal que (fib n) es el n-ésimo término de la sucesión de Fibonacci. Por
# ejemplo,
# fib(6) == 8
#
# Comparar la eficiencia de las dos definiciones.
# ---------------------------------------------------------------------
import numpy as np
import numpy.typing as npt
setrecursionlimit(10**6)
# =============================
v[1] = 1
for i in range(2, n + 1):
v[i] = v[i - 1] + v[i - 2]
return v
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('fib1(34)')
# 2.10 segundos
# >>> tiempo('fib2(34)')
# 0.00 segundos
# >>> tiempo('fib3(34)')
# 0.00 segundos
#
# >>> tiempo('fib2(100000)')
# 0.37 segundos
# >>> tiempo('fib3(100000)')
# 0.08 segundos
# Verificación
# ============
# La verificación es
# >>> test_fib()
# Verificado
Capítulo 14. Programación dinámica 1139
-- [[1],[1,1],[1,2,1],[1,3,3,1]]
matrizBinomial2 :: Integer -> Integer -> Array (Integer,Integer) Integer
matrizBinomial2 n k = q where
q = array ((0,0),(n,k)) [((i,j),f i j) | i <- [0..n], j <- [0..k]]
f _ 0 = 1
f i j
| i == j = 1
| otherwise = q!(i-1,j-1) + q!(i-1,j)
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> binomial1 25 12
-- 5200300
-- (6.45 secs, 2,654,797,776 bytes)
-- λ> binomial2 25 12
-- 5200300
-- (0.00 secs, 826,272 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
binomial1 6 3 `shouldBe` 20
it ”e2” $
binomial1 5 2 `shouldBe` 10
it ”e3” $
binomial1 5 3 `shouldBe` 10
it ”e4” $
binomial2 6 3 `shouldBe` 20
it ”e5” $
binomial2 5 2 `shouldBe` 10
it ”e6” $
binomial2 5 3 `shouldBe` 10
Capítulo 14. Programación dinámica 1141
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0006 seconds
-- 6 examples, 0 failures
14.2.2. En Python
# ---------------------------------------------------------------------
# El coeficiente binomial n sobre k es el número de subconjuntos de k
# elementos escogidos de un conjunto con n elementos.
#
# Definir la función
# binomial : (int, int) -> int
# tal que binomial(n, k) es el coeficiente binomial n sobre k. Por
# ejemplo,
# binomial(6, 3) == 20
# binomial(5, 2) == 10
# binomial(5, 3) == 10
# ---------------------------------------------------------------------
import numpy as np
import numpy.typing as npt
setrecursionlimit(10**6)
return q
return q
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('binomial1(27, 12)')
# 4.26 segundos
# >>> tiempo('binomial2(27, 12)')
# 0.00 segundos
# >>> tiempo('binomial3(27, 12)')
# 0.00 segundos
#
# >>> tiempo('binomial2(50000, 12)')
# 0.18 segundos
# >>> tiempo('binomial3(50000, 12)')
# 0.26 segundos
1144 Ejercicios de programación con Python
# Verificación
# ============
# La verificación es
# >>> test_binomial()
# Verificado
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> longitudSCM1 (take 18 (cycle [1,3])) (take 18 (cycle [2,3]))
-- 9
-- (13.90 secs, 8,883,660,048 bytes)
-- λ> longitudSCM2 (take 18 (cycle [1,3])) (take 18 (cycle [2,3]))
-- 9
Capítulo 14. Programación dinámica 1147
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
longitudSCM1 ”amapola” ”matamoscas” `shouldBe` 4
it ”e2” $
longitudSCM1 ”atamos” ”matamoscas” `shouldBe` 6
it ”e3” $
longitudSCM1 ”aaa” ”bbbb” `shouldBe` 0
it ”e4” $
longitudSCM2 ”amapola” ”matamoscas” `shouldBe` 4
it ”e5” $
longitudSCM2 ”atamos” ”matamoscas” `shouldBe` 6
it ”e6” $
longitudSCM2 ”aaa” ”bbbb” `shouldBe` 0
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0025 seconds
-- 6 examples, 0 failures
14.3.2. En Python
# ---------------------------------------------------------------------
# Si a una secuencia X de elementos (pongamos por ejemplo, caracteres)
1148 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('longitudSCM1([1,3]*9, [2,3]*9)')
# 8.04 segundos
# >>> tiempo('longitudSCM2([1,3]*9, [2,3]*9)')
# 0.00 segundos
# Verificación
# ============
# La verificación es
# >>> test_longitudSCM()
# Verificado
Capítulo 14. Programación dinámica 1151
scm1 [] _ = []
scm1 _ [] = []
scm1 (x:xs) (y:ys)
| x == y = x : scm1 xs ys
| otherwise = mayor (scm1 (x:xs) ys) (scm1 xs (y:ys))
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (scm1 (take 18 (cycle [1,3])) (take 18 (cycle [2,3])))
-- 9
-- (20.17 secs, 11,436,759,992 bytes)
-- λ> length (scm2 (take 18 (cycle [1,3])) (take 18 (cycle [2,3])))
-- 9
-- (0.00 secs, 1,013,624 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
scm1 ”amapola” ”matamoscas” `shouldBe` ”amoa”
it ”e2” $
scm1 ”atamos” ”matamoscas” `shouldBe` ”atamos”
it ”e3” $
1154 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
--
-- Finished in 0.0026 seconds
-- 6 examples, 0 failures
14.4.2. En Python
# ---------------------------------------------------------------------
# Si a una secuencia X de elementos (pongamos por ejemplo, caracteres)
# le quitamos algunos de ellos y dejamos los que quedan en el orden en
# el que aparecían originalmente tenemos lo que se llama una
# subsecuencia de X. Por ejemplo, ”aaoa” es una subsecuencia de la
# secuencia ”amapola”.
#
# El término también se aplica cuando quitamos todos los elementos (es
# decir, la secuencia vacía es siempre subsecuencia de cualquier
# secuencia) o cuando no quitamos ninguno (lo que significa que
# cualquier secuencia es siempre subsecuencia de sí misma).
#
# Dadas dos secuencias X e Y, decimos que Z es una subsecuencia común
# de X e Y si Z es subsecuencia de X y de Y. Por ejemplo, si X =
# ”amapola” e Y = ”matamoscas”, la secuencia ”aaoa” es una de las
# subsecuencias comunes de X e Y más larga, con longitud 4, ya que no
# hay ninguna subsecuencia común a X e Y de longitud mayor que
Capítulo 14. Programación dinámica 1155
setrecursionlimit(10**6)
n = len(xs)
m = len(ys)
return (matrizSCM2(xs, ys)[n][m])[::-1]
# # Comparación de eficiencia
Capítulo 14. Programación dinámica 1157
# # =========================
# La comparación es
# >>> tiempo('scm1([”1”,”3”]*9, [”2”,”3”]*9)')
# 8.44 segundos
# >>> tiempo('scm2([”1”,”3”]*9, [”2”,”3”]*9)')
# 0.00 segundos
# Verificación
# ============
# La verificación es
# >>> test_scm()
# Verificado
-- =========================================
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> levenshtein1 (show (2^33)) (show (3^33))
-- 12
-- (16.19 secs, 11,766,254,536 bytes)
1160 Ejercicios de programación con Python
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”ej1” $
levenshtein1 ”casa” ”calle” `shouldBe` 3
it ”ej2” $
levenshtein1 ”calle” ”casa” `shouldBe` 3
it ”ej3” $
levenshtein1 ”casa” ”casa” `shouldBe` 0
it ”ej4” $
levenshtein1 ”ana” ”maria” `shouldBe` 3
it ”ej5” $
levenshtein1 ”agua” ”manantial” `shouldBe` 7
it ”ej6” $
levenshtein2 ”casa” ”calle” `shouldBe` 3
it ”ej7” $
levenshtein2 ”calle” ”casa” `shouldBe` 3
it ”ej8” $
levenshtein2 ”casa” ”casa” `shouldBe` 0
it ”ej9” $
levenshtein2 ”ana” ”maria” `shouldBe` 3
it ”ej10” $
levenshtein2 ”agua” ”manantial” `shouldBe` 7
-- La verificación es
-- λ> verifica
--
-- ej1
-- ej2
-- ej3
-- ej4
Capítulo 14. Programación dinámica 1161
-- ej5
-- ej6
-- ej7
-- ej8
-- ej9
-- ej10
--
-- Finished in 0.0024 seconds
-- 10 examples, 0 failures
14.5.2. En Python
# ---------------------------------------------------------------------
# La distancia de Levenshtein (o distancia de edición) es el número
# mínimo de operaciones requeridas para transformar una cadena de
# caracteres en otra. Las operaciones de edición que se pueden hacer
# son:
# + insertar un carácter (por ejemplo, de ”abc” a ”abca”)
# + eliminar un carácter (por ejemplo, de ”abc” a ”ac”)
# + sustituir un carácter (por ejemplo, de ”abc” a ”adc”)
#
# Por ejemplo, la distancia de Levenshtein entre ”casa” y ”calle” es de
# 3 porque se necesitan al menos tres ediciones elementales para
# cambiar uno en el otro:
# ”casa” --> ”cala” (sustitución de 's' por 'l')
# ”cala” --> ”calla” (inserción de 'l' entre 'l' y 'a')
# ”calla” --> ”calle” (sustitución de 'a' por 'e')
#
# Definir la función
# levenshtein : (str, str) -> int
# tal que levenshtein(xs, ys) es la distancia de Levenshtein entre xs e
# ys. Por ejemplo,
# levenshtein(”casa”, ”calle”) == 3
# levenshtein(”calle”, ”casa”) == 3
# levenshtein(”casa”, ”casa”) == 0
# levenshtein(”ana”, ”maria”) == 3
# levenshtein(”agua”, ”manantial”) == 7
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('levenshtein1(str(2**33), str(3**33))')
# 13.78 segundos
# >>> tiempo('levenshtein2(str(2**33), str(3**33))')
# 0.00 segundos
# Verificación
# ============
-- [(1,1),(1,2),(1,3),(2,3),(3,3),(3,4)]
-- [(1,1),(1,2),(2,2),(2,3),(3,3),(3,4)]
-- [(1,1),(2,1),(2,2),(2,3),(3,3),(3,4)]
-- [(1,1),(1,2),(2,2),(3,2),(3,3),(3,4)]
-- [(1,1),(2,1),(2,2),(3,2),(3,3),(3,4)]
-- [(1,1),(2,1),(3,1),(3,2),(3,3),(3,4)]
-- ---------------------------------------------------------------------
-- Comparación de eficiencia
-- =========================
1166 Ejercicios de programación con Python
-- La comparación es
-- λ> maximum (head (caminos1 (2000,2000)))
-- (2000,2000)
-- (0.01 secs, 3,459,576 bytes)
-- λ> maximum (head (caminos2 (2000,2000)))
-- (2000,2000)
-- (2.79 secs, 1,507,636,688 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
caminos1 (2,3) `shouldBe`
[[(1,1),(1,2),(1,3),(2,3)],
[(1,1),(1,2),(2,2),(2,3)],
[(1,1),(2,1),(2,2),(2,3)]]
it ”e2” $
caminos2 (2,3) `shouldBe`
[[(1,1),(1,2),(1,3),(2,3)],
[(1,1),(1,2),(2,2),(2,3)],
[(1,1),(2,1),(2,2),(2,3)]]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0010 seconds
-- 2 examples, 0 failures
Capítulo 14. Programación dinámica 1167
14.6.2. En Python
# ---------------------------------------------------------------------
# Se considera una retícula con sus posiciones numeradas, desde el
# vértice superior izquierdo, hacia la derecha y hacia abajo. Por
# ejemplo, la retícula de dimensión 3x4 se numera como sigue:
# |-------+-------+-------+-------|
# | (1,1) | (1,2) | (1,3) | (1,4) |
# | (2,1) | (2,2) | (2,3) | (2,4) |
# | (3,1) | (3,2) | (3,3) | (3,4) |
# |-------+-------+-------+-------|
#
# Definir la función
# caminos : (tuple[int, int]) -> list[list[tuple[int, int]]]
# tal que caminos((m,n)) es la lista de los caminos en la retícula de
# dimensión mxn desde (1,1) hasta (m,n). Por ejemplo,
# >>> caminos((2,3))
# [[(1, 1), (1, 2), (1, 3), (2, 3)],
# [(1, 1), (1, 2), (2, 2), (2, 3)],
# [(1, 1), (2, 1), (2, 2), (2, 3)]]
# >>> for c in caminos1((3,4)):
# ... print(c)
# ...
# [(1, 1), (1, 2), (1, 3), (1, 4), (2, 4), (3, 4)]
# [(1, 1), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)]
# [(1, 1), (1, 2), (2, 2), (2, 3), (2, 4), (3, 4)]
# [(1, 1), (2, 1), (2, 2), (2, 3), (2, 4), (3, 4)]
# [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 4)]
# [(1, 1), (1, 2), (2, 2), (2, 3), (3, 3), (3, 4)]
# [(1, 1), (2, 1), (2, 2), (2, 3), (3, 3), (3, 4)]
# [(1, 1), (1, 2), (2, 2), (3, 2), (3, 3), (3, 4)]
# [(1, 1), (2, 1), (2, 2), (3, 2), (3, 3), (3, 4)]
# [(1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4)]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
1168 Ejercicios de programación con Python
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('max(caminos1((13,13))[0])')
# 26.75 segundos
# >>> tiempo('max(caminos2((13,13))[0])')
# 7.40 segundos
# Verificación
# ============
# La verificación es
# >>> test_caminos()
# Verificado
1170 Ejercicios de programación con Python
-- [1,7, 3,8,4,9]]
-- λ> length (caminos (fromList 12 12 [1..]))
-- 705432
-- ---------------------------------------------------------------------
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (caminos1 (fromList 11 11 [1..]))
-- 184756
-- (3.64 secs, 667,727,568 bytes)
-- λ> length (caminos2 (fromList 11 11 [1..]))
-- 184756
-- (0.82 secs, 129,181,072 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
caminos1 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` r
it ”e2” $
caminos2 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` r
where r = [[1,6,11,2,8,9],
[1,6,11,3,8,9],
[1,6,12,3,8,9],
[1,7,12,3,8,9],
[1,6,11,3,4,9],
[1,6,12,3,4,9],
[1,7,12,3,4,9],
[1,6,12,8,4,9],
[1,7,12,8,4,9],
[1,7, 3,8,4,9]]
-- La verificación es
-- λ> verifica
Capítulo 14. Programación dinámica 1173
--
-- e1
-- e2
--
-- Finished in 0.0010 seconds
-- 2 examples, 0 failures
14.7.2. En Python
# ---------------------------------------------------------------------
# Los caminos desde el extremo superior izquierdo (posición (1,1))
# hasta el extremo inferior derecho (posición (3,4)) en la matriz
# ( 1 6 11 2 )
# ( 7 12 3 8 )
# ( 3 8 4 9 )
# moviéndose en cada paso una casilla hacia la derecha o abajo, son los
# siguientes:
# [1,6,11,2,8,9]
# [1,6,11,3,8,9]
# [1,6,12,3,8,9]
# [1,7,12,3,8,9]
# [1,6,11,3,4,9]
# [1,6,12,3,4,9]
# [1,7,12,3,4,9]
# [1,6,12,8,4,9]
# [1,7,12,8,4,9]
# [1,7, 3,8,4,9]
#
# Definir la función
# caminos : (list[list[int]]) -> list[list[int]]
# tal que caminos (m) es la lista de los caminos en la matriz m desde
# el extremo superior izquierdo hasta el extremo inferior derecho,
# moviéndose en cada paso una casilla hacia abajo o hacia la
# derecha. Por ejemplo,
# >>> caminos([[1,6,11,2],[7,12,3,8],[3,8,4,9]])
# [[1, 6, 11, 2, 8, 9],
# [1, 6, 11, 3, 8, 9],
# [1, 6, 12, 3, 8, 9],
# [1, 7, 12, 3, 8, 9],
# [1, 6, 11, 3, 4, 9],
1174 Ejercicios de programación con Python
setrecursionlimit(10**6)
m = len(p)
n = len(p[0])
return [list(reversed(xs)) for xs in diccionarioCaminos(p)[(m, n)]]
return q
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('caminos1([list(range(11*n+1, 11*(n+1)+1)) for n in range(12)])')
# 2.20 segundos
# >>> tiempo('caminos2([list(range(11*n+1, 11*(n+1)+1)) for n in range(12)])')
# 0.64 segundos
# Verificación
# ============
# La verificación es
# >>> test_caminos()
# Verificado
Capítulo 14. Programación dinámica 1177
-- Comparación de eficiencia
-- =========================
1180 Ejercicios de programación con Python
-- La comparación es
-- λ> maximaSuma1 (fromList 11 11 [1..])
-- 1781
-- (3.88 secs, 1,525,812,680 bytes)
-- λ> maximaSuma2 (fromList 11 11 [1..])
-- 1781
-- (1.08 secs, 546,144,264 bytes)
-- λ> maximaSuma3 (fromList 11 11 [1..])
-- 1781
-- (0.55 secs, 217,712,280 bytes)
-- λ> maximaSuma4 (fromList 11 11 [1..])
-- 1781
-- (0.01 secs, 643,832 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
maximaSuma1 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` 41
it ”e2” $
maximaSuma1 (fromList 4 4 [1..]) `shouldBe` 73
it ”e3” $
maximaSuma2 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` 41
it ”e4” $
maximaSuma2 (fromList 4 4 [1..]) `shouldBe` 73
it ”e5” $
maximaSuma3 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` 41
it ”e6” $
maximaSuma3 (fromList 4 4 [1..]) `shouldBe` 73
it ”e7” $
maximaSuma4 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]]) `shouldBe` 41
it ”e8” $
maximaSuma4 (fromList 4 4 [1..]) `shouldBe` 73
-- La verificación es
Capítulo 14. Programación dinámica 1181
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- e7
-- e8
--
-- Finished in 0.0034 seconds
-- 8 examples, 0 failures
14.8.2. En Python
# ---------------------------------------------------------------------
# Los caminos desde el extremo superior izquierdo (posición (1,1))
# hasta el extremo inferior derecho (posición (3,4)) en la matriz
# ( 1 6 11 2 )
# ( 7 12 3 8 )
# ( 3 8 4 9 )
# moviéndose en cada paso una casilla hacia la derecha o hacia abajo,
# son los siguientes:
# [1,6,11,2,8,9]
# [1,6,11,3,8,9]
# [1,6,12,3,8,9]
# [1,7,12,3,8,9]
# [1,6,11,3,4,9]
# [1,6,12,3,4,9]
# [1,7,12,3,4,9]
# [1,6,12,8,4,9]
# [1,7,12,8,4,9]
# [1,7, 3,8,4,9]
# La suma de los caminos son 37, 38, 39, 40, 34, 35, 36, 40, 41 y 32,
# respectivamente. El camino de máxima suma es el penúltimo (1, 7, 12, 8,
# 4, 9) que tiene una suma de 41.
#
# Definir la función
# maximaSuma : (list[list[int]]) -> int
1182 Ejercicios de programación con Python
setrecursionlimit(10**6)
# (3, 1): 11, (3, 2): 28, (3, 3): 32, (3, 4): 41})
def diccionarioMaxSuma(p: list[list[int]]) -> dict[tuple[int, int], int]:
m = len(p)
n = len(p[0])
q: dict[tuple[int, int], int] = defaultdict(int)
for i in range(1, m + 1):
for j in range(1, n + 1):
if i == 1:
q[(i, j)] = q[(1,j-1)] + p[0][j-1]
elif j == 1:
q[(i, j)] = q[(i-1,1)] + p[i-1][0]
else:
q[(i, j)] = max(q[(i,j-1)], q[(i-1,j)]) + p[i-1][j-1]
return q
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('maximaSuma1([list(range(12*n+1, 12*(n+1)+1)) for n in range(12)]
# 4.95 segundos
# >>> tiempo('maximaSuma2([list(range(12*n+1, 12*(n+1)+1)) for n in range(12)]
# 1.49 segundos
# >>> tiempo('maximaSuma3([list(range(12*n+1, 12*(n+1)+1)) for n in range(12)]
# 0.85 segundos
# >>> tiempo('maximaSuma4([list(range(12*n+1, 12*(n+1)+1)) for n in range(12)]
# 0.00 segundos
# Verificación
# ============
assert maximaSuma4([[1,6,11,2],[7,12,3,8],[3,8,4,9]]) == 41
print(”Verificado”)
# La verificación es
# >>> test_maximaSuma()
# Verificado
-- [1,7,12,8,4,9]
-- λ> sum (caminoMaxSuma (fromList 500 500 [1..]))
-- 187001249
-- ---------------------------------------------------------------------
(k2,ys) = q!(i-1,j)
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (caminoMaxSuma1 (fromList 11 11 [1..]))
-- 21
-- (3.92 secs, 1,778,557,904 bytes)
-- λ> length (caminoMaxSuma2 (fromList 11 11 [1..]))
-- 21
-- (1.16 secs, 798,889,488 bytes)
-- λ> length (caminoMaxSuma3 (fromList 11 11 [1..]))
-- 21
-- (0.00 secs, 680,256 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
caminoMaxSuma1 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]])
`shouldBe` [1,7,12,8,4,9]
it ”e2” $
caminoMaxSuma2 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]])
`shouldBe` [1,7,12,8,4,9]
it ”e3” $
caminoMaxSuma3 (fromLists [[1,6,11,2],[7,12,3,8],[3,8,4,9]])
`shouldBe` [1,7,12,8,4,9]
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
Capítulo 14. Programación dinámica 1189
--
-- Finished in 0.0007 seconds
-- 3 examples, 0 failures
14.9.2. En Python
# ---------------------------------------------------------------------
# Los caminos desde el extremo superior izquierdo (posición (1,1))
# hasta el extremo inferior derecho (posición (3,4)) en la matriz
# ( 1 6 11 2 )
# ( 7 12 3 8 )
# ( 3 8 4 9 )
# moviéndose en cada paso una casilla hacia la derecha o hacia abajo,
# son los siguientes:
# [1,6,11,2,8,9]
# [1,6,11,3,8,9]
# [1,6,12,3,8,9]
# [1,7,12,3,8,9]
# [1,6,11,3,4,9]
# [1,6,12,3,4,9]
# [1,7,12,3,4,9]
# [1,6,12,8,4,9]
# [1,7,12,8,4,9]
# [1,7, 3,8,4,9]
# La suma de los caminos son 37, 38, 39, 40, 34, 35, 36, 40, 41 y 32,
# respectivamente. El camino de máxima suma es el penúltimo (1, 7, 12, 8,
# 4, 9) que tiene una suma de 41.
#
# Definir la función
# caminoMaxSuma : (list[list[int]]) -> list[int]
# tal que caminoMaxSuma(m) es un camino de máxima suma en la matriz m
# desde el extremo superior izquierdo hasta el extremo inferior derecho,
# moviéndose en cada paso una casilla hacia abajo o hacia la
# derecha. Por ejemplo,
# >>> caminoMaxSuma1([[1,6,11,2],[7,12,3,8],[3,8,4,9]])
# [1, 7, 12, 8, 4, 9]
# >>> sum(caminoMaxSuma3([list(range(500*n+1, 500*(n+1)+1)) for n in range(500
# 187001249
# ---------------------------------------------------------------------
1190 Ejercicios de programación con Python
setrecursionlimit(10**6)
# Comparación de eficiencia
# =========================
1192 Ejercicios de programación con Python
# La comparación es
# >>> tiempo('caminoMaxSuma1([list(range(11*n+1, 11*(n+1)+1)) for n in range(1
# 1.92 segundos
# >>> tiempo('caminoMaxSuma2([list(range(11*n+1, 11*(n+1)+1)) for n in range(1
# 0.65 segundos
# >>> tiempo('caminoMaxSuma3([list(range(11*n+1, 11*(n+1)+1)) for n in range(1
# 0.00 segundos
# Verificación
# ============
# La verificación es
# >>> test_caminoMaxSuma()
# Verificado
Parte III
1193
Capítulo 15
Cálculo numérico
Contenido
15.1. Método de Herón para calcular la raíz cuadrada . . . . . .1172
.
15.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1172
.
15.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1174
.
15.2. Método de Newton para calcular raíces . . . . . . . . . . .1175
.
15.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1175
.
15.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1177
.
15.3. Funciones inversas por el método de Newton . . . . . . . .1179
.
15.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1179
.
15.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1181
.
15.4. Límites de sucesiones . . . . . . . . . . . . . . . . . . . . . .1182
.
15.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1182
.
15.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1183
.
15.5. Método de bisección para calcular ceros de una función .1185
.
15.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1185
.
15.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1187
.
15.6. Raíces enteras . . . . . . . . . . . . . . . . . . . . . . . . . .1189
.
15.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1189
.
15.6.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1193
.
15.7. Integración por el método de los rectángulos . . . . . . . .1196
.
15.7.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1196
.
15.7.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1199
.
1195
1196 Ejercicios de programación con Python
import Test.QuickCheck
import Test.Hspec (Spec, hspec, it, shouldBe)
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_raiz :: Positive Double -> Bool
prop_raiz (Positive x) =
raiz x ~= sqrt x &&
raiz2 x ~= sqrt x
where
a ~= b = abs (a-b) < 0.001
-- La comprobación es
-- λ> quickCheck prop_raiz
-- +++ OK, passed 100 tests.
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
raiz 9 `shouldBe` 3.000000001396984
it ”e2” $
raiz2 9 `shouldBe` 3.000000001396984
1198 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0008 seconds
-- 2 examples, 0 failures
15.1.2. En Python
# ---------------------------------------------------------------------
# El método de Herón para calcular la raíz cuadrada de un número se
# basa en las siguientes propiedades:
# + Si y es una aproximación de la raíz cuadrada de x, entonces
# (y+x/y)/2 es una aproximación mejor.
# + El límite de la sucesión definida por
# x_0 = 1
# x_{n+1} = (x_n+x/x_n)/2
# es la raíz cuadrada de x.
#
# Definir la función
# raiz : (float) -> float
# tal que raiz(x) es la raíz cuadrada de x calculada usando la
# propiedad anterior con una aproximación de 0.00001 y tomando como
# valor inicial 1. Por ejemplo,
# raiz(9) == 3.000000001396984
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return y
return raizAux(mejora(y))
return raizAux(1)
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_raiz()
# Verificado
-- x_{n+1} = x_n-f(x_n)/f'(x_n)
-- es un cero de f.
--
-- Definir la función
-- puntoCero :: (Double -> Double) -> Double
-- tal que (puntoCero f) es un cero de la función f calculado usando la
-- propiedad anterior. Por ejemplo,
-- puntoCero cos == 1.5707963267949576
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
puntoCero cos `shouldBe` 1.5707963267949576
it ”e2” $
puntoCero2 cos `shouldBe` 1.5707963267949576
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.0002 seconds
-- 2 examples, 0 failures
15.2.2. En Python
# ---------------------------------------------------------------------
# Los ceros de una función pueden calcularse mediante el método de
# Newton basándose en las siguientes propiedades:
# + Si b es una aproximación para el punto cero de f, entonces
# b-f(b)/f'(b) es una mejor aproximación.
# + el límite de la sucesión x_n definida por
# x_0 = 1
# x_{n+1} = x_n-f(x_n)/f'(x_n)
# es un cero de f.
#
# Definir la función
# puntoCero : (Callable[[float], float]) -> float
# tal que puntoCero(f) es un cero de la función f calculado usando la
# propiedad anterior. Por ejemplo,
# puntoCero(cos) == 1.5707963267949576
# ---------------------------------------------------------------------
1202 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
Capítulo 15. Cálculo numérico 1203
# ============
# La comprobación es
# >>> test_puntoCero()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
inversa (^2) 9 `shouldBe` 3.000000002941184
it ”e2” $
raizCuadrada 9 `shouldBe` 3.000000002941184
it ”e3” $
raizCubica 27 `shouldBe` 3.0000000000196048
it ”e4” $
arcoseno 1 `shouldBe` 1.5665489428306574
it ”e5” $
arcocoseno 0 `shouldBe` 1.5707963267949576
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
--
Capítulo 15. Cálculo numérico 1205
15.3.2. En Python
# ---------------------------------------------------------------------
# Definir, usando puntoCero, la función
# inversa : (Callable[[float],float], float) -> float
# tal que inversa(g, x) es el valor de la inversa de g en x. Por
# ejemplo,
# inversa(lambda x: x**2, 9) == 3.000000002941184
#
# Definir, usando inversa, las funciones raizCuadrada, raizCubica,
# arcoseno y arcocoseno que calculen la raíz cuadrada, la raíz cúbica,
# el arco seno y el arco coseno, respectivamente. Por ejemplo,
# raizCuadrada(9) == 3.000000002941184
# raizCubica(27) == 3.0000000000196048
# arcoseno(1) == 1.5665489428306574
# arcocoseno(0) == 1.5707963267949576
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_inversa()
# Verificado
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
limite (\n -> (2*n+1)/(n+5)) 0.001 `shouldBe` 1.9900110987791344
it ”e2” $
limite (\n -> (1+1/n)**n) 0.001 `shouldBe` 2.714072874546881
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
--
-- Finished in 0.1927 seconds
-- 2 examples, 0 failures
15.4.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# limite : (Callable[[float], float], float) -> float
# tal que limite(f, a) es el valor de f en el primer término x tal que,
# para todo y entre x+1 y x+100, el valor absoluto de la diferencia
# entre f(y) y f(x) es menor que a. Por ejemplo,
# limite(lambda n : (2*n+1)/(n+5), 0.001) == 1.9900110987791344
# limite(lambda n : (1+1/n)**n, 0.001) == 2.714072874546881
# ---------------------------------------------------------------------
# 1ª solución
1208 Ejercicios de programación con Python
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La comprobación es
# >>> test_limite()
# Verificado
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
biseccion :: (Double -> Double) -> Double -> Double -> Double -> Double
biseccion f a b e
| abs (f c) < e = c
| f a * f c < 0 = biseccion f a c e
| otherwise = biseccion f c b e
where c = (a+b)/2
-- 2ª solución
-- ===========
biseccion2 :: (Double -> Double) -> Double -> Double -> Double -> Double
biseccion2 f a b e = aux a b
where aux a' b' | abs (f c) < e = c
| f a' * f c < 0 = aux a' c
| otherwise = aux c b'
where c = (a'+b')/2
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
biseccion (\x -> x^2 - 3) 0 5 0.01 `shouldBe` 1.7333984375
it ”e2” $
biseccion (\x -> x^3 - x - 2) 0 4 0.01 `shouldBe` 1.521484375
it ”e3” $
biseccion cos 0 2 0.01 `shouldBe` 1.5625
Capítulo 15. Cálculo numérico 1211
it ”e4” $
biseccion (\x -> log (50-x) - 4) (-10) 3 0.01 `shouldBe` -5.125
it ”e5” $
biseccion2 (\x -> x^2 - 3) 0 5 0.01 `shouldBe` 1.7333984375
it ”e6” $
biseccion2 (\x -> x^3 - x - 2) 0 4 0.01 `shouldBe` 1.521484375
it ”e7” $
biseccion2 cos 0 2 0.01 `shouldBe` 1.5625
it ”e8” $
biseccion2 (\x -> log (50-x) - 4) (-10) 3 0.01 `shouldBe` -5.125
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- e7
-- e8
--
-- Finished in 0.0008 seconds
-- 8 examples, 0 failures
15.5.2. En Python
# ---------------------------------------------------------------------
# El método de bisección para calcular un cero de una función en el
# intervalo [a,b] se basa en el teorema de Bolzano:
# ”Si f(x) es una función continua en el intervalo [a, b], y si,
# además, en los extremos del intervalo la función f(x) toma valores
# de signo opuesto (f(a) * f(b) < 0), entonces existe al menos un
# valor c en (a, b) para el que f(c) = 0”.
#
# El método para calcular un cero de la función f en el intervalo [a,b]
# con un error menor que e consiste en tomar el punto medio del
# intervalo c = (a+b)/2 y considerar los siguientes casos:
# + Si |f(c)| < e, hemos encontrado una aproximación del punto que
1212 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
# La comprobación es
# >>> test_biseccion()
# Verificado
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comparación de eficiencia
-- =========================
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_raizEnt :: Integer -> Bool
prop_raizEnt n =
raizEnt3 (10^(2*m)) 2 == 10^m
where m = abs n
-- La comprobación es
-- λ> quickCheck prop_raizEnt
-- +++ OK, passed 100 tests.
-- Verificación
-- ============
verifica :: IO ()
1216 Ejercicios de programación con Python
spec :: Spec
spec = do
it ”e1” $
raizEnt1 8 3 `shouldBe` 2
it ”e2” $
raizEnt1 9 3 `shouldBe` 2
it ”e3” $
raizEnt1 26 3 `shouldBe` 2
it ”e4” $
raizEnt1 27 3 `shouldBe` 3
it ”e5” $
raizEnt2 8 3 `shouldBe` 2
it ”e6” $
raizEnt2 9 3 `shouldBe` 2
it ”e7” $
raizEnt2 26 3 `shouldBe` 2
it ”e8” $
raizEnt2 27 3 `shouldBe` 3
it ”e9” $
raizEnt3 8 3 `shouldBe` 2
it ”e10” $
raizEnt3 9 3 `shouldBe` 2
it ”e11” $
raizEnt3 26 3 `shouldBe` 2
it ”e12” $
raizEnt3 27 3 `shouldBe` 3
-- La verificación es
-- λ> verifica
--
-- e1
-- e2
-- e3
-- e4
-- e5
-- e6
-- e7
-- e8
Capítulo 15. Cálculo numérico 1217
-- e9
-- e10
-- e11
-- e12
--
-- Finished in 0.0007 seconds
-- 12 examples, 0 failures
15.6.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# raizEnt : (int, int) -> int
# tal que raizEnt(x, n) es la raíz entera n-ésima de x; es decir, el
# mayor número entero y tal que y^n <= x. Por ejemplo,
# raizEnt(8, 3) == 2
# raizEnt(9, 3) == 2
# raizEnt(26, 3) == 2
# raizEnt(27, 3) == 3
# raizEnt(10**50, 2) == 10000000000000000000000000
#
# Comprobar con Hypothesis que para todo número natural n,
# raizEnt(10**(2*n), 2) == 10**n
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('raizEnt(10**14, 2)')
# 2.71 segundos
# >>> tiempo('raizEnt2(10**14, 2)')
# 0.00 segundos
Capítulo 15. Cálculo numérico 1219
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=1000))
def test_raizEntP(n: int) -> None:
assert raizEnt3(10**(2*n), 2) == 10**n
# La comprobación es
# >>> test_raizEntP()
# >>>
# Verificación
# ============
# La comprobación es
# >>> test_raizEnt()
1220 Ejercicios de programación con Python
# Verificado
-- 1ª solución
-- ===========
-- (suma a b s f) es l valor de
-- f(a) + f(s(a)) + f(s(s(a)) + ... + f(s(...(s(a))...))
Capítulo 15. Cálculo numérico 1221
-- (sucesion x y s) es la lista
-- [a, s(a), s(s(a), ..., s(...(s(a))...)]
-- hasta que s(s(...(s(a))...)) > b. Por ejemplo,
-- sucesion 3 20 (+2) == [3,5,7,9,11,13,15,17,19]
sucesion :: Ord a => a -> a -> (a -> a) -> [a]
sucesion a b s = takeWhile (<=b) (iterate s a)
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Comparación de eficiencia
-- λ> integral 0 10 (^3) 0.00001
-- 2499.9999998811422
-- (4.62 secs, 1084774336 bytes)
-- λ> integral2 0 10 (^3) 0.00001
-- 2499.999999881125
-- (7.90 secs, 1833360768 bytes)
-- λ> integral3 0 10 (^3) 0.00001
-- 2499.999999881125
-- (7.27 secs, 1686056080 bytes)
-- Verificación
1222 Ejercicios de programación con Python
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
integral' 0 1 (^(3::Int)) 0.01 `shouldBe` 0.24998750000000042
it ”e2” $
integral' 0 1 (^(4::Int)) 0.01 `shouldBe` 0.19998333362500048
it ”e3” $
integral' 0 1 (\x -> 3*x^(2::Int) + 4*x^(3::Int)) 0.01 `shouldBe` 1.999925000
it ”e4” $
log 2 - integral' 1 2 (1 /) 0.01 `shouldBe` 3.124931644782336e-6
it ”e5” $
pi - 4 * integral' 0 1 (\x -> 1/(x^(2::Int)+1)) 0.01 `shouldBe` -8.3333333313
it ”e1b” $
integral2' 0 1 (^(3::Int)) 0.01 `shouldSatisfy` (~= 0.24998750000000042)
it ”e2b” $
integral2' 0 1 (^(4::Int)) 0.01 `shouldSatisfy` (~= 0.19998333362500048)
it ”e3b” $
integral2' 0 1 (\x -> 3*x^(2::Int) + 4*x^(3::Int)) 0.01 `shouldSatisfy` (~= 1
it ”e4b” $
log 2 - integral2' 1 2 (1 /) 0.01 `shouldSatisfy` (~= 3.124931644782336e-6)
it ”e5b” $
pi - 4 * integral2' 0 1 (\x -> 1/(x^(2::Int)+1)) 0.01 `shouldSatisfy` (~= (-8
it ”e1c” $
integral3' 0 1 (^(3::Int)) 0.01 `shouldSatisfy` (~= 0.24998750000000042)
it ”e2c” $
integral3' 0 1 (^(4::Int)) 0.01 `shouldSatisfy` (~= 0.19998333362500048)
it ”e3c” $
integral3' 0 1 (\x -> 3*x^(2::Int) + 4*x^(3::Int)) 0.01 `shouldSatisfy` (~= 1
it ”e4c” $
log 2 - integral3' 1 2 (1 /) 0.01 `shouldSatisfy` (~= 3.124931644782336e-6)
it ”e5c” $
pi - 4 * integral3' 0 1 (\x -> 1/(x^(2::Int)+1)) 0.01 `shouldSatisfy` (~= (-8
where
integral', integral2', integral3' :: Double -> Double -> (Double -> Double) -
integral' = integral
Capítulo 15. Cálculo numérico 1223
integral2' = integral2
integral3' = integral3
a ~= b = abs (a - b) < 0.00001
-- La verificación es
-- λ> verifica
--
-- Finished in 0.0058 seconds
-- 15 examples, 0 failures
15.7.2. En Python
# ---------------------------------------------------------------------
# La integral definida de una función f entre los límites a y b puede
# calcularse mediante la regla del rectángulo (ver en
# http://bit.ly/1FDhZ1z) usando la fórmula
# h * (f(a+h/2) + f(a+h+h/2) + f(a+2h+h/2) + ... + f(a+n*h+h/2))
# con a+n*h+h/2 <= b < a+(n+1)*h+h/2 y usando valores pequeños para h.
#
# Definir la función
# integral : (float, float, Callable[[float], float], float) -> float
# tal que integral(a, b, f, h) es el valor de dicha expresión. Por
# ejemplo, el cálculo de la integral de f(x) = x^3 entre 0 y 1, con
# paso 0.01, es
# integral(0, 1, lambda x : x**3, 0.01) == 0.24998750000000042
# Otros ejemplos son
# integral(0, 1, lambda x : x**4, 0.01) == 0.19998333362500054
# integral(0, 1, lambda x : 3*x**2 + 4*x**3, 0.01) == 1.9999250000000026
# log(2) - integral(1, 2, lambda x : 1/x, 0.01) == 3.124931644782336e-6
# pi - 4 * integral(0, 1, lambda x : 1/(x**2+1), 0.01) == -8.333333331389525e-
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# sucesion(x, y, s) es la lista
# [a, s(a), s(s(a), ..., s(...(s(a))...)]
1224 Ejercicios de programación con Python
# suma(a, b, s, f) es el valor de
# f(a) + f(s(a)) + f(s(s(a)) + ... + f(s(...(s(a))...))
# hasta que s(s(...(s(a))...)) > b. Por ejemplo,
# suma(2, 5, lambda x: x+1, lambda x: x**3) == 224
def suma(a: float,
b: float,
s: Callable[[float], float],
f: Callable[[float], float]) -> float:
return sum(f(x) for x in sucesion(a, b, s))
# 2ª solución
# ===========
# 3ª solución
# ===========
b: float,
f: Callable[[float], float],
h: float) -> float:
def aux(x: float) -> float:
if x+h/2 > b:
return 0
return h * f(x+h/2) + aux(x+h)
return aux(a)
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_integral()
# Verificado
-- ( 0.0 )
-- Es decir, la solución del sistema
-- 2x = 3
-- 3x + y = 6.5
-- 4x + 2y + 5 z = 10
-- es x=1.5, y=2 y z=0.
-- ---------------------------------------------------------------------
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”ej1” $
toLists (bajada a b) `shouldBe` [[1.5],[2.0],[0.0]]
where
a = fromLists [[2,0,0],[3,1,0],[4,2,5.0]]
b = fromLists [[3],[6.5],[10]]
-- La verificación es
-- λ> verifica
--
-- Finished in 0.0007 seconds
-- 1 example, 0 failures
1228 Ejercicios de programación con Python
15.8.2. En Python
# ---------------------------------------------------------------------
# Un sistema de ecuaciones lineales Ax = b es triangular inferior si
# todos los elementos de la matriz A que están por encima de la
# diagonal principal son nulos; es decir, es de la forma
# a(1,1)*x(1) = b(1)
# a(2,1)*x(1) + a(2,2)*x(2) = b(2)
# a(3,1)*x(1) + a(3,2)*x(2) + a(3,3)*x(3) = b(3)
# ...
# a(n,1)*x(1) + a(n,2)*x(2) + a(n,3)*x(3) +...+ a(x,x)*x(n) = b(n)
#
# El sistema es compatible si, y sólo si, el producto de los elementos
# de la diagonal principal es distinto de cero. En este caso, la
# solución se puede calcular mediante el algoritmo de bajada:
# x(1) = b(1) / a(1,1)
# x(2) = (b(2) - a(2,1)*x(1)) / a(2,2)
# x(3) = (b(3) - a(3,1)*x(1) - a(3,2)*x(2)) / a(3,3)
# ...
# x(n) = (b(n) - a(n,1)*x(1) - a(n,2)*x(2) -...- a(n,n-1)*x(n-1)) / a(n,n)
#
# Definir la función
# bajada : (list[list[float]], list[list[float]]) -> list[list[float]]
# tal que bajada(a, b) es la solución, mediante el algoritmo de bajada,
# del sistema compatible triangular superior ax = b. Por ejemplo,
# >>> bajada([[2,0,0],[3,1,0],[4,2,5.0]], [[3],[6.5],[10]])
# [[1.5], [2.0], [0.0]]
# Es decir, la solución del sistema
# 2x = 3
# 3x + y = 6.5
# 4x + 2y + 5 z = 10
# es x=1.5, y=2 y z=0.
# ---------------------------------------------------------------------
# Verificación
Capítulo 15. Cálculo numérico 1229
# ============
# La verificación es
# >>> test_bajada()
# Verificado
1230 Ejercicios de programación con Python
Parte IV
Miscelánea
1231
Capítulo 16
Miscelánea
Contenido
16.1. Números de Pentanacci . . . . . . . . . . . . . . . . . . . . .1212
.
16.1.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1212
.
16.1.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1216
.
16.2. El teorema de Navidad de Fermat . . . . . . . . . . . . . . .1219
.
16.2.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1219
.
16.2.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1225
.
16.3. Números primos de Hilbert . . . . . . . . . . . . . . . . . . .1231
.
16.3.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1231
.
16.3.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1234
.
16.4. Factorizaciones de números de Hilbert . . . . . . . . . . . .1237
.
16.4.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1237
.
16.4.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1241
.
16.5. Sumas de dos primos . . . . . . . . . . . . . . . . . . . . . .1245
.
16.5.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1245
.
16.5.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1248
.
16.6. Representaciones de un número como suma de dos cua-
drados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1252
.
16.6.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1252
.
16.6.2.En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1257
.
16.7. La serie de Thue-Morse . . . . . . . . . . . . . . . . . . . . .1261
.
16.7.1.En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1261
.
1233
1234 Ejercicios de programación con Python
16.18.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1334
.
16.18.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1340
.
16.19. Producto de los elementos de la diagonal principal . . . .1343
.
16.19.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1343
.
16.19.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1346
.
16.20. Reconocimiento de potencias de 4 . . . . . . . . . . . . . .1348
.
16.20.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1348
.
16.20.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1352
.
16.21. Exponente en la factorización . . . . . . . . . . . . . . . . .1355
.
16.21.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1355
.
16.21.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1358
.
16.22. Mayor órbita de la sucesión de Collatz . . . . . . . . . . . .1360
.
16.22.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1360
.
16.22.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1364
.
16.23. Máximos locales . . . . . . . . . . . . . . . . . . . . . . . . .1369
.
16.23.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1369
.
16.23.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1371
.
16.24. Caminos en un triángulo . . . . . . . . . . . . . . . . . . . .1374
.
16.24.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1374
.
16.24.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1376
.
16.25. Máxima suma de caminos en un triángulo . . . . . . . . .1377
.
16.25.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1377
.
16.25.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1382
.
16.26. Números amigos . . . . . . . . . . . . . . . . . . . . . . . . .1385
.
16.26.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1385
.
16.26.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1391
.
16.27. Primos equidistantes . . . . . . . . . . . . . . . . . . . . . .1395
.
16.27.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1395
.
16.27.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1399
.
16.28. Numeración de ternas de naturales . . . . . . . . . . . . .1402
.
16.28.1.
En Haskell . . . . . . . . . . . . . . . . . . . . . . . . . . 1402
.
16.28.2.
En Python . . . . . . . . . . . . . . . . . . . . . . . . . . 1406
.
1236 Ejercicios de programación con Python
--
-- Una generalización de los anteriores son los números de Pentanacci
-- definidos por las siguientes ecuaciones
-- P(0) = 0
-- P(1) = 1
-- P(2) = 1
-- P(3) = 2
-- P(4) = 4
-- P(n) = P(n-1) + P(n-2) + P(n-3) + P(n-4) + P(n-5), si n > 4
-- Los primeros números de Pentanacci son
-- 0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525, ...
--
-- Definir las funciones
-- pentanacci :: Integer -> Integer
-- pentanaccis :: [Integer]
-- tales que
-- + (pentanacci n) es el n-ésimo número de Pentanacci. Por ejemplo,
-- λ> pentanacci 14
-- 3525
-- λ> pentanacci (10^5) `mod` 10^30
-- 482929150584077921552549215816
-- λ> length (show (pentanacci (10^5)))
-- 29357
-- + pentanaccis es la lista de los números de Pentanacci. Por ejemplo,
-- λ> take 15 pentanacci
-- [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
1238 Ejercicios de programación con Python
pentanaccis1 :: [Integer]
pentanaccis1 = [pentanacci1 n | n <- [0..]]
-- 2ª solución
-- ===========
pentanaccis2 :: [Integer]
pentanaccis2 =
0 : 1 : 1 : 2 : 4 : zipWith5 f (r 0) (r 1) (r 2) (r 3) (r 4)
where f a b c d e = a+b+c+d+e
r n = drop n pentanaccis2
-- 3ª solución
-- ===========
pentanaccis3 :: [Integer]
pentanaccis3 = p (0, 1, 1, 2, 4)
where p (a, b, c, d, e) = a : p (b, c, d, e, a + b + c + d + e)
-- 4ª solución
-- ===========
pentanaccis4 :: [Integer]
pentanaccis4 = 0: 1: 1: 2: 4: p pentanaccis4
where p (a:b:c:d:e:xs) = (a+b+c+d+e): p (b:c:d:e:xs)
Capítulo 16. Miscelánea 1239
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”ej1” $
take 15 pentanaccis1 `shouldBe`
[0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
it ”ej2” $
take 15 pentanaccis2 `shouldBe`
[0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
it ”ej3” $
take 15 pentanaccis3 `shouldBe`
[0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
it ”ej4” $
take 15 pentanaccis4 `shouldBe`
[0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
-- La verificación es
-- λ> verifica
--
-- 4 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_pentanaccis :: NonNegative Int -> Bool
prop_pentanaccis (NonNegative n) =
all (== pentanaccis1 !! n)
[pentanaccis1 !! n,
pentanaccis2 !! n,
pentanaccis3 !! n]
1240 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=25}) prop_pentanaccis
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> pentanacci1 25
-- 5976577
-- (4.64 secs, 1,865,626,496 bytes)
-- λ> pentanacci2 25
-- 5976577
-- (0.00 secs, 578,584 bytes)
--
-- λ> length (show (pentanacci2 (10^5)))
-- 29357
-- (1.16 secs, 2,543,272,136 bytes)
-- λ> length (show (pentanacci3 (10^5)))
-- 29357
-- (1.00 secs, 2,560,881,608 bytes)
-- λ> length (show (pentanacci4 (10^5)))
-- 29357
-- (1.03 secs, 2,592,078,744 bytes)
-- Referencias
-- ===========
16.1.2. En Python
# ---------------------------------------------------------------------
# Los números de Fibonacci se definen mediante las ecuaciones
# F(0) = 0
# F(1) = 1
# F(n) = F(n-1) + F(n-2), si n > 1
# Los primeros números de Fibonacci son
# 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, ...
Capítulo 16. Miscelánea 1241
#
# Una generalización de los anteriores son los números de Pentanacci
# definidos por las siguientes ecuaciones
# P(0) = 0
# P(1) = 1
# P(2) = 1
# P(3) = 2
# P(4) = 4
# P(n) = P(n-1) + P(n-2) + P(n-3) + P(n-4) + P(n-5), si n > 4
# Los primeros números de Pentanacci son
# 0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525, ...
#
# Definir las funciones
# pentanacci : (int) -> int
# pentanaccis : () -> Iterator[int]
# tales que
# + pentanacci(n) es el n-ésimo número de Pentanacci. Por ejemplo,
# >>> pentanacci(14)
# 3525
# >>> pentanacci(10**5) % 10**30
# 482929150584077921552549215816
# >>> len(str(pentanacci(10**5)))
# 29357
# + pentanaccis() genera los números de Pentanacci. Por ejemplo,
# >>> list(islice(pentanaccis(), 15))
# [0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525]
# ---------------------------------------------------------------------
set_int_max_str_digits(30000)
# 1ª solución
# ===========
return 0
if n == 1:
return 1
if n == 2:
return 1
if n == 3:
return 2
if n == 4:
return 4
return sum((pentanacci1(n-k) for k in range(1, 6)))
# 2ª solución
# ===========
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_pentanacci()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
def test_pentanacci_equiv() -> bool:
return list(islice(pentanaccis1(), 25)) == list(islice(pentanaccis2(), 25))
# La comprobación es
# >>> test_pentanacci_equiv()
# True
# Comparación de eficiencia
# =========================
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('pentanacci1(28)')
# 8.24 segundos
# >>> tiempo('pentanacci2(28)')
# 0.00 segundos
# Referencias
# ===========
-- 1ª definición de representaciones
-- =================================
-- 2ª definición de representaciones
-- =================================
raiz 0 = 0
raiz 1 = 1
raiz x = aux (0,x)
where aux (a,b) | d == x = c
| c == a = a
| d < x = aux (c,b)
| otherwise = aux (a,c)
where c = (a+b) `div` 2
d = c^2
-- 3ª definición de representaciones
-- =================================
-- 4ª definición de representaciones
-- =================================
| x > y = []
| otherwise = case compare (x*x + y*y) n of
LT -> aux (x + 1) y
EQ -> (x, y) : aux (x + 1) (y - 1)
GT -> aux x (y - 1)
-- La propiedad es
prop_representaciones_equiv :: Positive Integer -> Bool
prop_representaciones_equiv (Positive n) =
representaciones n == representaciones2 n &&
representaciones2 n == representaciones3 n &&
representaciones3 n == representaciones4 n
-- La comprobación es
-- λ> quickCheck prop_representaciones_equiv
-- +++ OK, passed 100 tests.
-- 6
-- (0.10 secs, 62,349,048 bytes)
-- λ> length (representaciones4 (10^10))
-- 6
-- (0.11 secs, 48,052,360 bytes)
--
-- λ> length (representaciones3 (4*10^12))
-- 7
-- (1.85 secs, 1,222,007,176 bytes)
-- λ> length (representaciones4 (4*10^12))
-- 7
-- (1.79 secs, 953,497,480 bytes)
-- Definición de primosImparesConRepresentacionUnica
-- =================================================
primosImparesConRepresentacionUnica :: [Integer]
primosImparesConRepresentacionUnica =
[x | x <- tail primes
, length (representaciones4 x) == 1]
-- Definición de primos4nM1
-- ========================
primos4nM1 :: [Integer]
primos4nM1 = [x | x <- primes
, x `mod` 4 == 1]
-- La propiedad es
prop_teoremaDeNavidadDeFermat :: Positive Int -> Bool
prop_teoremaDeNavidadDeFermat (Positive n) =
primosImparesConRepresentacionUnica !! n == primos4nM1 !! n
-- La comprobación es
-- λ> quickCheck prop_teoremaDeNavidadDeFermat
-- +++ OK, passed 100 tests.
Capítulo 16. Miscelánea 1249
-- ---------------------------------------------------------------------
-- § Verificación --
-- ---------------------------------------------------------------------
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
representaciones 20 `shouldBe` [(2,4)]
it ”e2” $
representaciones 25 `shouldBe` [(0,5),(3,4)]
it ”e3” $
representaciones 325 `shouldBe` [(1,18),(6,17),(10,15)]
it ”e4” $
take 20 primosImparesConRepresentacionUnica `shouldBe`
[5,13,17,29,37,41,53,61,73,89,97,101,109,113,137,149,157,173,181,193]
it ”e5” $
take 20 primos4nM1 `shouldBe`
[5,13,17,29,37,41,53,61,73,89,97,101,109,113,137,149,157,173,181,193]
-- La verificación es
-- λ> verifica
--
-- 5 examples, 0 failures
16.2.2. En Python
# ---------------------------------------------------------------------
# El 25 de diciembre de 1640, en una carta a Mersenne, Fermat demostró
# la conjetura de Girard: todo primo de la forma 4n+1 puede expresarse
# de manera única como suma de dos cuadrados. Por eso es conocido como
# el [teorema de Navidad de Fermat](http://bit.ly/2Roso1o).
#
# Definir las funciones
# representaciones : (int) -> list[tuple[int, int]]
# primosImparesConRepresentacionUnica : () -> Iterator[int]
# primos4nM1 : () -> Iterator[int]
# tales que
1250 Ejercicios de programación con Python
# 1ª definición de representaciones
# =================================
# 2ª definición de representaciones
# =================================
# esCuadrado(25) == True
# esCuadrado(26) == False
# esCuadrado(10**46) == True
# esCuadrado(10**47) == False
def esCuadrado(x: int) -> bool:
y = raiz(x)
return x == y * y
# 3ª definición de representaciones
# =================================
# Verificación
# ============
# La comprobación es
# >>> test_representaciones()
# Verificado
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_representaciones_equiv(x: int) -> None:
xs = representaciones(x)
assert representaciones2(x) == xs
assert representaciones3(x) == xs
# La comprobación es
# >>> test_representaciones_equiv()
# >>>
# La comparación es
# >>> tiempo('representaciones(5000)')
# 1.27 segundos
# >>> tiempo('representaciones2(5000)')
# 0.00 segundos
# >>> tiempo('representaciones3(5000)')
# 0.00 segundos
#
# >>> tiempo('len(representaciones2(10**12))')
# 11.54 segundos
# >>> tiempo('len(representaciones3(10**12))')
1254 Ejercicios de programación con Python
# 12.08 segundos
# Definición de primosImparesConRepresentacionUnica
# =================================================
# Verificación de primosImparesConRepresentacionUnica
# ===================================================
# La comprobación es
# >>> test_primosImparesConRepresentacionUnica()
# Verificado
# Definición de primos4nM1
# ========================
# La comprobación es
# >>> test_primos4nM1()
# Verificado
# Verificación de primos4nM1
# ==========================
Capítulo 16. Miscelánea 1255
# La propiedad es
@given(st.integers(min_value=1, max_value=300))
def test_teoremaDeNavidadDeFermat(n: int) -> None:
assert nth(primosImparesConRepresentacionUnica(), n) == nth(primos4nM1(), n)
# La comprobación es
# >>> test_teoremaDeNavidadDeFermat()
# >>>
-- tal que sus elementos son los primos de Hilbert. Por ejemplo,
-- take 15 primosH == [5,9,13,17,21,29,33,37,41,49,53,57,61,69,73]
-- primosH !! (3*10^4) == 313661
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
primosH1 :: [Integer]
primosH1 = [n | n <- tail numerosH,
divisoresH n == [1,n]]
-- 2ª solución
-- ===========
primosH2 :: [Integer]
primosH2 = filter esPrimoH (tail numerosH)
where esPrimoH n = all noDivideAn [5,9..m]
Capítulo 16. Miscelánea 1257
-- 3ª solución
-- ===========
primosH3 :: [Integer]
primosH3 = [ n | n <- numerosH, isPrime n || semiPrimoH n ]
where semiPrimoH n = length xs == 2 && all (\x -> (x-3) `mod` 4 == 0) xs
where xs = primeFactors n
-- Verificación --
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
take 15 primosH1 `shouldBe` [5,9,13,17,21,29,33,37,41,49,53,57,61,69,73]
it ”e2” $
take 15 primosH2 `shouldBe` [5,9,13,17,21,29,33,37,41,49,53,57,61,69,73]
it ”e3” $
take 15 primosH3 `shouldBe` [5,9,13,17,21,29,33,37,41,49,53,57,61,69,73]
-- La verificación es
-- λ> verifica
--
-- 3 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_primosH :: NonNegative Int -> Bool
1258 Ejercicios de programación con Python
prop_primosH (NonNegative n) =
all (== primosH1 !! n)
[primosH2 !! n,
primosH3 !! n]
-- La comprobación es
-- λ> quickCheck prop_primosH
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> primosH1 !! 2000
-- 16957
-- (2.16 secs, 752,085,752 bytes)
-- λ> primosH2 !! 2000
-- 16957
-- (0.03 secs, 19,771,008 bytes)
-- λ> primosH3 !! 2000
-- 16957
-- (0.07 secs, 152,029,168 bytes)
--
-- λ> primosH2 !! (3*10^4)
-- 313661
-- (1.44 secs, 989,761,888 bytes)
-- λ> primosH3 !! (3*10^4)
-- 313661
-- (2.06 secs, 6,554,068,992 bytes)
16.3.2. En Python
# ---------------------------------------------------------------------
# Un [número de Hilbert](http://bit.ly/204SW1p) es un entero positivo
# de la forma 4n+1. Los primeros números de Hilbert son 1, 5, 9, 13,
# 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81,
# 85, 89, 93, 97, ...
#
# Un primo de Hilbert es un número de Hilbert n que no es divisible
# por ningún número de Hilbert menor que n (salvo el 1). Los primeros
Capítulo 16. Miscelánea 1259
# primos de Hilbert son 5, 9, 13, 17, 21, 29, 33, 37, 41, 49, 53, 57,
# 61, 69, 73, 77, 89, 93, 97, 101, 109, 113, 121, 129, 133, 137, 141,
# 149, 157, 161, 173, 177, 181, 193, 197, ...
#
# Definir la sucesión
# primosH :: [Integer]
# tal que sus elementos son los primos de Hilbert. Por ejemplo,
# >>> list(islice(primosH1(), 15))
# [5, 9, 13, 17, 21, 29, 33, 37, 41, 49, 53, 57, 61, 69, 73]
# >>> nth(primosH1(), 5000)
# 45761
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación --
# ============
# La verificación es
# >>> test_primosH()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_primosH_equiv(n: int) -> None:
assert nth(primosH1(), n) == nth(primosH2(), n)
Capítulo 16. Miscelánea 1261
# La comprobación es
# >>> test_primosH_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('nth(primosH1(), 3000)')
# 2.51 segundos
# >>> tiempo('nth(primosH2(), 3000)')
# 0.05 segundos
--
-- Comprobar con QuickCheck que todos los números de Hilbert son
-- factorizables como producto de primos de Hilbert (aunque la
-- factorización, como para el 441, puede no ser única).
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Verificación --
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG factorizacionesH1
describe ”def. 2” $ specG factorizacionesH2
describe ”def. 3” $ specG factorizacionesH3
1264 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- 9 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_factorizacionesH :: Positive Integer -> Bool
prop_factorizacionesH (Positive n) =
all (== factorizacionesH1 m)
[factorizacionesH2 m,
factorizacionesH3 m]
where m = 1 + 4 * n
-- La comprobación es
-- λ> quickCheck prop_factorizacionesH
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> factorizacionesH1 80109
-- [[9,9,989],[9,69,129]]
-- (42.77 secs, 14,899,787,640 bytes)
-- λ> factorizacionesH2 80109
-- [[9,9,989],[9,69,129]]
-- (0.26 secs, 156,051,104 bytes)
-- λ> factorizacionesH3 80109
-- [[9,9,989],[9,69,129]]
-- (0.35 secs, 1,118,236,536 bytes)
-- Propiedad de factorización
-- ==========================
-- La propiedad es
prop_factorizable :: Positive Integer -> Bool
prop_factorizable (Positive n) =
Capítulo 16. Miscelánea 1265
-- La comprobación es
-- λ> quickCheck prop_factorizable
-- +++ OK, passed 100 tests.
-- ---------------------------------------------------------------------
-- § Referencias --
-- ---------------------------------------------------------------------
16.4.2. En Python
# ---------------------------------------------------------------------
# Un [**número de Hilbert**](http://bit.ly/204SW1p) es un entero
# positivo de la forma 4n+1. Los primeros números de Hilbert son 1, 5,
# 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, ...
#
# Un **primo de Hilbert** es un número de Hilbert n que no es divisible
# por ningún número de Hilbert menor que n (salvo el 1). Los primeros
# primos de Hilbert son 5, 9, 13, 17, 21, 29, 33, 37, 41, 49, 53, 57,
# 61, 69, 73, 77, 89, 93, 97, 101, 109, 113, 121, 129, 133, 137, ...
#
# Definir la función
# factorizacionesH : (int) -> list[list[int]]
# tal que factorizacionesH(n) es la listas de primos de Hilbert cuyo
# producto es el número de Hilbert n. Por ejemplo,
1266 Ejercicios de programación con Python
# factorizacionesH(25) == [[5,5]]
# factorizacionesH(45) == [[5,9]]
# factorizacionesH(441) == [[9,49],[21,21]]
# factorizacionesH(80109) == [[9,9,989],[9,69,129]]
#
# Comprobar con Hypothesis que todos los números de Hilbert son
# factorizables como producto de primos de Hilbert (aunque la
# factorización, como para el 441, puede no ser única).
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
Capítulo 16. Miscelánea 1267
# ===========
# Verificación
# ============
# La verificación es
# >>> test_factorizacionesH()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_factorizacionesH_equiv(n: int) -> None:
m = 1 + 4 * n
assert factorizacionesH1(m) == factorizacionesH2(m)
1268 Ejercicios de programación con Python
# La comprobación es
# >>> test_factorizacionesH_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('factorizacionesH1(80109)')
# 25.40 segundos
# >>> tiempo('factorizacionesH2(80109)')
# 0.27 segundos
# Propiedad de factorización
# ==========================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_factorizable(n: int) -> None:
assert factorizacionesH2(1 + 4 * n) != []
# La comprobación es
# >>> test_factorizable()
# >>>
# La comprobación es
# src> poetry run pytest -v Factorizaciones_de_numeros_de_Hilbert.py
# ===== test session starts =====
# test_factorizacionesH PASSED
# test_factorizacionesH_equiv PASSED
# test_factorizable PASSED
# ===== 3 passed in 2.25s =====
Capítulo 16. Miscelánea 1269
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
-- 1ª solución
-- ===========
sumasDeDosPrimos1 :: [Integer]
sumasDeDosPrimos1 =
[n | n <- [1..], not (null (sumaDeDosPrimos1 n))]
-- 2ª solución
-- ===========
sumasDeDosPrimos2 :: [Integer]
sumasDeDosPrimos2 =
[n | n <- [1..], not (null (sumaDeDosPrimos2 n))]
-- 3ª solución
-- ===========
sumasDeDosPrimos3 :: [Integer]
sumasDeDosPrimos3 = filter esSumaDeDosPrimos3 [4..]
Capítulo 16. Miscelánea 1271
-- 4ª solución
-- ===========
-- Usando la conjetura de Goldbach que dice que ”Todo número par mayor
-- que 2 puede escribirse como suma de dos números primos” .
sumasDeDosPrimos4 :: [Integer]
sumasDeDosPrimos4 = filter esSumaDeDosPrimos4 [4..]
-- Verificación --
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
1272 Ejercicios de programación con Python
spec = do
describe ”def. 1” $ specG sumasDeDosPrimos1
describe ”def. 2” $ specG sumasDeDosPrimos2
describe ”def. 3” $ specG sumasDeDosPrimos3
describe ”def. 4” $ specG sumasDeDosPrimos4
-- La verificación es
-- λ> verifica
--
-- 4 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_sumasDeDosPrimos :: NonNegative Int -> Bool
prop_sumasDeDosPrimos (NonNegative n) =
all (== sumasDeDosPrimos1 !! n)
[sumasDeDosPrimos2 !! n,
sumasDeDosPrimos3 !! n,
sumasDeDosPrimos4 !! n]
-- La comprobación es
-- λ> quickCheck prop_sumasDeDosPrimos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sumasDeDosPrimos1 !! 5000
-- 7994
-- (2.61 secs, 9,299,106,792 bytes)
-- λ> sumasDeDosPrimos2 !! 5000
-- 7994
-- (1.48 secs, 5,190,651,760 bytes)
-- λ> sumasDeDosPrimos3 !! 5000
-- 7994
-- (0.12 secs, 351,667,104 bytes)
-- λ> sumasDeDosPrimos4 !! 5000
Capítulo 16. Miscelánea 1273
-- 7994
-- (0.04 secs, 63,464,320 bytes)
--
-- λ> sumasDeDosPrimos3 !! (5*10^4)
-- 83674
-- (2.23 secs, 7,776,049,264 bytes)
-- λ> sumasDeDosPrimos4 !! (5*10^4)
-- 83674
-- (0.34 secs, 1,183,604,984 bytes)
-- ---------------------------------------------------------------------
-- § Referencia --
-- ---------------------------------------------------------------------
16.5.2. En Python
# ---------------------------------------------------------------------
# Definir la sucesión
# sumasDeDosPrimos :: [Integer]
# cuyos elementos son los números que se pueden escribir como suma de
# dos números primos. Por ejemplo,
# >>> list(islice(sumasDeDosPrimos1(), 23))
# [4,5,6,7,8,9,10,12,13,14,15,16,18,19,20,21,22,24,25,26,28,30,31]
# >>> list(islice(sumasDeDosPrimos4(), 5*10**5, 1+5*10**5))[0]
# 862878
# ---------------------------------------------------------------------
# 1ª solución
# ===========
1274 Ejercicios de programación con Python
# 2ª solución
# ===========
# 3ª solución
# ===========
# esSumaDeDosPrimos3(17) == False
def esSumaDeDosPrimos3(n: int) -> bool:
if n % 2 == 1:
return isprime(n-2)
return any(isprime(n-x)
for x in takewhile(lambda x : x <= n // 2, primos()))
# 4ª solución
# ===========
# Usando la conjetura de Goldbach que dice que ”Todo número par mayor
# que 2 puede escribirse como suma de dos números primos” .
# Verificación --
# ============
# La propiedad es
def test_sumasDeDosPrimos() -> None:
r = [4,5,6,7,8,9,10,12,13,14,15,16,18,19,20,21,22,24,25,26,28,30,31]
assert list(islice(sumasDeDosPrimos1(), 23)) == r
assert list(islice(sumasDeDosPrimos2(), 23)) == r
assert list(islice(sumasDeDosPrimos3(), 23)) == r
assert list(islice(sumasDeDosPrimos4(), 23)) == r
print(”Verificado”)
# La comprobación es
1276 Ejercicios de programación con Python
# >>> test_sumasDeDosPrimos()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=200))
def test_sumasDeDosPrimos_equiv(n: int) -> None:
r = nth(sumasDeDosPrimos1(), n)
assert nth(sumasDeDosPrimos2(), n) == r
assert nth(sumasDeDosPrimos3(), n) == r
assert nth(sumasDeDosPrimos4(), n) == r
# La comprobación es
# >>> test_sumasDeDosPrimos_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('nth(sumasDeDosPrimos1(), 1000)')
# 3.02 segundos
# >>> tiempo('nth(sumasDeDosPrimos2(), 1000)')
# 1.53 segundos
# >>> tiempo('nth(sumasDeDosPrimos3(), 1000)')
# 0.03 segundos
# >>> tiempo('nth(sumasDeDosPrimos4(), 1000)')
# 0.00 segundos
Capítulo 16. Miscelánea 1277
#
# >>> tiempo('nth(sumasDeDosPrimos3(), 5*10**4)')
# 3.76 segundos
# >>> tiempo('nth(sumasDeDosPrimos4(), 5*10**4)')
# 0.33 segundos
# ---------------------------------------------------------------------
# § Referencia --
# ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 9999999999999998758486016
-- λ> raiz (10^50)
-- 10000000000000000000000000
-- 3ª solución
-- ===========
-- Verificación --
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG representaciones1
describe ”def. 2” $ specG representaciones2
describe ”def. 3” $ specG representaciones3
-- La verificación es
-- λ> verifica
--
-- 9 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_representaciones :: Positive Integer -> Bool
prop_representaciones (Positive n) =
all (== representaciones1 n)
[representaciones2 n,
representaciones3 n]
-- La comprobación es
-- λ> quickCheck prop_representaciones
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> representaciones1 4000
-- [(20,60),(36,52)]
-- (4.95 secs, 2,434,929,624 bytes)
-- λ> representaciones2 4000
-- [(20,60),(36,52)]
-- (0.00 secs, 599,800 bytes)
-- λ> representaciones3 4000
-- [(20,60),(36,52)]
-- (0.01 secs, 591,184 bytes)
--
-- λ> length (representaciones2 (10^14))
Capítulo 16. Miscelánea 1281
-- 8
-- (6.64 secs, 5,600,837,088 bytes)
-- λ> length (representaciones3 (10^14))
-- 8
-- (9.37 secs, 4,720,548,264 bytes)
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_representacion :: Positive Integer -> Bool
prop_representacion (Positive n) =
not (null (representaciones2 n)) ==
all (\(p,e) -> p `mod` 4 /= 3 || even e) (factorizacion n)
-- La comprobación es
-- λ> quickCheck prop_representacion
-- +++ OK, passed 100 tests.
16.6.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# representaciones : (int) -> list[tuple[int, int]]
# tal que representaciones(n) es la lista de pares de números
# naturales (x,y) tales que n = x^2 + y^2. Por ejemplo.
# representaciones(20) == [(2,4)]
# representaciones(25) == [(0,5),(3,4)]
# representaciones(325) == [(1,18),(6,17),(10,15)]
# len(representaciones(10**14)) == 8
#
# Comprobar con Hypothesis que un número natural n se puede representar
# como suma de dos cuadrados si, y sólo si, en la factorización prima
# de n todos los exponentes de sus factores primos congruentes con 3
1282 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
1284 Ejercicios de programación con Python
# La comprobación es
# >>> test_representaciones()
# Verificado
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_representaciones_equiv(x: int) -> None:
xs = representaciones1(x)
assert representaciones2(x) == xs
assert representaciones3(x) == xs
# La comprobación es
# >>> test_representaciones_equiv()
# >>>
# La comparación es
Capítulo 16. Miscelánea 1285
# >>> tiempo('representaciones1(5000)')
# 1.27 segundos
# >>> tiempo('representaciones2(5000)')
# 0.00 segundos
# >>> tiempo('representaciones3(5000)')
# 0.00 segundos
#
# >>> tiempo('len(representaciones2(10**12))')
# 11.54 segundos
# >>> tiempo('len(representaciones3(10**12))')
# 12.08 segundos
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_representaciones_prop(n: int) -> None:
factores = factorint(n)
assert (len(representaciones2(n)) > 0) == \
all(p % 4 != 3 or e % 2 == 0 for p, e in factores.items())
# La comprobación es
# >>> test_representaciones_prop()
# >>>
--
-- Definir la lista
-- serieThueMorse :: [[Int]]
-- tal que sus elementos son los términos de la serie de Thue-Morse. Por
-- ejemplo,
-- λ> take 4 serieThueMorse
-- [[0],[0,1],[0,1,1,0],[0,1,1,0,1,0,0,1]]
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
serieThueMorse1 :: [[Int]]
serieThueMorse1 = map termSerieThueMorse [0..]
-- 2ª solución
-- ===========
serieThueMorse2 :: [[Int]]
serieThueMorse2 = [0] : map paso serieThueMorse2
where paso xs = xs ++ complementaria xs
-- 3ª solución
-- ===========
serieThueMorse3 :: [[Int]]
serieThueMorse3 = iterate paso [0]
where paso xs = xs ++ complementaria xs
-- 4ª solución
-- ===========
serieThueMorse4 :: [[Int]]
serieThueMorse4 = [0] : map (concatMap paso4) serieThueMorse4
where paso4 x = [x,1-x]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG serieThueMorse1
1288 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- 4 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_serieThueMorse :: NonNegative Int -> Bool
prop_serieThueMorse (NonNegative n) =
all (== serieThueMorse1 !! n)
[serieThueMorse2 !! n,
serieThueMorse3 !! n,
serieThueMorse4 !! n]
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_serieThueMorse
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> (serieThueMorse1 !! 23) !! (2^22)
-- 1
-- (1.08 secs, 839,419,224 bytes)
-- λ> (serieThueMorse2 !! 23) !! (2^22)
-- 1
-- (0.61 secs, 839,413,592 bytes)
-- λ> (serieThueMorse3 !! 23) !! (2^22)
-- 1
-- (1.43 secs, 839,413,592 bytes)
-- λ> (serieThueMorse4 !! 23) !! (2^22)
-- 1
-- (1.57 secs, 1,007,190,024 bytes)
Capítulo 16. Miscelánea 1289
-- ---------------------------------------------------------------------
-- § Referencias --
-- ---------------------------------------------------------------------
16.7.2. En Python
# ---------------------------------------------------------------------
# La [serie de Thue-Morse](http://bit.ly/1KvZONW) comienza con el
# término [0] y sus siguientes términos se construyen añadiéndole al
# anterior su complementario (es decir, la lista obtenida cambiando el
# 0 por 1 y el 1 por 0). Los primeros términos de la serie son
# [0]
# [0,1]
# [0,1,1,0]
# [0,1,1,0,1,0,0,1]
# [0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0]
#
# Definir la función
# serieThueMorse : () -> Iterator[list[list[int]]]
# tal que sus elementos son los términos de la serie de Thue-Morse. Por
# ejemplo,
# >>> list(islice(serieThueMorse(), 4))
# [[0], [0, 1], [0, 1, 1, 0], [0, 1, 1, 0, 1, 0, 0, 1]]
# ---------------------------------------------------------------------
# Solución
# ========
# Verificación
# ============
# La verificación es
# >>> test_serieThueMorse()
# Verificado
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
import Test.QuickCheck
import Test.Hspec (Spec, describe, hspec, it, shouldBe)
-- 1ª solución
1292 Ejercicios de programación con Python
-- ===========
sucThueMorse1 :: [Int]
sucThueMorse1 = map termSucThueMorse1 [0..]
-- 2ª solución
-- ===========
sucThueMorse2 :: [Int]
sucThueMorse2 =
0 : intercala (map (1-) sucThueMorse2) (tail sucThueMorse2)
-- 3ª solución
Capítulo 16. Miscelánea 1293
-- ===========
sucThueMorse3 :: [Int]
sucThueMorse3 = 0 : 1 : aux (tail sucThueMorse3)
where aux (x : xs) = x : (1 - x) : aux xs
-- 4ª solución
-- ===========
sucThueMorse4 :: [Int]
sucThueMorse4 = 0 : aux [1]
where aux xs = xs ++ aux (xs ++ map (1-) xs)
-- Comprobación de la propiedad
-- ============================
-- La propiedad es
prop_termSucThueMorse :: NonNegative Int -> Bool
prop_termSucThueMorse (NonNegative n) =
sucThueMorse1 !! (2*n) == sn &&
sucThueMorse1 !! (2*n+1) == 1 - sn
where sn = sucThueMorse1 !! n
-- La comprobación es
-- λ> quickCheck prop_termSucThueMorse
-- +++ OK, passed 100 tests.
-- 5ª solución
-- ===========
sucThueMorse5 :: [Int]
sucThueMorse5 = map termSucThueMorse5 [0..]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG sucThueMorse1
describe ”def. 2” $ specG sucThueMorse2
describe ”def. 3” $ specG sucThueMorse3
describe ”def. 4” $ specG sucThueMorse4
describe ”def. 5” $ specG sucThueMorse5
-- La verificación es
-- λ> verifica
--
-- 5 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_sucThueMorse :: NonNegative Int -> Bool
prop_sucThueMorse (NonNegative n) =
all (== sucThueMorse1 !! n)
[sucThueMorse2 !! n,
sucThueMorse3 !! n,
Capítulo 16. Miscelánea 1295
sucThueMorse4 !! n,
sucThueMorse5 !! n]
-- La comprobación es
-- λ> quickCheck prop_sucThueMorse
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> sucThueMorse1 !! (10^7)
-- 0
-- (3.28 secs, 3,420,080,168 bytes)
-- λ> sucThueMorse2 !! (10^7)
-- 0
-- (3.01 secs, 1,720,549,640 bytes)
-- λ> sucThueMorse3 !! (10^7)
-- 0
-- (1.80 secs, 1,360,550,040 bytes)
-- λ> sucThueMorse4 !! (10^7)
-- 0
-- (0.88 secs, 1,254,772,768 bytes)
-- λ> sucThueMorse5 !! (10^7)
-- 0
-- (0.62 secs, 1,600,557,072 bytes)
-- ---------------------------------------------------------------------
-- § Referencias --
-- ---------------------------------------------------------------------
16.8.2. En Python
# ---------------------------------------------------------------------
# La serie de Thue-Morse comienza con el término [0] y sus siguientes
# términos se construyen añadiéndole al anterior su complementario. Los
1296 Ejercicios de programación con Python
A = TypeVar('A')
# 1ª solución
# ===========
# Comprobación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=100))
def test_prop_termSucThueMorse(n: int) -> None:
sn = nth(sucThueMorse(), n)
assert nth(sucThueMorse(), 2*n) == sn
assert nth(sucThueMorse(), 2*n+1) == 1 - sn
# La comprobación es
# >>> test_prop_termSucThueMorse()
# >>>
# 2ª solución
# ===========
# termSucThueMorse2(4) == 1
def termSucThueMorse2(n: int) -> int:
if n == 0:
return 0
if n % 2 == 0:
return termSucThueMorse2(n // 2)
return 1 - termSucThueMorse2(n // 2)
# Verificación
# ============
# La verificación es
# >>> test_sucThueMorse()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=100))
def test_sucThueMorse_equiv(n: int) -> None:
assert nth(sucThueMorse(), n) == nth(sucThueMorse2(), n)
# La comprobación es
# >>> test_sucThueMorse_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('nth(sucThueMorse(), 6000)')
# 2.05 segundos
# >>> tiempo('nth(sucThueMorse2(), 6000)')
# 0.01 segundos
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
-- 23 6
-- 89 8
-- 113 14
-- 523 18
-- 887 20
--
-- Definir la sucesión
-- primosYhuecosMaximales :: [(Integer,Integer)]
-- cuyos elementos son los números primos con huecos maximales junto son
-- sus huecos. Por ejemplo,
-- λ> take 8 primosYhuecosMaximales
-- [(2,1),(3,2),(7,4),(23,6),(89,8),(113,14),(523,18),(887,20)]
-- λ> primosYhuecosMaximales !! 20
-- (2010733,148)
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
primosYhuecosMaximales1 :: [(Integer,Integer)]
primosYhuecosMaximales1 =
[(p,huecoPrimo p) | p <- primes, esMaximalHuecoPrimo p]
-- 2ª solución
-- ===========
primosYhuecosMaximales2 :: [(Integer,Integer)]
primosYhuecosMaximales2 = aux primosYhuecos
where aux ((x,y):ps) = (x,y) : aux (dropWhile (\(_,b) -> b <= y) ps)
-- 3ª solución
-- ===========
primosYhuecosMaximales3 :: [(Integer,Integer)]
primosYhuecosMaximales3 = aux 0 primes
where aux n (x:y:zs) | y-x > n = (x,y-x) : aux (y-x) (y:zs)
| otherwise = aux n (y:zs)
-- Verificación
-- ============
1302 Ejercicios de programación con Python
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG primosYhuecosMaximales1
describe ”def. 2” $ specG primosYhuecosMaximales2
describe ”def. 3” $ specG primosYhuecosMaximales3
-- La verificación es
-- λ> verifica
--
-- 3 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_primosYhuecosMaximales :: NonNegative Int -> Bool
prop_primosYhuecosMaximales (NonNegative n) =
all (== primosYhuecosMaximales1 !! n)
[primosYhuecosMaximales2 !! n,
primosYhuecosMaximales3 !! n]
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=12}) prop_primosYhuecosMaximales
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> primosYhuecosMaximales1 !! 10
Capítulo 16. Miscelánea 1303
-- (9551,36)
-- (2.63 secs, 7,400,316,112 bytes)
-- λ> primosYhuecosMaximales2 !! 10
-- (9551,36)
-- (0.01 secs, 7,060,744 bytes)
-- λ> primosYhuecosMaximales3 !! 10
-- (9551,36)
-- (0.01 secs, 4,000,368 bytes)
--
-- λ> primosYhuecosMaximales2 !! 22
-- (17051707,180)
-- (7.90 secs, 17,275,407,712 bytes)
-- λ> primosYhuecosMaximales3 !! 22
-- (17051707,180)
-- (3.78 secs, 8,808,779,096 bytes)
-- ---------------------------------------------------------------------
-- § Referencias --
-- ---------------------------------------------------------------------
-- Otras referencias
-- + C. Caldwell, ”The gaps between primes” http://bit.ly/1Znusp5
-- + J.K. Andersen, ”Maximal prime gaps” http://bit.ly/1ZntwRi
-- + N.J.A. Sloane ”Sequence A002386” en OEIS http://oeis.org/A002386
-- + N.J.A. Sloane ”Sequence A005250” en OEIS http://oeis.org/A005250
-- + E.W. Weisstein, ”Prime gaps” en MathWorld http://bit.ly/1ZnubCq
16.9.2. En Python
# ---------------------------------------------------------------------
# El **hueco de un número primo** p es la distancia entre p y primo
# siguiente de p. Por ejemplo, el hueco de 7 es 4 porque el primo
# siguiente de 7 es 11 y 4 = 11-7. Los huecos de los primeros números son
# Primo Hueco
# 2 1
# 3 2
# 7 4
1304 Ejercicios de programación con Python
# 11 2
#
# El hueco de un número primo p es **maximal** si es mayor que los
# huecos de todos los números menores que p. Por ejemplo, 4 es un hueco
# maximal de 7 ya que los huecos de los primos menores que 7 son 1 y 2
# y ambos son menores que 4. La tabla de los primeros huecos maximales es
# Primo Hueco
# 2 1
# 3 2
# 7 4
# 23 6
# 89 8
# 113 14
# 523 18
# 887 20
#
# Definir la sucesión
# primosYhuecosMaximales : () -> Iterator[tuple[int, int]]
# cuyos elementos son los números primos con huecos maximales junto son
# sus huecos. Por ejemplo,
# >>> list(islice(primosYhuecosMaximales1(), 8))
# [(2,1),(3,2),(7,4),(23,6),(89,8),(113,14),(523,18),(887,20)]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_primosYhuecosMaximales()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('list(islice(primosYhuecosMaximales1(), 15))')
# 8.08 segundos
# >>> tiempo('list(islice(primosYhuecosMaximales2(), 15))')
# 0.17 segundos
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
# Otras referencias
# + C. Caldwell, ”The gaps between primes” http://bit.ly/1Znusp5
# + J.K. Andersen, ”Maximal prime gaps” http://bit.ly/1ZntwRi
# + N.J.A. Sloane ”Sequence A002386” en OEIS http://oeis.org/A002386
# + N.J.A. Sloane ”Sequence A005250” en OEIS http://oeis.org/A005250
# + E.W. Weisstein, ”Prime gaps” en MathWorld http://bit.ly/1ZnubCq
Capítulo 16. Miscelánea 1307
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- =============
-- 4ª solución
-- =============
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG phi1
describe ”def. 2” $ specG phi2
Capítulo 16. Miscelánea 1309
-- La verificación es
-- λ> verifica
--
-- 8 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_phi :: Positive Integer -> Bool
prop_phi (Positive n) =
all (== phi1 n)
[phi2 n,
phi3 n,
phi4 n]
-- La comprobación es
-- λ> quickCheck prop_phi
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> phi1 (2*10^6)
-- 800000
-- (2.49 secs, 2,117,853,856 bytes)
-- λ> phi2 (2*10^6)
-- 800000
-- (0.02 secs, 565,664 bytes)
--
-- λ> length (show (phi2 (10^100000)))
-- 100000
-- (2.80 secs, 5,110,043,208 bytes)
-- λ> length (show (phi3 (10^100000)))
-- 100000
-- (4.81 secs, 7,249,353,896 bytes)
1310 Ejercicios de programación con Python
-- Verificación de la propiedad
-- ============================
-- La propiedad es
prop_phi2 :: Positive Integer -> Bool
prop_phi2 (Positive n) =
genericLength (show (phi4 (10^n))) == n
-- La comprobación es
-- λ> quickCheck prop_phi2
-- +++ OK, passed 100 tests.
16.10.2. En Python
# ---------------------------------------------------------------------
# La [indicatriz de Euler](https://bit.ly/3yQbzA6) (también llamada
# función φ de Euler) es una función importante en teoría de
# números. Si n es un entero positivo, entonces φ(n) se define como el
# número de enteros positivos menores o iguales a n y coprimos con
# n. Por ejemplo, φ(36) = 12 ya que los números menores o iguales a 36
# y coprimos con 36 son doce: 1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31,
# y 35.
#
# Definir la función
# phi : (int) -> int
# tal que phi(n) es igual a φ(n). Por ejemplo,
# phi(36) == 12
# list(map(phi, range(10, 21))) == [4,10,4,12,6,8,8,16,6,18,8]
# phi(3**10**5) % (10**9) == 681333334
# len(str(phi2(10**(10**5)))) == 100000
#
# Comprobar con Hypothesis que, para todo n > 0, φ(10^n) tiene n
# dígitos.
# ---------------------------------------------------------------------
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# =============
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_phi()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_phi_equiv(n: int) -> None:
r = phi1(n)
assert phi2(n) == r
assert phi3(n) == r
# La comprobación es
# >>> test_phi_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('phi1(9*10**6)')
# 2.09 segundos
# >>> tiempo('phi2(9*10**6)')
# 0.00 segundos
# >>> tiempo('phi3(9*10**6)')
# 0.00 segundos
#
# >>> tiempo('phi2(10**1000000)')
# 3.55 segundos
# >>> tiempo('phi3(10**1000000)')
# 3.37 segundos
Capítulo 16. Miscelánea 1313
# Verificación de la propiedad
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_phi_prop(n: int) -> None:
assert len(str(phi2(10**n))) == n
# La comprobación es
# >>> test_phi_prop()
# >>>
# La comprobación es
# src> poetry run pytest -v La_funcion_indicatriz_de_Euler.py
# ===== test session starts =====
# test_phi PASSED
# test_phi_equiv PASSED
# test_phi_prop PASSED
# ===== passed in 0.55s =====
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- =============
where m = n `div` 5
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG cerosDelFactorial1
describe ”def. 2” $ specG cerosDelFactorial2
describe ”def. 3” $ specG cerosDelFactorial3
-- La verificación es
-- λ> verifica
--
-- 6 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_cerosDelFactorial :: Positive Integer -> Bool
prop_cerosDelFactorial (Positive n) =
all (== cerosDelFactorial1 n)
[cerosDelFactorial2 n,
cerosDelFactorial3 n]
-- La comprobación es
-- λ> quickCheck prop_cerosDelFactorial
-- +++ OK, passed 100 tests.
1316 Ejercicios de programación con Python
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> cerosDelFactorial1 (4*10^4)
-- 9998
-- (1.93 secs, 2,296,317,904 bytes)
-- λ> cerosDelFactorial2 (4*10^4)
-- 9998
-- (1.57 secs, 1,636,242,040 bytes)
-- λ> cerosDelFactorial3 (4*10^4)
-- 9998
-- (0.02 secs, 527,584 bytes)
16.11.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# cerosDelFactorial : (int) -> int
# tal que cerosDelFactorial(n) es el número de ceros en que termina el
# factorial de n. Por ejemplo,
# cerosDelFactorial(24) == 4
# cerosDelFactorial(25) == 6
# len(str(cerosDelFactorial(10**70000))) == 70000
# ---------------------------------------------------------------------
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# ejemplo,
# ceros(320000) == 4
def ceros(n: int) -> int:
r = 0
while n % 10 == 0 and n != 0:
r += 1
n //= 10
return r
# 2ª solución
# ===========
# 3ª solución
# =============
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_cerosDelFactorial()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=0, max_value=1000))
def test_cerosDelFactorial_equiv(n: int) -> None:
r = cerosDelFactorial1(n)
assert cerosDelFactorial2(n) == r
assert cerosDelFactorial3(n) == r
# La comprobación es
# >>> test_cerosDelFactorial_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('cerosDelFactorial1(6*10**4)')
# 2.47 segundos
# >>> tiempo('cerosDelFactorial2(6*10**4)')
# 0.77 segundos
# >>> tiempo('cerosDelFactorial3(6*10**4)')
# 0.00 segundos
# La comprobación es
Capítulo 16. Miscelánea 1319
-- 1ª solución
-- ===========
cubanos1 :: [Integer]
cubanos1 = filter isPrime (zipWith (-) (tail cubos) cubos)
-- 2ª solución
-- ===========
cubanos2 :: [Integer]
cubanos2 = filter isPrime [(x+1)^3 - x^3 | x <- [1..]]
-- 3ª solución
-- ===========
cubanos3 :: [Integer]
cubanos3 = filter isPrime [3*x^2 + 3*x + 1 | x <- [1..]]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG cubanos1
describe ”def. 2” $ specG cubanos2
describe ”def. 3” $ specG cubanos3
-- La verificación es
-- λ> verifica
--
-- 3 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_cubanos :: NonNegative Int -> Bool
Capítulo 16. Miscelánea 1321
prop_cubanos (NonNegative n) =
all (== cubanos1 !! n)
[cubanos2 !! n,
cubanos3 !! n]
-- La comprobación es
-- λ> quickCheck prop_cubanos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> cubanos1 !! 3000
-- 795066361
-- (4.21 secs, 16,953,612,192 bytes)
-- λ> cubanos2 !! 3000
-- 795066361
-- (4.27 secs, 16,962,597,288 bytes)
-- λ> cubanos3 !! 3000
-- 795066361
-- (4.29 secs, 16,956,085,672 bytes)
16.12.2. En Python
# ---------------------------------------------------------------------
# Un [primo cubano](http://bit.ly/1jPy5QZ) es un número primo que se
# puede escribir como diferencia de dos cubos consecutivos. Por
# ejemplo, el 61 es un primo cubano porque es primo y 61 = 5³-4³.
#
# Definir la sucesión
# cubanos : () -> Iterator[int]
# tal que sus elementos son los números cubanos. Por ejemplo,
# >>> list(islice(cubanos(), 15))
# [7,19,37,61,127,271,331,397,547,631,919,1657,1801,1951,2269]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 16. Miscelánea 1323
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_cubanos()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
def test_cubanos_equiv(n: int) -> None:
r = list(islice(cubanos1(), n))
assert list(islice(cubanos2(), n)) == r
assert list(islice(cubanos3(), n)) == r
assert list(islice(cubanos4(), n)) == r
print(”Verificado”)
# La comprobación es
# >>> test_cubanos_equiv(10000)
# Verificado
# Comparación de eficiencia
# =========================
1324 Ejercicios de programación con Python
# La comparación es
# >>> tiempo('list(islice(cubanos1(), 30000))')
# 1.54 segundos
# >>> tiempo('list(islice(cubanos1(), 40000))')
# 2.20 segundos
# >>> tiempo('list(islice(cubanos2(), 40000))')
# 2.22 segundos
# >>> tiempo('list(islice(cubanos3(), 40000))')
# 2.19 segundos
# >>> tiempo('list(islice(cubanos4(), 40000))')
# 2.15 segundos
-- 1ª solución
-- ===========
Capítulo 16. Miscelánea 1325
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
1326 Ejercicios de programación con Python
-- 4ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
cuadradoCercano 8 `shouldBe` 9
spec :: Spec
spec = do
describe ”def. 1” $ specG cuadradoCercano1
describe ”def. 2” $ specG cuadradoCercano2
describe ”def. 3” $ specG cuadradoCercano3
-- La verificación es
-- λ> verifica
--
-- 9 examples, 0 failures
-- La propiedad es
prop_cuadradoCercano :: Positive Integer -> Bool
prop_cuadradoCercano (Positive x) =
all (== cuadradoCercano1 x)
[cuadradoCercano2 x,
cuadradoCercano3 x]
-- La comprobación es
-- λ> quickCheck prop_cuadradoCercano
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> cuadradoCercano1 (10^14)
-- 100000000000000
-- (4.59 secs, 5,920,475,784 bytes)
-- λ> cuadradoCercano2 (10^14)
-- 100000000000000
-- (0.01 secs, 512,472 bytes)
-- λ> cuadradoCercano3 (10^14)
-- 100000000000000
-- (0.01 secs, 494,248 bytes)
1328 Ejercicios de programación con Python
--
-- λ> length (show (cuadradoCercano2 (10^20000)))
-- 20001
-- (3.94 secs, 1,446,675,504 bytes)
-- λ> length (show (cuadradoCercano3 (10^20000)))
-- 20001
-- (4.50 secs, 926,647,904 bytes)
16.13.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# cuadradoCercano : (int) -> int
# tal que cuadradoCercano(n) es el número cuadrado más cercano a n,
# donde n es un entero positivo. Por ejemplo,
# cuadradoCercano(2) == 1
# cuadradoCercano(6) == 4
# cuadradoCercano(8) == 9
# cuadradoCercano(10^46) == 10000000000000000000000000000000000000000000000
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
if n == 1:
return 1
x = n
while x * x > n:
x = (x + n // x) // 2
return x
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_cuadradoCercano()
Capítulo 16. Miscelánea 1331
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_cuadradoCercano_equiv(x: int) -> None:
r = cuadradoCercano1(x)
assert cuadradoCercano2(x) == r
assert cuadradoCercano3(x) == r
assert cuadradoCercano4(x) == r
# La comprobación es
# >>> test_cuadradoCercano_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('cuadradoCercano1(10**14)')
# 2.88 segundos
# >>> tiempo('cuadradoCercano2(10**14)')
# 0.00 segundos
# >>> tiempo('cuadradoCercano3(10**14)')
# 0.00 segundos
#
# >>> tiempo('cuadradoCercano2(10**6000)')
# 1.21 segundos
# >>> tiempo('cuadradoCercano3(10**6000)')
# 2.08 segundos
1332 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
Capítulo 16. Miscelánea 1333
-- 4ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
it ”e6” $
sumaCadenas ”” ”5” `shouldBe` ”5”
it ”e7” $
sumaCadenas ”6” ”” `shouldBe` ”6”
it ”e8” $
sumaCadenas ”” ”” `shouldBe` ”0”
spec :: Spec
spec = do
describe ”def. 1” $ specG sumaCadenas1
describe ”def. 2” $ specG sumaCadenas2
describe ”def. 3” $ specG sumaCadenas3
describe ”def. 4” $ specG sumaCadenas4
-- La verificación es
-- λ> verifica
--
-- 32 examples, 0 failures
16.14.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# sumaCadenas : (str, str) -> str
# tal que sumaCadenas(xs, ys) es la cadena formada por el número entero
# que es la suma de los números enteros cuyas cadenas que lo
# representan son xs e ys; además, se supone que la cadena vacía
# representa al cero. Por ejemplo,
# sumaCadenas(”2”, ”6”) == ”8”
# sumaCadenas(”14”, ”2”) == ”16”
# sumaCadenas(”14”, ”-5”) == ”9”
# sumaCadenas(”-14”, ”-5”) == ”-19”
# sumaCadenas(”5”, ”-5”) == ”0”
# sumaCadenas(””, ”5”) == ”5”
# sumaCadenas(”6”, ””) == ”6”
# sumaCadenas(””, ””) == ”0”
# ---------------------------------------------------------------------
# 1ª solución
# ===========
Capítulo 16. Miscelánea 1335
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_sumaCadenas()
# Verificado
-- 1ª definición de factoradicoAdecimal
-- ====================================
-- 2ª definición de factoradicoAdecimal
-- ====================================
-- 3ª definición de factoradicoAdecimal
-- ====================================
-- 4ª definición de factoradicoAdecimal
1340 Ejercicios de programación con Python
-- ====================================
-- 1ª definición de decimalAfactoradico
-- ====================================
-- 2ª definición de decimalAfactoradico
-- ====================================
Capítulo 16. Miscelánea 1341
-- 3ª definición de decimalAfactoradico
-- ====================================
-- 4ª definición de decimalAfactoradico
-- ====================================
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG1 factoradicoAdecimal1
describe ”def. 2” $ specG1 factoradicoAdecimal2
describe ”def. 3” $ specG1 factoradicoAdecimal3
describe ”def. 4” $ specG1 factoradicoAdecimal4
describe ”def. 1” $ specG2 decimalAfactoradico1
describe ”def. 2” $ specG2 decimalAfactoradico2
describe ”def. 3” $ specG2 decimalAfactoradico3
describe ”def. 4” $ specG2 decimalAfactoradico4
-- La verificación es
-- λ> verifica
--
-- 24 examples, 0 failures
-- Propiedad de inverso
-- ====================
-- La comprobación es
-- λ> quickCheck prop_factoradico
-- +++ OK, passed 100 tests; 101 discarded.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (decimalAfactoradico1 (10^300000))
1344 Ejercicios de programación con Python
-- 68191
-- (2.46 secs, 9,088,634,744 bytes)
-- λ> length (decimalAfactoradico2 (10^300000))
-- 68191
-- (2.36 secs, 9,088,634,800 bytes)
-- λ> length (decimalAfactoradico3 (10^300000))
-- 68191
-- (2.18 secs, 4,490,856,416 bytes)
-- λ> length (decimalAfactoradico4 (10^300000))
-- 68191
-- (1.98 secs, 4,490,311,536 bytes)
--
-- λ> length (show (factoradicoAdecimal1 (show (10^50000))))
-- 213237
-- (0.93 secs, 2,654,156,680 bytes)
-- λ> length (show (factoradicoAdecimal2 (show (10^50000))))
-- 213237
-- (0.51 secs, 2,633,367,168 bytes)
-- λ> length (show (factoradicoAdecimal3 (show (10^50000))))
-- 213237
-- (0.93 secs, 2,635,792,192 bytes)
-- λ> length (show (factoradicoAdecimal4 (show (10^50000))))
-- 213237
-- (0.43 secs, 2,636,996,848 bytes)
16.15.2. En Python
# ---------------------------------------------------------------------
# El [sistema factorádico](https://bit.ly/3KQZRue) es un sistema
# numérico basado en factoriales en el que el n-ésimo dígito, empezando
# desde la derecha, debe ser multiplicado por n! Por ejemplo, el número
# ”341010” en el sistema factorádico es 463 en el sistema decimal ya
# que
# 3×5! + 4×4! + 1×3! + 0×2! + 1×1! + 0×0! = 463
#
# En este sistema numérico, el dígito de más a la derecha es siempre 0,
# el segundo 0 o 1, el tercero 0,1 o 2 y así sucesivamente.
#
# Con los dígitos del 0 al 9 el mayor número que podemos codificar es el
# 10!-1 = 3628799. En cambio, si lo ampliamos con las letras A a Z podemos
Capítulo 16. Miscelánea 1345
# 1ª definición de factoradicoAdecimal
# ====================================
# 2ª definición de factoradicoAdecimal
# ====================================
# caracterAentero2('B') == 11
# caracterAentero2('Z') == 35
def caracterAentero2(c: str) -> int:
return caracteresEnteros[c]
# 3ª definición de factoradicoAdecimal
# ====================================
# 1ª definición de decimalAfactoradico
# ====================================
# 2ª definición de decimalAfactoradico
# ====================================
y, *ys = xs
return enteroAcaracter2(m // y) + aux(m % y, ys)
return aux(n, list(reversed(list(takewhile(lambda x : x <= n, facts())))))
# 3ª definición de decimalAfactoradico
# ====================================
# Verificación
# ============
# La verificación es
# >>> test_factoradico()
# Verificado
# Propiedad de inverso
# ====================
@given(st.integers(min_value=0, max_value=1000))
def test_factoradico_equiv(n: int) -> None:
assert factoradicoAdecimal1(decimalAfactoradico1(n)) == n
assert factoradicoAdecimal2(decimalAfactoradico3(n)) == n
assert factoradicoAdecimal3(decimalAfactoradico3(n)) == n
# La comprobación es
# >>> test_factoradico_equiv()
# >>>
-- 1ª solución
Capítulo 16. Miscelánea 1351
-- 2 solución
duplicaElementos2 :: [a] -> [a]
duplicaElementos2 = foldr (\x ys -> x:x:ys) []
-- 3ª solución
duplicaElementos3 :: [a] -> [a]
duplicaElementos3 xs = concat [[x,x] | x <- xs]
-- 4ª solución
duplicaElementos4 :: [a] -> [a]
duplicaElementos4 xs = concat (map (replicate 2) xs)
-- 5ª solución
duplicaElementos5 :: [a] -> [a]
duplicaElementos5 = concatMap (replicate 2)
-- 6ª solución
duplicaElementos6 :: [a] -> [a]
duplicaElementos6 = (>>= replicate 2)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG duplicaElementos1
describe ”def. 2” $ specG duplicaElementos2
describe ”def. 3” $ specG duplicaElementos3
1352 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- 6 examples, 0 failures
-- La propiedad es
prop_duplicaElementos :: [Int] -> Bool
prop_duplicaElementos xs =
all (== duplicaElementos1 xs)
[f xs | f <- [duplicaElementos2,
duplicaElementos3,
duplicaElementos4,
duplicaElementos5,
duplicaElementos6]]
verifica_duplicaElementos :: IO ()
verifica_duplicaElementos = quickCheck prop_duplicaElementos
-- La comprobación es
-- λ> verifica_duplicaElementos
-- +++ OK, passed 100 tests.
16.16.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# duplicaElementos : (list[A]) -> list[A]
# tal que duplicaElementos(xs) es la lista obtenida duplicando cada
# elemento de xs. Por ejemplo,
# >>> duplicaElementos([3,2,5])
# [3, 3, 2, 2, 5, 5]
# >>> ””.join(duplicaElementos(”Haskell”))
# 'HHaasskkeellll'
Capítulo 16. Miscelánea 1353
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2 solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_duplicaElementos()
# Verificado
# La propiedad es
@given(st.lists(st.integers()))
def test_duplicaElementos_equiv(xs: list[int]) -> None:
r = duplicaElementos1(xs)
assert duplicaElementos2(xs) == r
assert duplicaElementos3(xs) == r
assert duplicaElementos4(xs) == r
# La comprobación es
# >>> test_duplicaElementos_equiv()
# >>>
-- 7 9 11
-- 13 15 17 19
-- 21 23 25 27 29
-- ...
--
-- Definir la función
-- sumaFilaTrianguloImpares :: Integer -> Integer
-- tal que (sumaFilaTrianguloImpares n) es la suma de la n-ésima fila
-- del triángulo de los números impares. Por ejemplo,
-- sumaFilaTrianguloImpares 1 == 1
-- sumaFilaTrianguloImpares 2 == 8
-- length (show (sumaFilaTrianguloImpares (10^500))) == 1501
-- length (show (sumaFilaTrianguloImpares (10^5000))) == 15001
-- length (show (sumaFilaTrianguloImpares (10^50000))) == 150001
-- ---------------------------------------------------------------------
-- 1ª solución
sumaFilaTrianguloImpares1 :: Integer -> Integer
sumaFilaTrianguloImpares1 n =
sum [n^2-n+1, n^2-n+3 .. n^2+n-1]
-- 2ª solución
sumaFilaTrianguloImpares2 :: Integer -> Integer
sumaFilaTrianguloImpares2 = (^3)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
it ”e2” $
sumaFilaTrianguloImpares 2 `shouldBe` 8
spec :: Spec
spec = do
describe ”def. 1” $ specG sumaFilaTrianguloImpares1
describe ”def. 2” $ specG sumaFilaTrianguloImpares2
-- La verificación es
-- λ> verifica
--
-- 4 examples, 0 failures
-- La propiedad es
prop_sumaFilaTrianguloImpares :: Positive Integer -> Bool
prop_sumaFilaTrianguloImpares (Positive n) =
sumaFilaTrianguloImpares1 n == sumaFilaTrianguloImpares2 n
-- La comprobación es
-- λ> quickCheck prop_sumaFilaTrianguloImpares
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (show (sumaFilaTrianguloImpares1 (10^7)))
-- 22
-- (2.91 secs, 2,167,239,232 bytes)
-- λ> length (show (sumaFilaTrianguloImpares2 (10^7)))
-- 22
-- (0.01 secs, 102,584 bytes)
16.17.2. En Python
# ---------------------------------------------------------------------
# Se condidera el siguiente triángulo de números impares
Capítulo 16. Miscelánea 1357
#
# 1
# 3 5
# 7 9 11
# 13 15 17 19
# 21 23 25 27 29
# ...
#
# Definir la función
# sumaFilaTrianguloImpares : (int) -> int
# tal que sumaFilaTrianguloImpares(n) es la suma de la n-ésima fila
# del triángulo de los números impares. Por ejemplo,
# sumaFilaTrianguloImpares(1) == 1
# sumaFilaTrianguloImpares(2) == 8
# len(str(sumaFilaTrianguloImpares(10**500))) == 1501
# len(str(sumaFilaTrianguloImpares(10**5000))) == 15001
# len(str(sumaFilaTrianguloImpares(10**50000))) == 150001
# ---------------------------------------------------------------------
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
1358 Ejercicios de programación con Python
# ============
# La verificación es
# >>> test_sumaFilaTrianguloImpares()
# Verificado
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_sumaFilaTrianguloImpares_equiv(n: int) -> None:
assert sumaFilaTrianguloImpares1(n) == sumaFilaTrianguloImpares2(n)
# La comprobación es
# >>> test_sumaFilaTrianguloImpares_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('len(str(sumaFilaTrianguloImpares1(6*10**7)))')
# 2.04 segundos
# >>> tiempo('len(str(sumaFilaTrianguloImpares2(6*10**7)))')
# 0.00 segundos
Capítulo 16. Miscelánea 1359
-- 1ª solución
-- ===========
1360 Ejercicios de programación con Python
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
Capítulo 16. Miscelánea 1361
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- 9ª solución
-- ===========
-- 10ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG sumaReiterada1
describe ”def. 2” $ specG sumaReiterada2
describe ”def. 3” $ specG sumaReiterada3
describe ”def. 4” $ specG sumaReiterada4
describe ”def. 5” $ specG sumaReiterada5
describe ”def. 6” $ specG sumaReiterada6
describe ”def. 7” $ specG sumaReiterada7
describe ”def. 8” $ specG sumaReiterada8
describe ”def. 9” $ specG sumaReiterada9
describe ”def. 10” $ specG sumaReiterada10
Capítulo 16. Miscelánea 1363
-- La verificación es
-- λ> verifica
--
-- 20 examples, 0 failures
-- La propiedad es
prop_sumaReiterada :: [Integer] -> Property
prop_sumaReiterada xs =
not (null xs) ==>
all (== (sumaReiterada1 xs))
[f xs | f <- [sumaReiterada2,
sumaReiterada3,
sumaReiterada4,
sumaReiterada5,
sumaReiterada6,
sumaReiterada7,
sumaReiterada8,
sumaReiterada9,
sumaReiterada10 ]]
-- La comprobación es
-- λ> quickCheck prop_sumaReiterada
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (show (sumaReiterada1 [1..4000]))
-- 1208
-- (4.84 secs, 4,444,754,000 bytes)
-- λ> length (show (sumaReiterada2 [1..4000]))
-- 1208
-- (3.07 secs, 3,332,858,616 bytes)
-- λ> length (show (sumaReiterada3 [1..4000]))
-- 1208
1364 Ejercicios de programación con Python
16.18.2. En Python
# ---------------------------------------------------------------------
# La reiteración de la suma de los elementos consecutivos de la lista
# [1,5,3] es 14 como se explica en el siguiente diagrama
# 1 + 5 = 6
# \
# ==> 14
# /
# 5 + 3 = 8
# y la de la lista [1,5,3,4] es 29 como se explica en el siguiente
# diagrama
# 1 + 5 = 6
# \
# ==> 14
# / \
# 5 + 3 = 8 ==> 29
Capítulo 16. Miscelánea 1365
# \ /
# ==> 15
# /
# 3 + 4 = 7
#
# Definir la función
# sumaReiterada : (list[int]) -> int
# tal que sumaReiterada(xs) es la suma reiterada de los elementos
# consecutivos de la lista no vacía xs. Por ejemplo,
# sumaReiterada([1,5,3]) == 14
# sumaReiterada([1,5,3,4]) == 29
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
1366 Ejercicios de programación con Python
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_sumaReiterada()
# Verificado
# La propiedad es
@given(st.lists(st.integers(), min_size=1))
def test_sumaReiterada_equiv(xs: list[int]) -> None:
r = sumaReiterada1(xs)
Capítulo 16. Miscelánea 1367
assert sumaReiterada2(xs) == r
assert sumaReiterada3(xs) == r
# La comprobación es
# >>> test_sumaReiterada_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('sumaReiterada1(range(4000))')
# 2.18 segundos
# >>> tiempo('sumaReiterada2(range(4000))')
# 1.90 segundos
# >>> tiempo('sumaReiterada3(range(4000))')
# 1.97 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG productoDiagonal1
describe ”def. 2” $ specG productoDiagonal2
describe ”def. 3” $ specG productoDiagonal3
describe ”def. 4” $ specG productoDiagonal4
describe ”def. 5” $ specG productoDiagonal5
-- La verificación es
1370 Ejercicios de programación con Python
-- λ> verifica
--
-- 10 examples, 0 failures
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (show (productoDiagonal1 (ejemplo 7000)))
-- 23878
-- (1.23 secs, 3,396,129,424 bytes)
-- λ> length (show (productoDiagonal2 (ejemplo 7000)))
-- 23878
-- (0.94 secs, 3,396,127,680 bytes)
-- λ> length (show (productoDiagonal3 (ejemplo 7000)))
-- 23878
-- (0.09 secs, 44,841,864 bytes)
-- λ> length (show (productoDiagonal4 (ejemplo 7000)))
-- 23878
-- (0.96 secs, 3,614,137,840 bytes)
-- λ> length (show (productoDiagonal5 (ejemplo 7000)))
-- 23878
-- (0.07 secs, 44,168,984 bytes)
--
-- λ> length (show (productoDiagonal3 (ejemplo 70000)))
-- 308760
-- (8.26 secs, 5,359,752,408 bytes)
-- λ> length (show (productoDiagonal5 (ejemplo 70000)))
-- 308760
-- (9.34 secs, 5,353,035,656 bytes)
16.19.2. En Python
# ---------------------------------------------------------------------
# Las matrices se pueden representar como lista de listas de la misma
# longitud, donde cada uno de sus elementos representa una fila de la
# matriz.
Capítulo 16. Miscelánea 1371
#
# Definir la función
# productoDiagonalPrincipal :: Num a => [[a]] -> a
# tal que (productoDiagonalPrincipal xss) es el producto de los
# elementos de la diagonal principal de la matriz cuadrada xss. Por
# ejemplo,
# productoDiagonal([[3,5,2],[4,7,1],[6,9,8]]) == 168
# productoDiagonal([[1, 2, 3, 4, 5]]*5) == 120
# len(str(productoDiagonal([range(1, 30001)]*30000))) == 121288
# ---------------------------------------------------------------------
set_int_max_str_digits(10**6)
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
1372 Ejercicios de programación con Python
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_productoDiagonal()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('productoDiagonal1(ejemplo(1200))')
# 1.97 segundos
# >>> tiempo('productoDiagonal2(ejemplo(1200))')
# 0.00 segundos
# >>> tiempo('productoDiagonal3(ejemplo(1200))')
# 1.56 segundos
-- 1ª solución
-- ===========
esPotenciaDe4_1 1 = True
esPotenciaDe4_1 n = n `mod` 4 == 0 && esPotenciaDe4_1 (n `div` 4)
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
Capítulo 16. Miscelánea 1375
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG esPotenciaDe4_1
describe ”def. 2” $ specG esPotenciaDe4_2
describe ”def. 3” $ specG esPotenciaDe4_3
describe ”def. 4” $ specG esPotenciaDe4_4
describe ”def. 5” $ specG esPotenciaDe4_5
-- La verificación es
-- λ> verifica
--
-- 10 examples, 0 failures
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> esPotenciaDe4_1 (4^(4*10^4))
-- True
-- (0.18 secs, 233,903,248 bytes)
-- λ> esPotenciaDe4_2 (4^(4*10^4))
1376 Ejercicios de programación con Python
-- True
-- (2.01 secs, 756,125,712 bytes)
-- λ> esPotenciaDe4_3 (4^(4*10^4))
-- True
-- (0.05 secs, 212,019,464 bytes)
-- λ> esPotenciaDe4_4 (4^(4*10^4))
-- True
-- (0.05 secs, 212,019,368 bytes)
-- λ> esPotenciaDe4_5 (4^(4*10^4))
-- True
-- (0.07 secs, 209,779,888 bytes)
--
-- λ> esPotenciaDe4_3 (4^(2*10^5))
-- True
-- (0.64 secs, 5,184,667,280 bytes)
-- λ> esPotenciaDe4_4 (4^(2*10^5))
-- True
-- (0.64 secs, 5,184,667,200 bytes)
-- λ> esPotenciaDe4_5 (4^(2*10^5))
-- True
-- (0.63 secs, 5,173,467,656 bytes)
--
-- λ> esPotenciaDe4_3 (4^(4*10^5))
-- True
-- (2.27 secs, 20,681,727,464 bytes)
-- λ> esPotenciaDe4_4 (4^(4*10^5))
-- True
-- (2.30 secs, 20,681,727,320 bytes)
-- λ> esPotenciaDe4_5 (4^(4*10^5))
-- True
-- (2.28 secs, 20,659,327,352 bytes)
16.20.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# esPotenciaDe4 : (int) -> bool
# tal que esPotenciaDe4(n) se verifica si n es una potencia de 4. Por
# ejemplo,
# esPotenciaDe4(16) == True
Capítulo 16. Miscelánea 1377
# esPotenciaDe4(17) == False
# esPotenciaDe4(4**(4*10**5)) == True
# esPotenciaDe4(1 + 4**(4*10**5)) == False
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Verificación
Capítulo 16. Miscelánea 1379
# ============
# La verificación es
# >>> test_esPotenciaDe4()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('esPotenciaDe4_1(4**(2*10**4))')
# 0.33 segundos
# >>> tiempo('esPotenciaDe4_2(4**(2*10**4))')
# 0.63 segundos
# >>> tiempo('esPotenciaDe4_3(4**(2*10**4))')
# 0.04 segundos
# >>> tiempo('esPotenciaDe4_4(4**(2*10**4))')
# 0.05 segundos
# >>> tiempo('esPotenciaDe4_5(4**(2*10**4))')
# 0.04 segundos
#
# >>> tiempo('esPotenciaDe4_3(4**(3*10**5))')
# 2.29 segundos
# >>> tiempo('esPotenciaDe4_4(4**(3*10**5))')
# 2.28 segundos
# >>> tiempo('esPotenciaDe4_5(4**(3*10**5))')
1380 Ejercicios de programación con Python
# 2.31 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
1382 Ejercicios de programación con Python
-- La verificación es
-- λ> verifica
--
-- 12 examples, 0 failures
-- La propiedad es
prop_exponente :: Integer -> Integer -> Property
prop_exponente x n =
x > 1 && n > 0 ==>
exponente1 x n == exponente2 x n &&
exponente1 x n == exponente3 x n
-- La comprobación es
-- λ> quickCheck prop_exponente
-- +++ OK, passed 100 tests.
16.21.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# exponente : (int, int) -> int
# tal que exponente(x, n) es el exponente de x en la factorización
# prima de n (se supone que x > 1 y n > 0). Por ejemplo,
# exponente(2, 24) == 3
# exponente(3, 24) == 1
# exponente(6, 24) == 0
# exponente(7, 24) == 0
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_exponente()
# Verificado
# La propiedad es
@given(st.integers(min_value=1, max_value=1000),
st.integers(min_value=0, max_value=1000))
def test_exponente_equiv(x: int, n: int) -> None:
r = exponente1(x, n)
assert r == exponente2(x, n)
assert r == exponente3(x, n)
# La comprobación es
# >>> test_exponente_equiv()
Capítulo 16. Miscelánea 1385
# >>>
-- ejemplo,
-- mayoresGeneradores 20 == [18,19]
-- mayoresGeneradores (10^6) == [837799]
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 1ª definición de collatz
collatz1 :: Integer -> [Integer]
collatz1 1 = [1]
collatz1 n = n : collatz1 (siguiente n)
-- 2ª definición de collatz
collatz2 :: Integer -> [Integer]
collatz2 n = takeWhile (/=1) (iterate siguiente n) ++ [1]
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
where
longitudOrbita2' 1 = 1
longitudOrbita2' x = 1 + longitudOrbita2 (siguiente x)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG mayoresGeneradores1
describe ”def. 2” $ specG mayoresGeneradores2
describe ”def. 3” $ specG mayoresGeneradores3
-- La verificación es
-- λ> verifica
--
-- 3 examples, 0 failures
-- Equivalencia de definiciones
-- ============================
-- La propiedad es
prop_mayoresGeneradores :: Positive Integer -> Bool
prop_mayoresGeneradores (Positive n) =
all (== mayoresGeneradores1 n)
[mayoresGeneradores2 n,
mayoresGeneradores3 n]
-- La comprobación es
-- λ> quickCheck prop_mayoresGeneradores
-- +++ OK, passed 100 tests.
Capítulo 16. Miscelánea 1389
-- Comprobación de eficiencia
-- ==========================
-- La comprobación es
-- λ> mayoresGeneradores (10^5)
-- [77031]
-- (5.43 secs, 6,232,320,064 bytes)
-- λ> mayoresGeneradores2 (10^5)
-- [77031]
-- (7.68 secs, 5,238,991,616 bytes)
-- λ> mayoresGeneradores3 (10^5)
-- [77031]
-- (0.88 secs, 571,788,736 bytes)
16.22.2. En Python
# ---------------------------------------------------------------------
# Se considera la siguiente operación, aplicable a cualquier número
# entero positivo:
# * Si el número es par, se divide entre 2.
# * Si el número es impar, se multiplica por 3 y se suma 1.
#
# Dado un número cualquiera, podemos calcular su órbita; es decir,
# las imágenes sucesivas al iterar la función. Por ejemplo, la órbita
# de 13 es
# 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1,...
#
# Si observamos este ejemplo, la órbita de 13 es periódica, es decir,
# se repite indefinidamente a partir de un momento dado). La conjetura
# de Collatz dice que siempre alcanzaremos el 1 para cualquier número
# con el que comencemos. Ejemplos:
# * Empezando en n = 6 se obtiene 6, 3, 10, 5, 16, 8, 4, 2, 1.
# * Empezando en n = 11 se obtiene: 11, 34, 17, 52, 26, 13, 40, 20,
# 10, 5, 16, 8, 4, 2, 1.
# * Empezando en n = 27, la sucesión tiene 112 pasos, llegando hasta
# 9232 antes de descender a 1: 27, 82, 41, 124, 62, 31, 94, 47,
# 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274,
# 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263,
# 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502,
# 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958,
1390 Ejercicios de programación con Python
# 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644,
# 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308,
# 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122,
# 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5,
# 16, 8, 4, 2, 1.
#
# Definir la función
# mayoresGeneradores :: Integer -> [Integer]
# tal que (mayoresGeneradores n) es la lista de los números menores o
# iguales que n cuyas órbitas de Collatz son las de mayor longitud. Por
# ejemplo,
# mayoresGeneradores(20) == [18,19]
# mayoresGeneradores(10^6) == [837799]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return [1]
return [n]+ collatz1(siguiente(n))
# 2ª solución
# ===========
# 3ª solución
# ===========
return 1
return 1 + longitudOrbita(siguiente(x))
# 4ª solución
# ===========
# 5ª solución
# ===========
@lru_cache(maxsize=None)
def longitudOrbita3(x: int) -> int:
if x == 1:
return 1
return 1 + longitudOrbita3(siguiente(x))
# Verificación
Capítulo 16. Miscelánea 1393
# ============
# La verificación es
# >>> test_mayoresGeneradores()
# Verificado
# Equivalencia de definiciones
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_mayoresGeneradores_equiv(n: int) -> None:
r = mayoresGeneradores1(n)
assert mayoresGeneradores2(n) == r
assert mayoresGeneradores3(n) == r
# La comprobación es
# >>> test_mayoresGeneradores_equiv()
# >>>
# Comprobación de eficiencia
# ==========================
# La comprobación es
# >>> tiempo('mayoresGeneradores1(10**5)')
# 4.08 segundos
1394 Ejercicios de programación con Python
# >>> tiempo('mayoresGeneradores2(10**5)')
# 1.95 segundos
# >>> tiempo('mayoresGeneradores3(10**5)')
# 2.16 segundos
# >>> tiempo('mayoresGeneradores4(10**5)')
# 1.71 segundos
# >>> tiempo('mayoresGeneradores5(10**5)')
# 0.14 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG maximosLocales1
describe ”def. 2” $ specG maximosLocales2
-- La verificación es
-- λ> verifica
--
-- 4 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_maximosLocales :: [Int] -> Property
prop_maximosLocales xs =
1396 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_maximosLocales
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> last (maximosLocales1 (take (6*10^6) (cycle ”abc”)))
-- 'c'
-- (3.26 secs, 1,904,464,984 bytes)
-- λ> last (maximosLocales2 (take (6*10^6) (cycle ”abc”)))
-- 'c'
-- (2.79 secs, 1,616,465,088 bytes)
16.23.2. En Python
# ---------------------------------------------------------------------
# Un máximo local de una lista es un elemento de la lista que es mayor
# que su predecesor y que su sucesor en la lista. Por ejemplo, 5 es un
# máximo local de [3,2,5,3,7,7,1,6,2] ya que es mayor que 2 (su
# predecesor) y que 3 (su sucesor).
#
# Definir la función
# maximosLocales : (xs: list[int]) -> list[int]
# tal que maximosLocales(xs) es la lista de los máximos locales de la
# lista xs. Por ejemplo,
# maximosLocales([3,2,5,3,7,7,1,6,2]) == [5,6]
# maximosLocales(list(range(0, 100))) == []
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
Capítulo 16. Miscelánea 1397
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_maximosLocales()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()))
def test_maximosLocales_equiv(xs: list[int]) -> None:
r = maximosLocales1(xs)
assert maximosLocales2(xs) == r
assert maximosLocales3(xs) == r
# La comprobación es
# >>> test_maximosLocales_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('maximosLocales1([1,2,3]*(10**4))')
# 3.19 segundos
# >>> tiempo('maximosLocales2([1,2,3]*(10**4))')
# 0.01 segundos
# >>> tiempo('maximosLocales3([1,2,3]*(10**4))')
# 0.01 segundos
#
# >>> tiempo('maximosLocales2([1,2,3]*(10**7))')
# 3.95 segundos
# >>> tiempo('maximosLocales3([1,2,3]*(10**7))')
# 1.85 segundos
Capítulo 16. Miscelánea 1399
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
it ”e1” $
caminos [[3],[7,4]] `shouldBe`
[[3,7],[3,4]]
it ”e2” $
caminos [[3],[7,4],[2,4,6]] `shouldBe`
[[3,7,2],[3,7,4],[3,4,4],[3,4,6]]
it ”e3” $
caminos [[3],[7,4],[2,4,6],[8,5,9,3]] `shouldBe`
[[3,7,2,8],[3,7,2,5],[3,7,4,5],[3,7,4,9],[3,4,4,5],[3,4,4,9],[3,4,6,9],[3,4,6
-- La verificación es
-- λ> verifica
--
-- 3 examples, 0 failures
16.24.2. En Python
# ---------------------------------------------------------------------
# Los triángulos se pueden representar mediante listas de listas. Por
# ejemplo, el triángulo
# 3
# 7 4
# 2 4 6
# 8 5 9 3
# se representa por
# [[3],[7,4],[2,4,6],[8,5,9,3]]
#
# Definir la función
# caminos : (list[list[A]]) -> list[list[A]]
# tal que caminos(xss) es la lista de los caminos en el triángulo xss
# donde los caminos comienzan en el elemento de la primera fila, en cada
# paso se mueve a uno de sus dos elementos adyacentes en la fila
Capítulo 16. Miscelánea 1401
A = TypeVar('A')
# Verificación
# ============
# La verificación es
# >>> test_caminos()
# Verificado
1402 Ejercicios de programación con Python
-- 1ª solución
Capítulo 16. Miscelánea 1403
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
where
f x y z = x + max y z
g xs ys = zipWith3 f xs ys (tail ys)
-- 6ª solución
-- ===========
-- 7ª solución
-- ===========
-- 8ª solución
-- ===========
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- (1.97 secs, 876,483,056 bytes)
Capítulo 16. Miscelánea 1405
--
-- λ> maximaSuma5 (triangulo 3000)
-- 8997000
-- (2.25 secs, 2,059,784,792 bytes)
-- λ> maximaSuma6 (triangulo 3000)
-- 8997000
-- (2.15 secs, 2,404,239,896 bytes)
-- λ> maximaSuma7 (triangulo 3000)
-- 8997000
-- (1.53 secs, 2,612,659,504 bytes)
-- λ> maximaSuma8 (triangulo 3000)
-- 8997000
-- (3.47 secs, 3,520,910,256 bytes)
--
-- λ> maximaSuma7 (triangulo 4000)
-- 15996000
-- (3.12 secs, 4,634,841,200 bytes)
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG maximaSuma1
describe ”def. 2” $ specG maximaSuma2
describe ”def. 3” $ specG maximaSuma3
describe ”def. 4” $ specG maximaSuma4
describe ”def. 5” $ specG maximaSuma5
Capítulo 16. Miscelánea 1407
-- La verificación es
-- λ> verifica
-- Finished in 0.0053 seconds
-- 24 examples, 0 failures
16.25.2. En Python
# ---------------------------------------------------------------------
# Los triángulos se pueden representar mediante listas de listas. Por
# ejemplo, el triángulo
# 3
# 7 4
# 2 4 6
# 8 5 9 3
# se representa por
# [[3],[7,4],[2,4,6],[8,5,9,3]]
#
# Definir la función
# maximaSuma : (list[list[int]]) -> int
# tal que maximaSuma(xss) es el máximo de las sumas de los elementos
# de los caminos en el triángulo xss donde los caminos comienzan en el
# elemento de la primera fila, en cada paso se mueve a uno de sus dos
# elementos adyacentes en la fila siguiente y terminan en la última
# fila. Por ejemplo,
# maximaSuma([[3],[7,4]]) == 10
# maximaSuma([[3],[7,4],[2,4,6]]) == 14
# maximaSuma([[3],[7,4],[2,4,6],[8,5,9,3]]) == 23
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return [[]]
if len(xss) == 1:
return xss
x = xss[0][0]
y1 = xss[1][0]
y2 = xss[1][1]
zss = xss[2:]
return [[x, y1] + us for _, *us in caminos([[y1]] + [zs[:-1] for zs in zss])]
[[x, y2] + us for _, *us in caminos([[y2]] + [zs[1:] for zs in zss])]
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
Capítulo 16. Miscelánea 1409
# >>> test_maximaSuma()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('maximaSuma1(triangulo(20))')
# 3.21 segundos
# >>> tiempo('maximaSuma2(triangulo(20))')
# 0.59 segundos
-- 1ª solución --
-- ===========
-- 2ª solución --
-- ===========
-- 3ª solución --
-- ===========
-- 4ª solución --
-- ===========
. sort
. map (product . concat)
. productoCartesiano
. map inits
. group
. primeFactors
-- 5ª solución --
-- ===========
-- 6ª solución --
-- ===========
Capítulo 16. Miscelánea 1413
-- 7ª solución --
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG amigos1
describe ”def. 2” $ specG amigos2
describe ”def. 3” $ specG amigos3
describe ”def. 4” $ specG amigos4
describe ”def. 5” $ specG amigos5
describe ”def. 6” $ specG amigos6
describe ”def. 7” $ specG amigos7
-- La verificación es
-- λ> verifica
-- 14 examples, 0 failures
-- Comparación de eficiencia
-- =========================
-- La comparación es
Capítulo 16. Miscelánea 1415
--
-- λ> amigos3 42262694537514864075544955198125 4240581727118860669746697184187
-- True
-- (107.54 secs, 5,594,306,392 bytes)
-- λ> amigos4 42262694537514864075544955198125 4240581727118860669746697184187
-- True
-- (1.03 secs, 942,530,824 bytes)
-- λ> amigos5 42262694537514864075544955198125 4240581727118860669746697184187
-- True
-- (0.51 secs, 591,144,304 bytes)
-- λ> amigos6 42262694537514864075544955198125 4240581727118860669746697184187
-- True
-- (0.26 secs, 379,534,608 bytes)
-- λ> amigos7 42262694537514864075544955198125 4240581727118860669746697184187
-- True
-- (0.05 secs, 25,635,464 bytes)
16.26.2. En Python
# ---------------------------------------------------------------------
# Dos [números amigos](https://bit.ly/36gSRHt) son dos números enteros
# positivos distintos tales que la suma de los divisores propios de
# cada uno es igual al otro. Los divisores propios de un número
# incluyen la unidad pero no al propio número. Por ejemplo, los
# divisores propios de 220 son 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 y
# 110. La suma de estos números equivale a 284. A su vez, los divisores
# propios de 284 son 1, 2, 4, 71 y 142. Su suma equivale a 220. Por
# tanto, 220 y 284 son amigos.
#
# Definir la función
# amigos : (int, int) -> bool
# tal que amigos(x, y) se verifica si los números x e y son amigos. Por
# ejemplo,
# amigos(220, 284) == True
# amigos(220, 23) == False
# amigos(42262694537514864075544955198125, 42405817271188606697466971841875) =
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
1418 Ejercicios de programación con Python
# 4ª solución
# ===========
# 5ª solución
# ===========
# Verificación
# ============
Capítulo 16. Miscelánea 1419
# La verificación es
# >>> test_amigos()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('amigos1(5864660, 7489324)')
# 0.65 segundos
# >>> tiempo('amigos2(5864660, 7489324)')
# 0.00 segundos
# >>> tiempo('amigos3(5864660, 7489324)')
# 0.00 segundos
# >>> tiempo('amigos4(5864660, 7489324)')
# 0.00 segundos
# >>> tiempo('amigos5(5864660, 7489324)')
# 0.00 segundos
#
# >>> x = 42262694537514864075544955198125
# >>> y = 42405817271188606697466971841875
# >>> tiempo('amigos2(x, y)')
# 0.10 segundos
# >>> tiempo('amigos3(x, y)')
# 0.00 segundos
# >>> tiempo('amigos4(x, y)')
# 0.00 segundos
# >>> tiempo('amigos5(x, y)')
1420 Ejercicios de programación con Python
# 0.00 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
primos2 :: [Integer]
primos2 = criba [2..]
where criba (p:ps) = p : criba [n | n <- ps, mod n p /= 0]
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG primosEquidistantes1
describe ”def. 2” $ specG primosEquidistantes2
describe ”def. 3” $ specG primosEquidistantes3
describe ”def. 4” $ specG primosEquidistantes4
describe ”def. 5” $ specG primosEquidistantes5
-- La verificación es
-- λ> verifica
-- 20 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_primosEquidistantes :: Int -> Integer -> Bool
prop_primosEquidistantes n k =
all (== take n (primosEquidistantes1 k))
[take n (f k) | f <- [primosEquidistantes2,
primosEquidistantes3,
primosEquidistantes4,
primosEquidistantes5]]
Capítulo 16. Miscelánea 1423
-- La comprobación es
-- λ> prop_primosEquidistantes 100 4
-- True
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> primosEquidistantes1 4 !! 200
-- (9829,9833)
-- (2.60 secs, 1,126,458,272 bytes)
-- λ> primosEquidistantes2 4 !! 200
-- (9829,9833)
-- (0.44 secs, 249,622,048 bytes)
-- λ> primosEquidistantes3 4 !! 200
-- (9829,9833)
-- (0.36 secs, 207,549,592 bytes)
-- λ> primosEquidistantes4 4 !! 200
-- (9829,9833)
-- (0.02 secs, 4,012,848 bytes)
-- λ> primosEquidistantes5 4 !! 200
-- (9829,9833)
-- (0.01 secs, 7,085,072 bytes)
--
-- λ> primosEquidistantes2 4 !! 600
-- (41617,41621)
-- (5.67 secs, 3,340,313,480 bytes)
-- λ> primosEquidistantes3 4 !! 600
-- (41617,41621)
-- (5.43 secs, 3,090,994,096 bytes)
-- λ> primosEquidistantes4 4 !! 600
-- (41617,41621)
-- (0.03 secs, 15,465,824 bytes)
-- λ> primosEquidistantes5 4 !! 600
-- (41617,41621)
-- (0.04 secs, 28,858,232 bytes)
--
-- λ> primosEquidistantes4 4 !! (10^5)
-- (18467047,18467051)
1424 Ejercicios de programación con Python
16.27.2. En Python
# ---------------------------------------------------------------------
# Definir la función
# primosEquidistantes : (int) -> list[tuple[int,int]]
# tal que primosEquidistantes(k) es la lista de los pares de primos
# cuya diferencia es k. Por ejemplo,
# >>> list(islice(primosEquidistantes(2), 3))
# [(3, 5), (5, 7), (11, 13)]
# >>> list(islice(primosEquidistantes(4), 3))
# [(7, 11), (13, 17), (19, 23)]
# >>> list(islice(primosEquidistantes(6), 3))
# [(23, 29), (31, 37), (47, 53)]
# >>> list(islice(primosEquidistantes(8), 3))
# [(89, 97), (359, 367), (389, 397)]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_primosEquidestantes()
# Verificado
# Comprobación de equivalencia
1426 Ejercicios de programación con Python
# ============================
# La propiedad es
def primosEquidistantes_equiv(n: int, k: int) -> bool:
return list(islice(primosEquidistantes1(k), n)) == \
list(islice(primosEquidistantes2(k), n))
# La comprobación es
# >>> primosEquidistantes_equiv(100, 4)
# True
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('list(islice(primosEquidistantes1(4), 300))')
# 3.19 segundos
# >>> tiempo('list(islice(primosEquidistantes2(4), 300))')
# 0.01 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
posicion2 t =
head [n | (n,t') <- zip [0..] ternas, t' == t]
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG posicion1
describe ”def. 2” $ specG posicion2
describe ”def. 3” $ specG posicion3
describe ”def. 4” $ specG posicion4
describe ”def. 5” $ specG posicion5
-- La verificación es
-- λ> verifica
-- 15 examples, 0 failures
-- Equivalencia
-- ============
-- La propiedad es
prop_posicion_equiv :: NonNegative Int
-> NonNegative Int
-> NonNegative Int
-> Bool
prop_posicion_equiv (NonNegative x) (NonNegative y) (NonNegative z) =
all (== posicion1 (x,y,z))
[f (x,y,z) | f <- [ posicion2
, posicion3
, posicion4
, posicion5 ]]
-- La comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_posicion_equiv
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> posicion1 (147,46,116)
-- 5000000
-- (5.84 secs, 2,621,428,184 bytes)
-- λ> posicion2 (147,46,116)
1430 Ejercicios de programación con Python
-- 5000000
-- (3.63 secs, 2,173,230,200 bytes)
-- λ> posicion3 (147,46,116)
-- 5000000
-- (2.48 secs, 1,453,229,880 bytes)
-- λ> posicion4 (147,46,116)
-- 5000000
-- (1.91 secs, 1,173,229,840 bytes)
-- λ> posicion5 (147,46,116)
-- 5000000
-- (1.94 secs, 1,173,229,960 bytes)
-- Propiedades
-- ===========
-- La 1ª propiedad es
prop_posicion1 :: NonNegative Int -> Bool
prop_posicion1 (NonNegative x) =
posicion5 (x,0,0) == x * (x^2 + 6*x + 11) `div` 6
-- Su comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_posicion1
-- +++ OK, passed 100 tests.
-- La 2ª propiedad es
prop_posicion2 :: NonNegative Int -> Bool
prop_posicion2 (NonNegative y) =
posicion5 (0,y,0) == y * (y^2 + 3*y + 8) `div` 6
-- Su comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_posicion2
-- +++ OK, passed 100 tests.
-- La 3ª propiedad es
prop_posicion3 :: NonNegative Int -> Bool
prop_posicion3 (NonNegative z) =
posicion5 (0,0,z) == z * (z^2 + 3*z + 2) `div` 6
-- Su comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_posicion3
Capítulo 16. Miscelánea 1431
-- La 4ª propiedad es
prop_posicion4 :: NonNegative Int -> Bool
prop_posicion4 (NonNegative x) =
posicion5 (x,x,x) == x * (9 * x^2 + 14 * x + 7) `div` 2
-- Su comprobación es
-- λ> quickCheckWith (stdArgs {maxSize=20}) prop_posicion4
-- +++ OK, passed 100 tests.
16.28.2. En Python
# ---------------------------------------------------------------------
# Las ternas de números naturales se pueden ordenar como sigue
# (0,0,0),
# (0,0,1),(0,1,0),(1,0,0),
# (0,0,2),(0,1,1),(0,2,0),(1,0,1),(1,1,0),(2,0,0),
# (0,0,3),(0,1,2),(0,2,1),(0,3,0),(1,0,2),(1,1,1),(1,2,0),(2,0,1),...
# ...
#
# Definir la función
# posicion :: (Int,Int,Int) -> Int
# tal que (posicion (x,y,z)) es la posición de la terna de números
# naturales (x,y,z) en la ordenación anterior. Por ejemplo,
# posicion (0,1,0) == 2
# posicion (0,0,2) == 4
# posicion (0,1,1) == 5
#
# Comprobar con QuickCheck que
# + la posición de (x,0,0) es x(x²+6x+11)/6
# + la posición de (0,y,0) es y(y²+3y+ 8)/6
# + la posición de (0,0,z) es z(z²+3z+ 2)/6
# + la posición de (x,x,x) es x(9x²+14x+7)/2
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
Capítulo 16. Miscelánea 1433
# La verificación es
# >>> test_posicion()
# Verificado
# Equivalencia
# ============
@given(st.integers(min_value=1, max_value=10),
st.integers(min_value=1, max_value=10),
st.integers(min_value=1, max_value=10))
def test_posicion_equiv(x: int, y: int, z: int) -> None:
r = posicion1((x, y, z))
assert posicion2((x, y, z)) == r
assert posicion3((x, y, z)) == r
# La comprobación es
# >>> test_posicion_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('posicion1((147,46,116))')
# 0.72 segundos
1434 Ejercicios de programación con Python
# >>> tiempo('posicion2((147,46,116))')
# 0.68 segundos
# >>> tiempo('posicion3((147,46,116))')
# 0.93 segundos
# Propiedades
# ===========
# La 1ª propiedad es
@given(st.integers(min_value=1, max_value=100))
def prop_posicion1(x: int) -> None:
assert posicion1((x,0,0)) == x * (x**2 + 6*x + 11) // 6
# Su comprobación es
# >>> prop_posicion1()
# >>>
# La 2ª propiedad es
@given(st.integers(min_value=1, max_value=100))
def prop_posicion2(y: int) -> None:
assert posicion1((0,y,0)) == y * (y**2 + 3*y + 8) // 6
# Su comprobación es
# >>> prop_posicion2()
# >>>
# La 3ª propiedad es
@given(st.integers(min_value=1, max_value=100))
def prop_posicion3(z: int) -> None:
assert posicion1((0,0,z)) == z * (z**2 + 3*z + 2) // 6
# Su comprobación es
# >>> prop_posicion3()
# >>>
# La 4ª propiedad es
@given(st.integers(min_value=1, max_value=10))
def prop_posicion4(x: int) -> None:
assert posicion1((x,x,x)) == x * (9 * x**2 + 14 * x + 7) // 2
Capítulo 16. Miscelánea 1435
# Su comprobación es
# >>> prop_posicion4()
# >>>
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
triangulares2 :: [Integer]
triangulares2 = [(n*(n+1)) `div` 2 | n <- [1..]]
-- 3ª solución
Capítulo 16. Miscelánea 1437
-- ===========
triangulares3 :: [Integer]
triangulares3 = 1 : [x+y | (x,y) <- zip [2..] triangulares3]
-- 4ª solución
-- ===========
triangulares4 :: [Integer]
triangulares4 = 1 : zipWith (+) [2..] triangulares4
-- 5ª solución
-- ===========
triangulares5 :: [Integer]
triangulares5 = scanl (+) 1 [2..]
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
it ”e1” $
take 6 (triangularesConCifras 1) `shouldBe` [1,3,6,55,66,666]
it ”e2” $
take 6 (triangularesConCifras 2) `shouldBe` [10,15,21,28,36,45]
it ”e3” $
take 6 (triangularesConCifras 3) `shouldBe` [105,120,136,153,190,210]
it ”e4” $
take 5 (triangularesConCifras 4) `shouldBe` [1035,1275,1326,1378,1485]
spec :: Spec
spec = do
describe ”def. 1” $ specG triangularesConCifras1
describe ”def. 2” $ specG triangularesConCifras2
describe ”def. 3” $ specG triangularesConCifras3
describe ”def. 4” $ specG triangularesConCifras4
describe ”def. 5” $ specG triangularesConCifras5
-- La verificación es
-- λ> verifica
-- 20 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La 1ª propiedad es
prop_triangularesConCifras1 :: Bool
prop_triangularesConCifras1 =
[take 2 (triangularesConCifras1 n) | n <- [1..7]] ==
[take 2 (triangularesConCifras2 n) | n <- [1..7]]
-- La comprobación es
-- λ> prop_triangularesConCifras1
-- True
-- La 2ª propiedad es
prop_triangularesConCifras2 :: Int -> Bool
prop_triangularesConCifras2 n =
all (== take 5 (triangularesConCifras2 n'))
[take 5 (triangularesConCifras3 n'),
take 5 (triangularesConCifras4 n'),
Capítulo 16. Miscelánea 1439
-- La comprobación es
-- λ> quickCheck prop_triangularesConCifras
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> (triangularesConCifras1 3) !! 220
-- 5456556
-- (2.48 secs, 1,228,690,120 bytes)
-- λ> (triangularesConCifras2 3) !! 220
-- 5456556
-- (0.01 secs, 4,667,288 bytes)
--
-- λ> (triangularesConCifras2 3) !! 600
-- 500010500055
-- (1.76 secs, 1,659,299,872 bytes)
-- λ> (triangularesConCifras3 3) !! 600
-- 500010500055
-- (1.67 secs, 1,603,298,648 bytes)
-- λ> (triangularesConCifras4 3) !! 600
-- 500010500055
-- (1.20 secs, 1,507,298,248 bytes)
-- λ> (triangularesConCifras5 3) !! 600
-- 500010500055
-- (1.15 secs, 1,507,298,256 bytes)
-- ---------------------------------------------------------------------
-- § Referencias --
-- ---------------------------------------------------------------------
16.29.2. En Python
# ---------------------------------------------------------------------
# Los números triangulares se forman como sigue
#
# * * *
# * * * *
# * * *
# 1 3 6
#
# La sucesión de los números triangulares se obtiene sumando los
# números naturales. Así, los 5 primeros números triangulares son
# 1 = 1
# 3 = 1 + 2
# 6 = 1 + 2 + 3
# 10 = 1 + 2 + 3 + 4
# 15 = 1 + 2 + 3 + 4 + 5
#
# Definir la función
# triangularesConCifras :: Int -> [Integer]
# tal que (triangularesConCifras n) es la lista de los números
# triangulares con n cifras distintas. Por ejemplo,
# take 6 (triangularesConCifras 1) == [1,3,6,55,66,666]
# take 6 (triangularesConCifras 2) == [10,15,21,28,36,45]
# take 6 (triangularesConCifras 3) == [105,120,136,153,190,210]
# take 5 (triangularesConCifras 4) == [1035,1275,1326,1378,1485]
# take 2 (triangularesConCifras 10) == [1062489753,1239845706]
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
Capítulo 16. Miscelánea 1441
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
yield x
# Verificación
# ============
# La verificación es
# >>> test_triangularesConCifras()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('list(islice(triangularesConCifras1(3), 300))')
# 11.18 segundos
# >>> tiempo('list(islice(triangularesConCifras2(3), 300))')
# 0.03 segundos
# >>> tiempo('list(islice(triangularesConCifras3(3), 300))')
Capítulo 16. Miscelánea 1443
# 0.03 segundos
#
# >>> tiempo('list(islice(triangularesConCifras2(3), 700))')
# 2.19 segundos
# >>> tiempo('list(islice(triangularesConCifras3(3), 700))')
# 2.01 segundos
-- [(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ª solución
-- ===========
-- 2ª solución
-- ===========
-- Verificación
-- ============
Capítulo 16. Miscelánea 1445
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG posicionesDiagonalesPrincipales1
describe ”def. 2” $ specG posicionesDiagonalesPrincipales2
-- La verificación es
-- λ> verifica
-- 6 examples, 0 failures
1446 Ejercicios de programación con Python
-- La propiedad es
prop_posicionesDiagonalesPrincipales_equiv :: Positive Int -> Positive Int -> Boo
prop_posicionesDiagonalesPrincipales_equiv (Positive m) (Positive n) =
posicionesDiagonalesPrincipales1 m n ==
posicionesDiagonalesPrincipales2 m n
-- La comprobación es
-- λ> quickCheck prop_posicionesDiagonalesPrincipales_equiv
-- +++ 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)
16.30.2. En Python
# ---------------------------------------------------------------------
# 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
Capítulo 16. Miscelánea 1447
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
[(1,3)]]
print(”Verificado”)
# La verificación es
# >>> test_posicionesDiagonalesPrincipales()
# Verificado
# 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
# =========================
# La comparación es
# >>> tiempo('posicionesDiagonalesPrincipales1(10**4, 2*10**3)')
# 3.32 segundos
# >>> tiempo('posicionesDiagonalesPrincipales2(10**4, 2*10**3)')
# 2.16 segundos
1450 Ejercicios de programación con Python
-- 1ª solución
-- ===========
-- 2ª solución
Capítulo 16. Miscelánea 1451
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG diagonalesPrincipales1
describe ”def. 2” $ specG diagonalesPrincipales2
-- La verificación es
-- λ> verifica
-- 2 examples, 0 failures
-- La propiedad es
prop_diagonalesPrincipales2 :: Positive Int -> Positive Int -> Bool
prop_diagonalesPrincipales2 (Positive m) (Positive n) =
diagonalesPrincipales1 p == diagonalesPrincipales2 p
1452 Ejercicios de programación con Python
-- La comprobación es
-- λ> quickCheck prop_diagonalesPrincipales2
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (diagonalesPrincipales1 (listArray ((1,1),(10^4,10^4)) [1..]))
-- 19999
-- (6.90 secs, 8,010,369,224 bytes)
-- λ> length (diagonalesPrincipales2 (listArray ((1,1),(10^4,10^4)) [1..]))
-- 19999
-- (6.78 secs, 8,008,289,224 bytes)
16.31.2. En Python
# ---------------------------------------------------------------------
# La lista de las diagonales principales de la matriz
# 1 2 3 4
# 5 6 7 8
# 9 10 11 12
# es
# [[9],[5,10],[1,6,11],[2,7,12],[3,8],[4]]
#
# Definir la función
# diagonalesPrincipales : (list[list[A]]) -> list[list[A]]
# tal que diagonalesPrincipales(p) es la lista de las diagonales
# principales de p. Por ejemplo,
# >>> diagonalesPrincipales([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
# [[9], [5, 10], [1, 6, 11], [2, 7, 12], [3, 8], [4]]
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
matriz = list[list[A]]
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_diagonalesPrincipales()
# Verificado
1454 Ejercicios de programación con Python
-- 1ª solución
-- ===========
esCuadrada p &&
all todosIguales (diagonalesPrincipales p)
-- [(4,1)]
-- [(3,1),(4,2)]
-- [(2,1),(3,2),(4,3)]
-- [(1,1),(2,2),(3,3)]
-- [(1,2),(2,3)]
-- [(1,3)]
posicionesDiagonalesPrincipales :: Int -> Int -> [[(Int, Int)]]
posicionesDiagonalesPrincipales m n =
[zip [i..m] [1..n] | i <- [m,m-1..1]] ++
[zip [1..m] [j..n] | j <- [2..n]]
-- 2ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG esToeplitz1
describe ”def. 2” $ specG esToeplitz2
-- La verificación es
-- λ> verifica
-- 4 examples, 0 failures
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> esToeplitz1 (listArray ((1,1),(2*10^3,2*10^3)) (repeat 1))
-- True
-- (2.26 secs, 2,211,553,888 bytes)
-- λ> esToeplitz2 (listArray ((1,1),(2*10^3,2*10^3)) (repeat 1))
-- True
-- (4.26 secs, 3,421,651,032 bytes)
16.32.2. En Python
# ---------------------------------------------------------------------
# Una [matriz de Toeplitz](https://bit.ly/3pqjY9D) es una matriz
# cuadrada que es constante a lo largo de las diagonales paralelas a la
# diagonal principal. Por ejemplo,
# |2 5 1 6| |2 5 1 6|
# |4 2 5 1| |4 2 6 1|
# |7 4 2 5| |7 4 2 5|
# |9 7 4 2| |9 7 4 2|
# la primera es una matriz de Toeplitz y la segunda no lo es.
#
# Las anteriores matrices se pueden definir por
# ej1 = [[2,5,1,6],[4,2,5,1],[7,4,2,5],[9,7,4,2]]
# ej2 = [[2,5,1,6],[4,2,6,1],[7,4,2,5],[9,7,4,2]]
#
# Definir la función
# esToeplitz : (list[list[A]]) -> bool
# tal que esToeplitz(p) se verifica si la matriz p es de Toeplitz. Por
1458 Ejercicios de programación con Python
# ejemplo,
# esToeplitz(ej1) == True
# esToeplitz(ej2) == False
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_esToeplitz()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('esToeplitz1([[1]*2*10**3]*2*10**3)')
# 1.52 segundos
# >>> tiempo('esToeplitz2([[1]*2*10**3]*2*10**3)')
# 0.51 segundos
--
-- Definir la función
-- diferenciaSimetrica :: Ord a => [a] -> [a] -> [a]
-- tal que (diferenciaSimetrica xs ys) es la diferencia simétrica de xs
-- e ys. Por ejemplo,
-- diferenciaSimetrica [2,5,3] [4,2,3,7] == [4,5,7]
-- diferenciaSimetrica [2,5,3] [5,2,3] == []
-- diferenciaSimetrica [2,5,2] [4,2,3,7] == [3,4,5,7]
-- diferenciaSimetrica [2,5,2] [4,2,4,7] == [4,5,7]
-- diferenciaSimetrica [2,5,2,4] [4,2,4,7] == [5,7]
-- ---------------------------------------------------------------------
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
Capítulo 16. Miscelánea 1461
-- 4ª solución
-- ===========
-- 5ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG diferenciaSimetrica1
describe ”def. 2” $ specG diferenciaSimetrica2
describe ”def. 3” $ specG diferenciaSimetrica3
describe ”def. 4” $ specG diferenciaSimetrica4
describe ”def. 5” $ specG diferenciaSimetrica5
-- La verificación es
-- λ> verifica
-- 25 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_diferenciaSimetrica :: [Int] -> [Int] -> Bool
prop_diferenciaSimetrica xs ys =
all (== diferenciaSimetrica1 xs ys)
[diferenciaSimetrica2 xs ys,
diferenciaSimetrica3 xs ys,
diferenciaSimetrica4 xs ys,
diferenciaSimetrica5 xs ys]
-- La comprobación es
-- λ> quickCheck prop_diferenciaSimetrica
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> length (diferenciaSimetrica1 [1..2*10^4] [2,4..2*10^4])
-- 10000
-- (2.34 secs, 10,014,360 bytes)
-- λ> length (diferenciaSimetrica2 [1..2*10^4] [2,4..2*10^4])
-- 10000
-- (2.41 secs, 8,174,264 bytes)
-- λ> length (diferenciaSimetrica3 [1..2*10^4] [2,4..2*10^4])
-- 10000
Capítulo 16. Miscelánea 1463
16.33.2. En Python
# ---------------------------------------------------------------------
# La [diferencia simétrica](http://bit.ly/1Rdcqxs) de dos conjuntos es
# el conjunto cuyos elementos son aquellos que pertenecen a alguno de
# los conjuntos iniciales, sin pertenecer a ambos a la vez. Por
# ejemplo, la diferencia simétrica de {2,5,3} y {4,2,3,7} es {5,4,7}.
#
# Definir la función
# diferenciaSimetrica :: Ord a => [a] -> [a] -> [a]
# tal que (diferenciaSimetrica xs ys) es la diferencia simétrica de xs
# e ys. Por ejemplo,
# diferenciaSimetrica [2,5,3] [4,2,3,7] == [4,5,7]
# diferenciaSimetrica [2,5,3] [5,2,3] == []
# diferenciaSimetrica [2,5,2] [4,2,3,7] == [3,4,5,7]
# diferenciaSimetrica [2,5,2] [4,2,4,7] == [4,5,7]
# diferenciaSimetrica [2,5,2,4] [4,2,4,7] == [5,7]
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_diferenciaSimetrica()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers()),
st.lists(st.integers()))
def test_diferenciaSimetrica_equiv(xs: list[int], ys: list[int]) -> None:
assert set(diferenciaSimetrica1(xs, ys)) ==\
set(diferenciaSimetrica2(xs, ys)) ==\
set(diferenciaSimetrica3(xs, ys)) ==\
set(diferenciaSimetrica4(xs, ys)) ==\
set(diferenciaSimetrica5(xs, ys))
# La comprobación es
# >>> test_diferenciaSimetrica_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('diferenciaSimetrica1(list(range(1,1+2*10**4)), list(range(2,1+2*
# 1.62 segundos
# >>> tiempo('diferenciaSimetrica2(list(range(1,1+2*10**4)), list(range(2,1+2*
# 1.60 segundos
1466 Ejercicios de programación con Python
-- 1ª solución
-- ===========
Capítulo 16. Miscelánea 1467
-- 2ª solución
-- ===========
-- Verificación
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG primosRelativos1
describe ”def. 2” $ specG primosRelativos2
-- La verificación es
-- λ> verifica
-- 12 examples, 0 failures
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_primosRelativos :: [Positive Int] -> Bool
prop_primosRelativos xs =
primosRelativos1 ys == primosRelativos2 ys
where ys = getPositive <$> xs
-- La comprobación es
-- λ> quickCheck prop_primosRelativos
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> primosRelativos1 (take 2000 primes)
-- True
-- (1.43 secs, 1,730,437,768 bytes)
-- λ> primosRelativos2 (take 2000 primes)
-- True
-- (0.99 secs, 1,490,445,736 bytes)
16.34.2. En Python
# ---------------------------------------------------------------------
# Dos números enteros positivos son [primos relativos](http://bit.ly/1xgqDTK)
# si no tienen ningún factor primo en común; es decir, si 1 es su único
Capítulo 16. Miscelánea 1469
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_primosRelativos()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.lists(st.integers(min_value=1, max_value=1000)))
def test_primosRelativos_equiv(xs: list[int]) -> None:
assert primosRelativos1(xs) == primosRelativos2(xs)
# La comprobación es
# >>> test_primosRelativos_equiv()
Capítulo 16. Miscelánea 1471
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('primosRelativos1(list(primerange(40000)))')
# 2.20 segundos
# >>> tiempo('primosRelativos2(list(primerange(40000)))')
# 1.82 segundos
-- 1ª solución
-- ===========
-- 2ª solución
-- ===========
-- 3ª solución
-- ===========
-- 4ª solución
-- ===========
-- Verificación
1474 Ejercicios de programación con Python
-- ============
verifica :: IO ()
verifica = hspec spec
spec :: Spec
spec = do
describe ”def. 1” $ specG descomposicionesTriangulares1
describe ”def. 2” $ specG descomposicionesTriangulares2
describe ”def. 3” $ specG descomposicionesTriangulares3
describe ”def. 4” $ specG descomposicionesTriangulares4
-- La verificación es
-- λ> verifica
-- 28 examples, 0 failures
Capítulo 16. Miscelánea 1475
-- Comprobación de equivalencia
-- ============================
-- La propiedad es
prop_descomposicionesTriangulares_equiv :: Positive Int -> Bool
prop_descomposicionesTriangulares_equiv (Positive n) =
all (== descomposicionesTriangulares1 n)
[descomposicionesTriangulares2 n,
descomposicionesTriangulares3 n,
descomposicionesTriangulares4 n]
-- La comprobación es
-- λ> quickCheck prop_descomposicionesTriangulares_equiv
-- +++ OK, passed 100 tests.
-- Comparación de eficiencia
-- =========================
-- La comparación es
-- λ> last (descomposicionesTriangulares1 (2*10^4))
-- (5671,6328,8001)
-- (3.34 secs, 1,469,517,168 bytes)
-- λ> last (descomposicionesTriangulares2 (2*10^4))
-- (5671,6328,8001)
-- (1.29 secs, 461,433,928 bytes)
-- λ> last (descomposicionesTriangulares3 (2*10^4))
-- (5671,6328,8001)
-- (0.08 secs, 6,574,056 bytes)
--
-- λ> last (descomposicionesTriangulares3 (5*10^5))
-- (140185,148240,211575)
-- (2.12 secs, 151,137,280 bytes)
-- λ> last (descomposicionesTriangulares4 (5*10^5))
-- (140185,148240,211575)
-- (2.30 secs, 103,280,216 bytes)
16.35.2. En Python
# ---------------------------------------------------------------------
# Los números triangulares se forman como sigue
1476 Ejercicios de programación con Python
#
# * * *
# * * * *
# * * *
# 1 3 6
#
# La sucesión de los números triangulares se obtiene sumando los
# números naturales. Así, los 5 primeros números triangulares son
# 1 = 1
# 3 = 1 + 2
# 6 = 1 + 2 + 3
# 10 = 1 + 2 + 3 + 4
# 15 = 1 + 2 + 3 + 4 + 5
#
# Definir la función
# descomposicionesTriangulares : (int) -> list[tuple[int, int, int]]
# tal que descomposicionesTriangulares(n) es la lista de las
# ternas correspondientes a las descomposiciones de n en tres sumandos,
# como máximo, formados por números triangulares. Por ejemplo,
# >>> descomposicionesTriangulares4(4)
# []
# >>> descomposicionesTriangulares4(5)
# [(1,1,3)]
# >>> descomposicionesTriangulares4(12)
# [(1,1,10),(3,3,6)]
# >>> descomposicionesTriangulares4(30)
# [(1,1,28),(3,6,21),(10,10,10)]
# >>> descomposicionesTriangulares4(61)
# [(1,15,45),(3,3,55),(6,10,45),(10,15,36)]
# >>> descomposicionesTriangulares4(52)
# [(1,6,45),(1,15,36),(3,21,28),(6,10,36),(10,21,21)]
# >>> descomposicionesTriangulares4(82)
# [(1,3,78),(1,15,66),(1,36,45),(6,10,66),(6,21,55),(10,36,36)]
# >>> len(descomposicionesTriangulares4(5*10**5))
# 124
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_descomposicionesTriangulares()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(st.integers(min_value=1, max_value=1000))
def test_descomposicionesTriangulares_equiv(n: int) -> None:
r = descomposicionesTriangulares1(n)
assert descomposicionesTriangulares2(n) == r
assert descomposicionesTriangulares3(n) == r
assert descomposicionesTriangulares4(n) == r
1480 Ejercicios de programación con Python
# La comprobación es
# >>> test_descomposicionesTriangulares_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('descomposicionesTriangulares1(6*10**4)[-1]')
# 2.16 segundos
# >>> tiempo('descomposicionesTriangulares2(6*10**4)[-1]')
# 2.05 segundos
# >>> tiempo('descomposicionesTriangulares3(6*10**4)[-1]')
# 1.04 segundos
# >>> tiempo('descomposicionesTriangulares4(6*10**4)[-1]')
# 0.10 segundos
Apéndices
1481
Apéndice A
1483
1484 Ejercicios de programación con Python
¿Has empleado todos los datos? ¿Has empleado toda la condición? ¿Has
considerado todas las nociones esenciales concernientes al problema?
¿Puede resolver una parte del problema? ¿Puedes deducir algún elemen-
to útil de los datos? ¿Puedes pensar en algunos otros datos apropiados
para determinar la incógnita? ¿Puedes cambiar la incógnita? ¿Puedes
cambiar la incógnita o los datos, o ambos si es necesario, de tal forma
que estén más cercanos entre sí?
¿Has empleado todos los datos? ¿Has empleado todas las restricciones
sobre los datos? ¿Has considerado todas los requisitos de la especifica-
ción?
1486 Ejercicios de programación con Python
[1] C. Allen, J. Moronuki, and S. Syrek. Haskell programming from first prin-
ciples. Lorepub LLC, 2016.
[9] R. Bird and J. Gibbons. Algorithm design with Haskell. Cambridge Uni-
versity Press, 2020.
1487
1488 Ejercicios de programación con Python
[14] T. Hall and J. Stacey. Python 3 for absolute beginners. Apress, 2010.
[30] A. Saha. Doing Math with Python: Use Programming to explore algebra,
statistics, calculus, and more! No Starch Press, 2015.
[37] R. van Hattem. Mastering Python: Write powerful and efficient code
using the full range of Python’s capabilities. Packt Publishing, 2022.