Ejercicios de Python: Números Lychrel
Ejercicios de Python: Números Lychrel
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 [Link]
5/es/ o envie una carta a Creative Commons, 559 Nathan Abbott Way, Stan-
ford, California 94305, USA.
Índice general
II Algorítmica 153
6 El tipo abstracto de datos de las pilas 155
6.1 El tipo abstracto de datos (TAD) de las pilas . . . . . . . . . . . .155
6.2 Implementación del TAD de las pilas mediante listas . . . . . . .156
6.3 Implementación del TAD de las pilas mediante deque . . . . . .161
3
4 Ejercicios de programación con Python
17 Miscelánea 549
17.1 Números de Pentanacci . . . . . . . . . . . . . . . . . . . . . . . .549
17.2 El teorema de Navidad de Fermat . . . . . . . . . . . . . . . . . .552
17.3 Números primos de Hilbert . . . . . . . . . . . . . . . . . . . . . .558
17.4 Factorizaciones de números de Hilbert . . . . . . . . . . . . . . .561
17.5 Representaciones de un número como suma de dos cuadrados 565
17.6 La serie de Thue-Morse . . . . . . . . . . . . . . . . . . . . . . . .569
17.7 La sucesión de Thue-Morse . . . . . . . . . . . . . . . . . . . . . .570
17.8 Huecos maximales entre primos . . . . . . . . . . . . . . . . . . .574
17.9 La función indicatriz de Euler . . . . . . . . . . . . . . . . . . . . .577
17.10 Ceros finales del factorial . . . . . . . . . . . . . . . . . . . . . . .580
17.11 Primos cubanos . . . . . . . . . . . . . . . . . . . . . . . . . . . . .583
17.12 Cuadrado más cercano . . . . . . . . . . . . . . . . . . . . . . . .586
17.13 Suma de cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . .590
17.14 Sistema factorádico de numeración . . . . . . . . . . . . . . . . .592
17.15 Duplicación de cada elemento . . . . . . . . . . . . . . . . . . . .598
17.16 Suma de fila del triángulo de los impares . . . . . . . . . . . . .600
17.17 Reiteración de suma de consecutivos . . . . . . . . . . . . . . . .602
17.18 Producto de los elementos de la diagonal principal . . . . . . . .605
17.19 Reconocimiento de potencias de 4 . . . . . . . . . . . . . . . . . .607
17.20 Exponente en la factorización . . . . . . . . . . . . . . . . . . . .611
17.21 Mayor órbita de la sucesión de Collatz . . . . . . . . . . . . . . .613
17.22 Máximos locales . . . . . . . . . . . . . . . . . . . . . . . . . . . . .618
Bibliografía 620
Introducción
1
[Link]
7
8 Ejercicios de programación con Python
Parte I
Introducción a la programación
con Python
9
Capítulo 1
Definiciones elementales de
funciones
# ----------------------------------------------------------------------
# Cabecera
# ----------------------------------------------------------------------
A = TypeVar('A')
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# media3 : (float, float, float) -> float
11
12 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# sumaMonedas : (int, int, int, int, int) -> int
# tal que sumaMonedas(a, b, c, d, e) es la suma de los euros
# correspondientes a a monedas de 1 euro, b de 2 euros, c de 5 euros, d
# 10 euros y e de 20 euros. Por ejemplo,
# sumaMonedas(0, 0, 0, 0, 1) == 20
# sumaMonedas(0, 0, 8, 0, 3) == 100
# sumaMonedas(1, 1, 1, 1, 1) == 38
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. 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,
Capítulo 1. Definiciones elementales de funciones 13
# areaDeCoronaCircular(1, 2) == 9.42477796076938
# areaDeCoronaCircular(2, 5) == 65.97344572538566
# areaDeCoronaCircular(3, 5) == 50.26548245743669
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. 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']
# ---------------------------------------------------------------------
14 Ejercicios de programación con Python
# 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:]
[Link](xs[0])
return ys
# 3ª solución
def rota1c(xs: list[A]) -> list[A]:
if xs == []:
return []
y, *ys = xs
return ys + [y]
# ---------------------------------------------------------------------
# Ejercicio 8. 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11. 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]
# ---------------------------------------------------------------------
# 1ª solución
def interior1(xs: list[A]) -> list[A]:
return xs[1:][:-1]
# 2ª solución
def interior2(xs: list[A]) -> list[A]:
return xs[1:-1]
16 Ejercicios de programación con Python
# La propiedad de equivalencia es
@given([Link]([Link]()))
def test_interior(xs):
assert interior1(xs) == interior2(xs)
# ---------------------------------------------------------------------
# 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]
# ---------------------------------------------------------------------
# 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]:
ys = list(reversed(xs))
return list(reversed(ys[:n]))
# La propiedad de equivalencia es
@given([Link](min_value=0), [Link]([Link]()))
def test_equiv_finales(n, xs):
assert finales1(n, xs) == finales2(n, xs) == finales3(n, xs)
# ---------------------------------------------------------------------
# Ejercicio 13. 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]
# segmento(3, 5, [3, 4, 1, 2, 7, 9, 0]) == [1, 2, 7]
# segmento(5, 3, [3, 4, 1, 2, 7, 9, 0]) == []
# ---------------------------------------------------------------------
# 1ª definición
def segmento1(m: int, n: int, xs: list[A]) -> list[A]:
ys = xs[:n]
return ys[m - 1:]
# 2ª definición
def segmento2(m: int, n: int, xs: list[A]) -> list[A]:
return xs[m-1:n]
# La propiedad de equivalencia es
@given([Link](), [Link](), [Link]([Link]()))
def test_equiv_segmento(m, n, xs):
assert segmento1(m, n, xs) == segmento2(m, n, xs)
# ---------------------------------------------------------------------
# Ejercicio 14. 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 15. Definir la función
# mediano : (int, int, int) -> int
18 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 16. 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ª solución
def tresIguales1(x: int, y: int, z: int) -> bool:
return x == y and y == z
# 2ª solución
def tresIguales2(x: int, y: int, z: int) -> bool:
return x == y == z
# La propiedad de equivalencia es
@given([Link](), [Link](), [Link]())
def test_equiv_tresIguales(x, y, z):
assert tresIguales1(x, y, z) == tresIguales2(x, y, z)
# ---------------------------------------------------------------------
# Ejercicio 17. 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,
Capítulo 1. Definiciones elementales de funciones 19
# tresDiferentes(3, 5, 2) == True
# tresDiferentes(3, 5, 3) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 18. Definir la función
# cuatroIguales : (int, int, int, int) -> bool
# tal que cuatroIguales(x,y,z,u) se verifica si los elementos x, y, z y
# u son iguales. Por ejemplo,
# cuatroIguales(5, 5, 5, 5) == True
# cuatroIguales(5, 5, 4, 5) == False
# ---------------------------------------------------------------------
# 1ª solución
def cuatroIguales1(x: int, y: int, z: int, u: int) -> bool:
return x == y and tresIguales1(y, z, u)
# 2ª solución
def cuatroIguales2(x: int, y: int, z: int, u: int) -> bool:
return x == y == z == u
# La propiedad de equivalencia es
@given([Link](), [Link](), [Link](), [Link]())
def test_equiv_cuatroIguales(x, y, z, u):
assert cuatroIguales1(x, y, z, u) == cuatroIguales2(x, y, z, u)
# ---------------------------------------------------------------------
# Cabecera
# ---------------------------------------------------------------------
A = TypeVar('A')
B = TypeVar('B')
# ---------------------------------------------------------------------
# Ejercicio 1. 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
Capítulo 1. Definiciones elementales de funciones 21
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([Link](allow_nan=False, allow_infinity=False),
[Link](allow_nan=False, allow_infinity=False))
def test_equiv_divisionSegura(x, y):
assert divisionSegura1(x, y) == divisionSegura2(x, y)
# ---------------------------------------------------------------------
# Ejercicio 2. 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
# True | False | True
# False | True | True
# False | False | False
#
# Definir la función
# xor : (bool, bool) -> bool
# tal que xor(x, y) es la disyunción excluyente de x e y. Por ejemplo,
# xor(True, True) == False
# xor(True, False) == True
# xor(False, True) == True
# xor(False, False) == False
# ---------------------------------------------------------------------
# 1ª solución
def xor1(x, y):
match x, y:
22 Ejercicios de programación con Python
# 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)
# 5ª solución
def xor5(x: bool, y: bool) -> bool:
return x != y
# La propiedad de equivalencia es
@given([Link](), [Link]())
def test_equiv_xor(x, y):
assert xor1(x, y) == xor2(x, y) == xor3(x, y) == xor4(x, y) == xor5(x, y)
# ---------------------------------------------------------------------
# Ejercicio 3. 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,
Capítulo 1. Definiciones elementales de funciones 23
# ---------------------------------------------------------------------
# Ejercicio 4. 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 esidempotente; es
# decir, si se aplica dos veces es lo mismo que no aplicarla ninguna.
# ---------------------------------------------------------------------
# La propiedad de es
@given([Link]([Link](), [Link]()))
def test_equiv_intercambia(p):
assert intercambia(intercambia(p)) == p
# ---------------------------------------------------------------------
# Ejercicio 5. 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
24 Ejercicios de programación con Python
# La propiedad es
cota = 2 ** 30
@given([Link]([Link](min_value=0, max_value=cota),
[Link](min_value=0, max_value=cota)),
[Link]([Link](min_value=0, max_value=cota),
[Link](min_value=0, max_value=cota)),
[Link]([Link](min_value=0, max_value=cota),
[Link](min_value=0, max_value=cota)))
def test_triangular(p1, p2, p3):
assert distancia(p1, p3) <= distancia(p1, p2) + distancia(p2, p3)
# ---------------------------------------------------------------------
# Ejercicio 6. 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.
# ---------------------------------------------------------------------
# La propiedad de es
@given([Link]([Link]()))
def test_equiv_ciclo(xs):
assert len(ciclo(xs)) == len(xs)
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# numeroMayor : (int, int) -> int
# tal que numeroMayor(x, y) es el mayor número de dos cifras que puede
# construirse con los dígitos x e y. Por ejemplo,
# numeroMayor(2, 5) == 52
# numeroMayor(5, 2) == 52
# ---------------------------------------------------------------------
# 1ª definición
def numeroMayor1(x: int, y: int) -> int:
return 10 * max(x, y) + min(x, y)
26 Ejercicios de programación con Python
# 2ª definición
def numeroMayor2(x: int, y: int) -> int:
if x > y:
return 10 * x + y
return 10 * y + x
# ---------------------------------------------------------------------
# Ejercicio 8. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. 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) == []
Capítulo 1. Definiciones elementales de funciones 27
#
# 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([Link](min_value=-100, max_value=100),
[Link](min_value=-100, max_value=100),
[Link](min_value=-100, max_value=100))
def test_prop_raices(a, b, c):
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)
# ---------------------------------------------------------------------
# Ejercicio 10. 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
28 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 11. 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]
if i1 and i2:
[a1, b1] = i1
[a2, b2] = i2
a = max(a1, a2)
b = min(b1, b2)
if a <= b:
return [a, b]
return []
return []
# La propiedad es
@given([Link](), [Link](), [Link](), [Link]())
def test_prop_raices2(a1, b1, a2, b2):
assume(a1 <= b1 and a2 <= b2)
assert interseccion([a1, b1], [a2, b2]) == interseccion([a2, b2], [a1, b1])
# ---------------------------------------------------------------------
# Ejercicio 12.1. 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 la función
# formaReducida : (Racional) -> Racional
# tal que formaReducida(x) es la forma reducida del número racional
# x. Por ejemplo,
# formaReducida((4, 10)) == (2, 5)
# formaReducida((0, 5)) == (0, 1)
# ---------------------------------------------------------------------
c = gcd(a, b)
return (a // c, b // c)
# ---------------------------------------------------------------------
# Ejercicio 12.2. Definir la función
# sumaRacional : (Racional, Racional) -> Racional
# tal que 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)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12.3. Definir la función
# productoRacional : (Racional, Racional) -> Racional
# tal que 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)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12.4. Definir la función
# igualdadRacional : (Racional, Racional) -> bool
# tal que 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
Capítulo 1. Definiciones elementales de funciones 31
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12.5. Comprobar con Hypothesis la propiedad distributiva del
# producto racional respecto de la suma.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link](), [Link]()),
[Link]([Link](), [Link]()),
[Link]([Link](), [Link]()))
def test_prop_distributiva(x, y, z):
(_, 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),
productoRacional(x, z)))
# ---------------------------------------------------------------------
# Comprobación de propiedades.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Librerías auxiliares --
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
# ---------------------------------------------------------------------
# Ejercicio 1.1. (Problema 6 del proyecto Euler) En los distintos
33
34 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_suma(n: int) -> None:
assert suma1(n) == suma2(n)
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('suma1(10**8)')
# 1.55 segundos
# >>> tiempo('suma2(10**8)')
# 0.00 segundos
# ---------------------------------------------------------------------
# Ejercicio 1.2. Definir, por comprensión, 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(sumaDeCuadrados2(10**100))) == 300
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_sumaDeCuadrados(n: int) -> None:
assert sumaDeCuadrados1(n) == sumaDeCuadrados2(n)
# Comparación de eficiencia
# =========================
36 Ejercicios de programación con Python
# La comparación es
# >>> tiempo('sumaDeCuadrados1(10**7)')
# 2.19 segundos
# >>> tiempo('sumaDeCuadrados2(10**7)')
# 0.00 segundos
# ---------------------------------------------------------------------
# Ejercicio 1.3. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_euler6(n: int) -> None:
assert euler6a(n) == euler6b(n)
# Comparación de eficiencia
Capítulo 2. Definiciones por comprensión 37
# =========================
# La comparación es
# >>> tiempo('euler6a(10**7)')
# 2.26 segundos
# >>> tiempo('euler6b(10**7)')
# 0.00 segundos
# ---------------------------------------------------------------------
# Ejercicio 2. Definir, por comprensión, la función
# replica : (int, A) -> list[A]
# tal que replica(n, x) es la lista formada por n copias del elemento
# x. Por ejemplo,
# replica(4, 7) == [7,7,7,7]
# replica(3, True) == [True, True, True]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.1. 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 la función
# linea : (int) -> list[int]
# tal 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
# ---------------------------------------------------------------------
# 1ª definición
# =============
38 Ejercicios de programación con Python
# 2ª definición
# =============
# 3ª definición
# =============
# Comprobación de equivalencia
# ============================
@given([Link](min_value=1, max_value=1000))
def test_linea(n: int) -> None:
r = linea1(n)
assert linea2(n) == r
assert linea3(n) == r
# 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
Capítulo 2. Definiciones por comprensión 39
# ---------------------------------------------------------------------
# Ejercicio 3.2. Definir la función
# triangulo : (int) -> list[list[int]]
# tale que 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
# =============
# 2ª definición
# =============
# 3ª definición
# =============
# Comprobación de equivalencia
# ============================
@given([Link](min_value=1, max_value=1000))
def test_triangulo(n: int) -> None:
r = triangulo1(n)
assert triangulo2(n) == r
assert triangulo3(n) == r
# Comparación de eficiencia
# =========================
40 Ejercicios de programación con Python
# La comparación es
# >>> tiempo('triangulo1(10**4)')
# 2.58 segundos
# >>> tiempo('triangulo2(10**4)')
# 1.91 segundos
# >>> tiempo('triangulo3(10**4)')
# 1.26 segundos
# ---------------------------------------------------------------------
# Ejercicio 4. Un números entero positivo es perfecto 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, por comprensión, 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.1. Un número natural n se denomina abundante 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
# numeroAbundante : (int) -> bool
# tal que numeroAbundante(n) se verifica si n es un número
# abundante. Por ejemplo,
# numeroAbundante(5) == False
# numeroAbundante(12) == True
# numeroAbundante(28) == False
# numeroAbundante(30) == True
# numeroAbundante(100000000) == True
# numeroAbundante(100000001) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.2. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.3. Definir la función
# todosPares : (int) -> bool
42 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 6. 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
# euler1(10**10) == 23333333331666666668
# euler1(10**20) == 2333333333333333333316666666666666666668
#
# Nota: Este ejercicio está basado en el problema 1 del Proyecto Euler
# [Link]
# ---------------------------------------------------------------------
# El cálculo es
# >>> euler1(1000)
# 233168
Capítulo 2. Definiciones por comprensión 43
# ---------------------------------------------------------------------
# Ejercicio 7. 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
# 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([Link](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
# Comparación de eficiencia
# =========================
# La comparación es
Capítulo 2. Definiciones por comprensión 45
# >>> tiempo('circulo1(2000)')
# 0.71 segundos
# >>> tiempo('circulo2(2000)')
# 0.76 segundos
# >>> tiempo('circulo3(2000)')
# 2.63 segundos
# >>> tiempo('circulo4(2000)')
# 1.06 segundos
# ---------------------------------------------------------------------
# Ejercicio 8.1. El número e se define como el límite de la sucesión
# (1+1/n)**n; es decir,
# e = lim (1+1/n)**n
#
# Definir la función
# aproxE : (int) -> list[float]
# tal 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.2. Definir la función
# errorAproxE : (float) -> int
# tal que 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9.1. 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 la función
# aproxLimSeno : (int) -> list[float]
# tal que aproxLimSeno(n) es la lista cuyos elementos son los n primeros
# términos de la sucesión sen(1/m)/(1/m). Por ejemplo,
# aproxLimSeno(1) == [0.8414709848078965]
# aproxLimSeno(2) == [0.8414709848078965,0.958851077208406]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9.2. Definir la función
# errorLimSeno : (float) -> int
# tal que errorLimSeno(x) es el menor número de términos de la sucesión
# sen(1/m)/(1/m) necesarios para obtener su límite con un error menor
# que x. Por ejemplo,
# errorLimSeno(0.1) == 2
# errorLimSeno(0.01) == 5
# errorLimSeno(0.001) == 13
# errorLimSeno(0.0001) == 41
# ---------------------------------------------------------------------
# 1ª definición de errorLimSeno
# ============================
Capítulo 2. Definiciones por comprensión 47
# ---------------------------------------------------------------------
# Ejercicio 10.1. El número π puede calcularse con la fórmula de
# Leibniz
# π/4 = 1 - 1/3 + 1/5 - 1/7 + ...+ (-1)**n/(2*n+1) + ...
#
# Definir la función
# calculaPi : (int) -> float
# tal 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
# calculaPi(300) == 3.1449149035588526
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10.2. Definir la función
# errorPi : (float) -> int
# tal que 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11.1. Una terna (x,y,z) de enteros positivos es pitagórica
# si x^2 + y^2 = z^2 y x < y < z.
48 Ejercicios de programación con Python
#
# 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
# ===========
# Comprobación de equivalencia
Capítulo 2. Definiciones por comprensión 49
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=50))
def test_pitagoricas(n: int) -> None:
r = pitagoricas1(n)
assert pitagoricas2(n) == r
assert pitagoricas3(n) == r
# 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
# ---------------------------------------------------------------------
# Ejercicio 11.2. Definir la función
# numeroDePares : (int, int, int) -> int
# tal que numeroDePares(t) es el número de elementos pares de la terna
# t. Por ejemplo,
# numeroDePares(3, 5, 7) == 0
# numeroDePares(3, 6, 7) == 1
# numeroDePares(3, 6, 4) == 2
# numeroDePares(4, 6, 4) == 3
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11.3. Definir la función
# conjetura : (int) -> bool
# tal que conjetura(n) se verifica si todas las ternas pitagóricas
50 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 11.4. Demostrar la conjetura para todas las ternas
# pitagóricas.
# ---------------------------------------------------------------------
#
# Sea (x,y,z) una terna pitagórica. Entonces x^2+y^2=z^2. Pueden darse
# 4 casos:
#
# Caso 1: x e y son pares. Entonces, x^2, y^2 y z^2 también lo
# son. Luego el número de componentes pares es 3 que es impar.
#
# Caso 2: x es par e y es impar. Entonces, x^2 es par, y^2 es impar y
# z^2 es impar. Luego el número de componentes pares es 1 que es impar.
#
# Caso 3: x es impar e y es par. Análogo al caso 2.
#
# Caso 4: x e y son impares. Entonces, x^2 e y^2 también son impares y
# z^2 es par. Luego el número de componentes pares es 1 que es impar.
# ---------------------------------------------------------------------
# Ejercicio 12.1. (Problema 9 del proyecto Euler). 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),
Capítulo 2. Definiciones por comprensión 51
# 1ª solución --
# ===========
# 2ª solución --
# ===========
# 3ª solución --
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
# 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
# ---------------------------------------------------------------------
# Ejercicio 12.2. Definir la función
# euler9 : () -> int
# tal que euler9() es producto abc donde (a,b,c) es la única terna
# pitagórica tal que a+b+c=1000.
#
Capítulo 2. Definiciones por comprensión 53
# ---------------------------------------------------------------------
# Ejercicio 13. 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, por comprensión, 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 14. Definir , por comprensión,la función
# sumaConsecutivos : (list[int]) -> list[int]
# tal que sumaConsecutivos(xs) es la suma de los pares de elementos
# consecutivos de la lista xs. Por ejemplo,
# sumaConsecutivos([3, 1, 5, 2]) == [4, 6, 7]
# sumaConsecutivos([3]) == []
# sumaConsecutivos(range(1, 1+10**8))[-1] == 199999999
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
54 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 16. 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]]
#
# 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),
Capítulo 2. Definiciones por comprensión 55
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)]
# ---------------------------------------------------------------------
# Ejercicio 16.1. Definir la función
# nombres : (BD) -> list[str]
# tal 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']
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 16.2. Definir la función
# musicos : (BD) -> list[str]
# tal que 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']
# ---------------------------------------------------------------------
56 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 16.3. Definir la función
# seleccion : (BD, str) -> list[str]
# tal que 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']
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 16.4. Definir la función
# musicos2 : (BD) -> list[str]
# tal que 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']
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 16.5. Definir la función
# vivas : (BD, int) -> list[str]
# tal que 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']
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Comprobación
# ---------------------------------------------------------------------
# La comprobación es
# src> poetry run pytest -q definiciones_por_comprension.py
# 8 passed in 4.23s
58 Ejercicios de programación con Python
Capítulo 3
# ---------------------------------------------------------------------
# Importación de librerías auxiliares --
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
# ---------------------------------------------------------------------
# Ejercio 1. Definir, por recursión, la función
59
60 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 1.2. Comprobar con Hypothesis que la función potencia es
# equivalente a la predefinida (^).
# ---------------------------------------------------------------------
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](),
[Link](min_value=0, max_value=100))
def test_potencia(m: int, n: int) -> None:
assert potencia(m, n) == m ** n
# ---------------------------------------------------------------------
# Ejercicio 2. 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
#
Capítulo 3. Definiciones por recursión 61
# La propiedad es
@given([Link](min_value=1, max_value=1000),
[Link](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
# ---------------------------------------------------------------------
# Ejercicio 3.1, Definir por recursión la función
# pertenece : (A, list[A]) -> bool
# tal que pertenece(x, ys) se verifica si x pertenece a la lista ys.
# Por ejemplo,
# pertenece(3, [2, 3, 5]) == True
# pertenece(4, [2, 3, 5]) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.2. Comprobar con Hypothesis que pertenece es equivalente
# a in.
# ---------------------------------------------------------------------
# La propiedad es
62 Ejercicios de programación con Python
@given([Link](),
[Link]([Link]()))
def test_pertenece(x: int, ys: list[int]) -> None:
assert pertenece(x, ys) == (x in ys)
# ---------------------------------------------------------------------
# Ejercicio 4. Definir por recursión la función
# concatenaListas :: [[a]] -> [a]
# tal que (concatenaListas xss) es la lista obtenida concatenando las
# listas de xss. Por ejemplo,
# concatenaListas([[1, 3], [5], [2, 4, 6]]) == [1, 3, 5, 2, 4, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.1. Definir por recursión la función
# coge : (int, list[A]) -> list[A]
# tal que coge(n, xs) es la lista de los n primeros elementos de
# xs. Por ejemplo,
# coge(3, range(4, 12)) == [4, 5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.2. Comprobar con Hypothesis que coge(n, xs) es equivalente
# a xs[:n], suponiendo que n >= 0.
# ---------------------------------------------------------------------
Capítulo 3. Definiciones por recursión 63
# La propiedad es
@given([Link](min_value=0),
[Link]([Link]()))
def test_coge(n: int, xs: list[int]) -> None:
assert coge(n, xs) == xs[:n]
# ---------------------------------------------------------------------
# Ejercicio 6.1. Definir, por recursión la función
# sumaDeCuadradosR : (int) -> int
# tal sumaDeCuadradosR(n) es la suma de los cuadrados de los n primeros
# números naturales. Por ejemplo,
# sumaDeCuadradosR(3) == 14
# sumaDeCuadradosR(100) == 338350
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6.2. Comprobar con Hypothesis que sumaCuadradosR(n) es igual
# a n(n+1)(2n+1)/6.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_sumaDeCuadrados(n: int) -> None:
assert sumaDeCuadradosR(n) == n * (n + 1) * (2 * n + 1) // 6
# ---------------------------------------------------------------------
# Ejercicio 6.3. Definir, por comprensión, la función
# sumaDeCuadradosC : (int) -> int
# tal sumaDeCuadradosC(n) es la suma de los cuadrados de los n primeros
# números naturales. Por ejemplo,
# sumaDeCuadradosC(3) == 14
64 Ejercicios de programación con Python
# sumaDeCuadradosC(100) == 338350
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6.4. Comprobar con Hypothesis que las funciones
# sumaCuadradosR y sumaCuadradosC son equivalentes sobre los números
# naturales.
# ---------------------------------------------------------------------
@given([Link](min_value=1, max_value=1000))
def test_sumaDeCuadrados2(n: int) -> None:
assert sumaDeCuadradosR(n) == sumaDeCuadradosC(n)
# ---------------------------------------------------------------------
# Ejercicio 7.1. Definir, por recursión, la función
# digitosR : (int) -> list[int]
# tal que digitosR(n) es la lista de los dígitos del número n. Por
# ejemplo,
# digitosR(320274) == [3, 2, 0, 2, 7, 4]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7.2. Definir, por comprensión, la función
# digitosC : (int) -> list[int]
# tal que digitosC(n) es la lista de los dígitos del número n. Por
# ejemplo,
# digitosC(320274) == [3, 2, 0, 2, 7, 4]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7.3. Comprobar con Hypothesis que las funciones digitosR y
# digitosC son equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_digitos(n: int) -> None:
assert digitosR(n) == digitosC(n)
# ---------------------------------------------------------------------
# Ejercicio 8.1. Definir, por recursión, la función
# sumaDigitosR : (int) -> int
# tal que sumaDigitosR(n) es la suma de los dígitos de n. Por ejemplo,
# sumaDigitosR(3) == 3
# sumaDigitosR(2454) == 15
# sumaDigitosR(20045) == 11
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.2. Definir, sin usar recursión, la función
# sumaDigitosNR : (int) -> int
# tal que sumaDigitosNR(n) es la suma de los dígitos de n. Por ejemplo,
# sumaDigitosNR(3) == 3
# sumaDigitosNR(2454) == 15
# sumaDigitosNR(20045) == 11
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.3. Comprobar con Hypothesis que las funciones sumaDigitosR
# y sumaDigitosNR son equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_sumaDigitos(n: int) -> None:
assert sumaDigitosR(n) == sumaDigitosNR(n)
# ---------------------------------------------------------------------
# Ejercicio 9.1. Definir, por recursión, la función
# listaNumeroR : (list[int]) -> int
# tal que listaNumeroR(xs) es el número formado por los dígitos xs. Por
# ejemplo,
# listaNumeroR([5]) == 5
# listaNumeroR([1, 3, 4, 7]) == 1347
# listaNumeroR([0, 0, 1]) == 1
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9.2. Definir, por comprensión, la función
# listaNumeroC : (list[int]) -> int
# tal que listaNumeroC(xs) es el número formado por los dígitos xs. Por
# ejemplo,
# listaNumeroC([5]) == 5
# listaNumeroC([1, 3, 4, 7]) == 1347
# listaNumeroC([0, 0, 1]) == 1
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9.3. Comprobar con Hypothesis que las funciones
# listaNumeroR y listaNumeroC son equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link](min_value=0, max_value=9), min_size=1))
def test_listaNumero(xs: list[int]) -> None:
print(”listaNumero”)
assert listaNumeroR(xs) == listaNumeroC(xs)
# ---------------------------------------------------------------------
# Ejercicio 10.1. Definir, por recursión, la función
# mayorExponenteR : (int, int) -> int
# tal que mayorExponenteR(a, b) es el exponente de la mayor potencia de
# a que divide b. Por ejemplo,
# mayorExponenteR(2, 8) == 3
# mayorExponenteR(2, 9) == 0
# mayorExponenteR(5, 100) == 2
# mayorExponenteR(2, 60) == 2
#
# Nota: Se supone que a > 1 y b > 0.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10.2. Definir, por comprensión, la función
# mayorExponenteC : (int, int) -> int
# tal que mayorExponenteC(a, b) es el exponente de la mayor potencia de
# a que divide b. Por ejemplo,
# mayorExponenteC(2, 8) == 3
68 Ejercicios de programación con Python
# mayorExponenteC(2, 9) == 0
# mayorExponenteC(5, 100) == 2
# mayorExponenteC(2, 60) == 2
#
# Nota: Se supone que a > 1 y b > 0.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10.3. Comprobar con Hypothesis que las funciones
# mayorExponenteR y mayorExponenteC son equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=2, max_value=10),
[Link](min_value=1, max_value=10))
def test_mayorExponente(a: int, b: int) -> None:
assert mayorExponenteR(a, b) == mayorExponenteC(a, b)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Librerías auxiliares --
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
A = TypeVar('A')
B = TypeVar('B')
# ---------------------------------------------------------------------
# Ejercicio 1. 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
# ---------------------------------------------------------------------
# 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:
70 Ejercicios de programación con Python
# 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([Link]([Link]()),
[Link]([Link]()))
def test_subconjunto(xs: list[int], ys: list[int]) -> None:
assert subconjunto1(xs, ys)\
== subconjunto2(xs, ys)\
== subconjunto3(xs, ys)\
== subconjunto4(xs, ys)
# 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)')
Capítulo 3. Definiciones por recursión 71
# 1.19 segundos
# >>> tiempo('subconjunto4(xs, xs)')
# 0.01 segundos
# ---------------------------------------------------------------------
# Ejercicio 2. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
def test_iguales(xs: list[int], ys: list[int]) -> None:
assert iguales1(xs, ys) == iguales2(xs, ys)
# Comparación de eficiencia
# =========================
72 Ejercicios de programación con Python
# La comparación es
# >>> xs = list(range(20000))
# >>> tiempo('iguales1(xs, xs)')
# 2.71 segundos
# >>> tiempo('iguales2(xs, xs)')
# 0.01 segundos
# ---------------------------------------------------------------------
# Ejercicio 3.1. 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
# repetidos xs e ys. Por ejemplo,
# union([3, 2, 5], [5, 7, 3, 4]) == [3, 2, 5, 7, 4]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
return zs
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
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))
# 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
# >>> tiempo('union3(list(range(0,30000,2)), list(range(1,30000,2)))')
# 1.45 segundos
# >>> tiempo('union4(list(range(0,30000,2)), list(range(1,30000,2)))')
# 0.00 segundos
# ---------------------------------------------------------------------
# Nota. En los ejercicios de comprobación de propiedades, cuando se
# trata con igualdades se usa la igualdad conjuntista (definida por la
# función iguales) en lugar de la igualdad de lista (definida por ==)
# ---------------------------------------------------------------------
74 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 3.2. Comprobar con Hypothesis que la unión es conmutativa.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
def test_union_conmutativa(xs: list[int], ys: list[int]) -> None:
xs1 = list(set(xs))
ys1 = list(set(ys))
assert iguales1(union1(xs1, ys1), union1(ys1, xs1))
# ---------------------------------------------------------------------
# Ejercicio 4.1. 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,
# interseccion([3, 2, 5], [5, 7, 3, 4]) == [3, 5]
# interseccion([3, 2, 5], [9, 7, 6, 4]) == []
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 3. Definiciones por recursión 75
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
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))
# 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
# >>> tiempo('interseccion4(list(range(0,20000)), list(range(1,20000,2)))')
# 0.00 segundos
76 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 4.2. Comprobar con Hypothesis si se cumple la siguiente
# propiedad
# A ∪ (B ∩ C) = (A ∪ B) ∩ C
# donde se considera la igualdad como conjuntos. En el caso de que no
# se cumpla verificar el contraejemplo calculado por Hypothesis.
# ---------------------------------------------------------------------
# La propiedad es
# @given([Link]([Link]()),
# [Link]([Link]()),
# [Link]([Link]()))
# def test_union_interseccion(xs: list[int],
# ys: list[int],
# zs: list[int]) -> None:
# assert iguales1(union1(xs, interseccion1(ys, zs)),
# interseccion1(union1(xs, ys), zs))
# -------------------------------------------------------------------
# Ejercicio 5.1. 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)]
# -------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
def test_producto(xs: list[int], ys: list[int]) -> None:
assert sorted(producto1(xs, ys)) == sorted(producto2(xs, ys))
# 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)))')
# 2.58 segundos
# ------------------------------------------------------------
# Ejercicio 5.2. 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.
# ------------------------------------------------------------
# La propiedad es
@given([Link]([Link]()),
[Link]([Link]()))
def test_elementos_producto(xs: list[int], ys: list[int]) -> None:
assert len(producto1(xs, ys)) == len(xs) * len(ys)
# ---------------------------------------------------------------------
# Ejercicio 6.1. Definir la función
78 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
for r in range(len(xs)+1)
for ys in combinations(xs, r)]
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link](), 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
# 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))')
# 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
# ---------------------------------------------------------------------
# Ejercicio 6.2. Comprobar con Hypothesis que el número de elementos de
# (subconjuntos xs) es 2 elevado al número de elementos de xs.
# ---------------------------------------------------------------------
80 Ejercicios de programación con Python
# La propiedad es
@given([Link]([Link](), max_size=7))
def test_length_subconjuntos(xs: list[int]) -> None:
assert len(subconjuntos1(xs)) == 2 ** len(xs)
# ---------------------------------------------------------------------
# Comprobación de las propiedades
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
Capítulo 3. Definiciones por recursión 81
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# doblePosImpar : (list[int]) -> list[int]
# tal que 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]
# ---------------------------------------------------------------------
# 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]:
def f(n: int, x: int) -> int:
if n % 2 == 1:
return 2 * x
return x
return [f(n, x) for (n, x) in enumerate(xs)]
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# sumaDigitos : (list[int]) -> int
# tal que 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
# ---------------------------------------------------------------------
82 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# ultimoDigito : (int) -> int
# tal que ultimoDigito(n) es el último dígito de n. Por ejemplo,
# ultimoDigito(123) == 3
# ultimoDigito(0) == 0
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# luhn :: (int) -> bool
# tal que luhn(n) se verifica si n es un número de Luhn. Por ejemplo,
# luhn(5594589764218858) == True
# luhn(1234567898765432) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
# Esta relación es una adaptación del primer trabajo del curso ”CIS 194:
# Introduction to Haskell (Spring 2015)” de la Univ. de Pensilvania,
# impartido por Noam Zilberstein. El trabajo se encuentra en
# [Link]
#
# En el artículo [Algoritmo de Luhn]([Link] de la
# Wikipedia se encuentra información del algoritmo
Capítulo 3. Definiciones por recursión 83
# ---------------------------------------------------------------------
# Librerías auxiliares --
# ---------------------------------------------------------------------
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
# ---------------------------------------------------------------------
return x == int(str(x)[::-1])
# ---------------------------------------------------------------------
# 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# 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
Capítulo 3. Definiciones por recursión 85
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# orden : (int) -> int
# 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 : (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
# ---------------------------------------------------------------------
return ordenMayor(siguiente(x), n - 1)
# ---------------------------------------------------------------------
# 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# 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]]
Capítulo 3. Definiciones por recursión 87
# ---------------------------------------------------------------------
# Ejercicio 11. A la vista de los resultados de (menoresdDeOrdenMayor 5)
# conjeturar sobre la última cifra de menorDeOrdenMayor.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12. Decidir con Hypothesis la conjetura.
# ---------------------------------------------------------------------
# La conjetura es
# @given([Link](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)
88 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 Hypothesis la conjetura sobre el orden de
# 196.
# ---------------------------------------------------------------------
# La propiedad es
@settings(deadline=None)
@given([Link](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
setrecursionlimit(10**6)
# ---------------------------------------------------------------------
# Ejercicio 1.1. Definir, por comprensión, la función
# sumaDigitosC : (str) -> int
# tal que sumaDigitosC(xs) es la suma de los dígitos de la cadena
# xs. Por ejemplo,
# sumaDigitosC(”SE 2431 X”) == 10
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.2. Definir, por recursión, la función
# sumaDigitosR : (str) -> int
# tal que sumaDigitosR(xs) es la suma de los dígitos de la cadena
# xs. Por ejemplo,
# sumaDigitosR(”SE 2431 X”) == 10
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.3. Definir, por iteración, la función
# sumaDigitosI : (str) -> int
# tal que sumaDigitosI(xs) es la suma de los dígitos de la cadena
# xs. Por ejemplo,
# sumaDigitosI(”SE 2431 X”) == 10
90 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.4. Comprobar con QuickCheck que las tres definiciones son
# equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](alphabet=[Link](min_codepoint=32, max_codepoint=127)))
def test_sumaDigitos(xs: str) -> None:
r = sumaDigitosC(xs)
assert sumaDigitosR(xs) == r
assert sumaDigitosI(xs) == r
# ---------------------------------------------------------------------
# Ejercicio 2.1. Definir, por comprensión, 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(””) == ””
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2.2. Definir, por recursión, la función
# mayusculaInicialRec : (str) -> str
# tal que mayusculaInicialRec(xs) es la palabra xs con la letra inicial
# en mayúscula y las restantes en minúsculas. Por ejemplo,
Capítulo 3. Definiciones por recursión 91
# mayusculaInicialRec(”sEviLLa”) == ”Sevilla”
# mayusculaInicialRec(””) == ””
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2.3. Comprobar con Hypothesis que ambas definiciones son
# equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]())
def test_mayusculaInicial(xs: str) -> None:
assert mayusculaInicial(xs) == mayusculaInicialRec(xs)
# ---------------------------------------------------------------------
# Ejercicio 3.1. 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 mayúsculas
#
# Definir, por comprensión, 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”]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.2. Definir, por recursión, la función
# tituloRec : (list[str]) -> list[str]
# tal que tituloRec(ps) es la lista de las palabras de ps con
# las reglas de mayúsculas iniciales de los títulos. Por ejemplo,
# >>> tituloRec([”eL”, ”arTE”, ”DE”, ”La”, ”proGraMacion”])
# [”El”, ”Arte”, ”de”, ”la”, ”Programacion”]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.3. Comprobar con Hypothesis que ambas definiciones son
# equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link]()))
Capítulo 3. Definiciones por recursión 93
# ---------------------------------------------------------------------
# Ejercicio 4.1. Definir, por comprensión, 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.2. Definir, por recursión, la función
# posicionesR : (str, str) -> list[int]
# tal que posicionesR(x, ys) es la lista de la posiciones del carácter x
# en la cadena ys. Por ejemplo,
# posicionesR('a', ”Salamamca”) == [1,3,5,8]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.3. Definir, por iteración, la función
# posicionesI : (str, str) -> list[int]
# tal que posicionesI(x ,ys) es la lista de la posiciones del carácter x
# en la cadena ys. Por ejemplo,
# posicionesI('a', ”Salamamca”) == [1,3,5,8]
# ---------------------------------------------------------------------
r = []
for n, y in enumerate(ys):
if x == y:
[Link](n)
return r
# ---------------------------------------------------------------------
# Ejercicio 4.3. Comprobar con Hypothesis que las tres definiciones son
# equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](), [Link]())
def test_posiciones(x: str, ys: str) -> None:
r = posiciones(x, ys)
assert posicionesR(x, ys) == r
assert posicionesI(x, ys) == r
# ---------------------------------------------------------------------
# Ejercicio 5.1. Definir, por recursión, la función
# esSubcadenaR : (str, str) -> bool
# tal que esSubcadenaR(xs ys) se verifica si xs es una subcadena de ys.
# Por ejemplo,
# esSubcadenaR(”casa”, ”escasamente”) == True
# esSubcadenaR(”cante”, ”escasamente”) == False
# esSubcadenaR(””, ””) == True
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.2. Definir, por comprensión, la función
# esSubcadena : (str, str) -> bool
# tal que esSubcadena(xs ys) se verifica si xs es una subcadena de ys.
# Por ejemplo,
Capítulo 3. Definiciones por recursión 95
# ---------------------------------------------------------------------
# Ejercicio 5.3. Comprobar con Hypothesis que las tres definiciones son
# equivalentes.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](), [Link]())
def test_esSubcadena(xs: str, ys: str) -> None:
r = esSubcadenaR(xs, ys)
assert esSubcadena(xs, ys) == r
assert esSubcadena3(xs, ys) == r
# ---------------------------------------------------------------------
# Comprobación de las propiedades
# ---------------------------------------------------------------------
# La comprobación es
# src> poetry run pytest -q funciones_sobre_cadenas.py
# 1 passed in 0.41s
96 Ejercicios de programación con Python
Capítulo 4
# ---------------------------------------------------------------------
# Importación de librerías auxiliares --
# ---------------------------------------------------------------------
97
98 Ejercicios de programación con Python
setrecursionlimit(10**6)
A = TypeVar('A')
B = TypeVar('B')
C = TypeVar('C', bound=Union[int, float, str])
# ---------------------------------------------------------------------
# Ejercicio 1. 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,
# >>> segmentos((lambda x: x % 2 == 0), [1,2,0,4,9,6,4,5,7,2])
# [[2, 0, 4], [6, 4], [2]]
# >>> segmentos((lambda x: x % 2 == 1), [1,2,0,4,9,6,4,5,7,2])
# [[1], [9], [5, 7]]
# ---------------------------------------------------------------------
# 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
# ---------------------------------------------------------------------
# Ejercicio 2.1. Definir, por comprensión, la función
# relacionadosC : (Callable[[A, A], bool], list[A]) -> bool
# tal que relacionadosC(r, xs) se verifica si para todo par (x,y) de
# elementos consecutivos de xs se cumple la relación r. Por ejemplo,
# >>> relacionadosC(lambda x, y: x < y, [2, 3, 7, 9])
# True
# >>> relacionadosC(lambda x, y: x < y, [2, 3, 1, 9])
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2.2. Definir, por recursión, la función
# relacionadosR : (Callable[[A, A], bool], list[A]) -> bool
# tal que relacionadosR(r, xs) se verifica si para todo par (x,y) de
# elementos consecutivos de xs se cumple la relación r. Por ejemplo,
# >>> relacionadosR(lambda x, y: x < y, [2, 3, 7, 9])
# True
# >>> relacionadosR(lambda x, y: x < y, [2, 3, 1, 9])
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
100 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
@given([Link]([Link]([Link]()), min_size=1))
def test_agrupa(xss: list[list[int]]) -> None:
r = agrupa1(xss)
102 Ejercicios de programación con Python
assert agrupa2(xss) == r
assert agrupa3(xss) == r
assert agrupa4(xss) == r
assert agrupa5(xss) == r
# 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
# ---------------------------------------------------------------------
# Ejercicio 3.2. Comprobar con Hypothesis que la longitud de todos los
# elementos de agrupa(xs) es igual a la longitud de xs.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link]([Link]()), 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)))
Capítulo 4. Funciones de orden superior 103
# ---------------------------------------------------------------------
# Ejercicio 4.1. Definir, por comprensión, la función
# concC : (list[list[A]]) -> list[A]
# tal que concC(xss) es la concenación de las listas de xss. Por
# ejemplo,
# concC([[1,3],[2,4,6],[1,9]]) == [1,3,2,4,6,1,9]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.2. Definir, por recursión, la función
# concR : (list[list[A]]) -> list[A]
# tal que concR(xss) es la concenación de las listas de xss. Por
# ejemplo,
# concR([[1,3],[2,4,6],[1,9]]) == [1,3,2,4,6,1,9]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.3. Definir, usando reduce, la función
# concP : (Any) -> Any:
# tal que concP(xss) es la concenación de las listas de xss. Por
# ejemplo,
# concP([[1,3],[2,4,6],[1,9]]) == [1,3,2,4,6,1,9]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.4. Comprobar con Hypothesis que la funciones concC,
# concatR y concP son equivalentes.
# ---------------------------------------------------------------------
104 Ejercicios de programación con Python
# La propiedad es
@given([Link]([Link]([Link]()), min_size=1))
def test_conc(xss: list[list[int]]) -> None:
r = concC(xss)
assert concR(xss) == r
assert concP(xss) == r
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('concC([list(range(n)) for n in range(1500)])')
# 0.04 segundos
# >>> tiempo('concR([list(range(n)) for n in range(1500)])')
# 6.28 segundos
# >>> tiempo('concP([list(range(n)) for n in range(1500)])')
# 2.55 segundos
# ---------------------------------------------------------------------
# Ejercicio 4.5. Comprobar con Hypothesis que la longitud de
# concatP(xss) es la suma de las longitudes de los elementos de xss.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link]([Link]([Link]()), min_size=1))
def test_long_conc(xss: list[list[int]]) -> None:
assert len(concP(xss)) == sum(map(len, xss))
# ---------------------------------------------------------------------
# Ejercicio 5.1. Definir, por comprensión, la función
# filtraAplicaC : (Callable[[A], B], Callable[[A], bool], list[A])
# -> list[B]
# tal que filtraAplicaC(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,
# >>> filtraAplicaC(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.2. Definir, usando map y filter, la función
# filtraAplicaMF : (Callable[[A], B], Callable[[A], bool], list[A])
# -> list[B]
# tal que filtraAplicaMF(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,
# >>> filtraAplicaMF(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.3. Definir, por recursión, la función
# filtraAplicaR : (Callable[[A], B], Callable[[A], bool], list[A])
# -> list[B]
# tal que filtraAplicaR(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,
# >>> filtraAplicaR(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.4. Definir, por plegado, la función
# filtraAplicaP : (Callable[[A], B], Callable[[A], bool], list[A])
106 Ejercicios de programación con Python
# -> list[B]
# tal que filtraAplicaP(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,
# >>> filtraAplicaP(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.5. Definir, por iteración, la función
# filtraAplicaI : (Callable[[A], B], Callable[[A], bool], list[A])
# -> list[B]
# tal que filtraAplicaI(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,
# >>> filtraAplicaI(lambda x: x + 4, lambda x: x < 3, range(1, 7))
# [5, 6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.6. Comprobar que las definiciones de filtraAplica son
# equivalentes.
# ---------------------------------------------------------------------
Capítulo 4. Funciones de orden superior 107
# La propiedad es
@given([Link]([Link]()))
def test_filtraAplica(xs: list[int]) -> None:
def f(x: int) -> int:
return x + 4
r = filtraAplicaC(f, p, xs)
assert filtraAplicaMF(f, p, xs) == r
assert filtraAplicaR(f, p, xs) == r
assert filtraAplicaP(f, p, xs) == r
assert filtraAplicaI(f, p, xs) == r
# ---------------------------------------------------------------------
# Ejercicio 5.7. Comparar la eficiencia de las definiciones de
# filtraAplica.
# ---------------------------------------------------------------------
# La comparación es
# >>> tiempo('filtraAplicaC(lambda x: x, lambda x: x % 2 == 0,
# range(10**5))')
# 0.02 segundos
# >>> tiempo('filtraAplicaMF(lambda x: x, lambda x: x % 2 == 0,
# range(10**5))')
# 0.01 segundos
# >>> tiempo('filtraAplicaR(lambda x: x, lambda x: x % 2 == 0,
# range(10**5))')
# Process Python violación de segmento (core dumped)
# >>> tiempo('filtraAplicaP(lambda x: x, lambda x: x % 2 == 0,
# range(10**5))')
# 4.07 segundos
# >>> tiempo('filtraAplicaI(lambda x: x, lambda x: x % 2 == 0,
# range(10**5))')
# 0.01 segundos
#
# >>> tiempo('filtraAplicaC(lambda x: x, lambda x: x % 2 == 0,
# range(10**7))')
108 Ejercicios de programación con Python
# 1.66 segundos
# >>> tiempo('filtraAplicaMF(lambda x: x, lambda x: x % 2 == 0,
# range(10**7))')
# 1.00 segundos
# >>> tiempo('filtraAplicaI(lambda x: x, lambda x: x % 2 == 0,
# range(10**7))')
# 1.21 segundos
# ---------------------------------------------------------------------
# Ejercicio 6.1. Definir la función
# maximo : (list[C]) -> C:
# 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”
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
Capítulo 4. Funciones de orden superior 109
# La propiedad es
@given([Link]([Link](), min_size=2))
def test_maximo(xs: list[int]) -> None:
r = maximo1(xs)
assert maximo2(xs) == r
assert maximo3(xs) == r
# ---------------------------------------------------------------------
# Comprobación de las propiedades
# ---------------------------------------------------------------------
# La comprobación es
# src> poetry run pytest -q \
# funciones_de_orden_superior_y_definiciones_por_plegados.py
# 1 passed in 0.74s
110 Ejercicios de programación con Python
Capítulo 5
# ---------------------------------------------------------------------
# Librerías auxiliares
# ---------------------------------------------------------------------
A = TypeVar(”A”)
111
112 Ejercicios de programación con Python
B = TypeVar(”B”)
# ---------------------------------------------------------------------
# Nota 1. En los siguientes ejercicios se trabajará con los árboles
# binarios definidos como sigue
# @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]
# Por ejemplo, el árbol
# 9
# / \
# / \
# 3 7
# / \
# 2 4
# se representa por
# N(9, N(3, H(2), H(4)), H(7))
# ---------------------------------------------------------------------
@dataclass
class Arbol(Generic[A]):
pass
@dataclass
class H(Arbol[A]):
x: A
@dataclass
class N(Arbol[A]):
x: A
Capítulo 5. Tipos definidos y de datos algebraicos 113
i: Arbol[A]
d: Arbol[A]
# ---------------------------------------------------------------------
# Nota 2. En las comprobación de propiedades se usará el generador
# arbolArbitrario(int) -> Arbol[int]
# tal que (arbolArbitrario n) es un árbol aleatorio de orden n. Por ejemplo,
# >>> arbolArbitrario(4)
# N(x=2, i=H(x=1), d=H(x=9))
# >>> arbolArbitrario(4)
# H(x=10)
# >>> arbolArbitrario(4)
# N(x=4, i=N(x=7, i=H(x=4), d=H(x=0)), d=H(x=6))
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.1. Definir la función
# nHojas : (Arbol[A]) -> int
# tal 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
114 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 1.3. Comprobar con Hypothesis que en todo árbol binario el
# número de sus hojas es igual al número de sus nodos más uno.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=10))
def test_nHojas(n: int) -> None:
a = arbolArbitrario(n)
assert nHojas(a) == nNodos(a) + 1
# ---------------------------------------------------------------------
# Ejercicio 2.1. 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
# ---------------------------------------------------------------------
assert False
# ---------------------------------------------------------------------
# Ejercicio 2.2. Comprobar con Hypothesis que para todo árbol biario
# x, se tiene que
# nNodos(x) <= 2^profundidad(x) - 1
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=10))
def test_nNodos(n: int) -> None:
a = arbolArbitrario(n)
assert nNodos(a) <= 2 ** profundidad(a) - 1
# ---------------------------------------------------------------------
# Ejercicio 3.1. Definir la función
# preorden : (Arbol[A]) -> list[A]
# tal 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]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.2. Comprobar con Hypothesis que la longitud de la lista
# obtenida recorriendo un árbol en sentido preorden es igual al número
# de nodos del árbol más el número de hojas.
# ---------------------------------------------------------------------
# La propiedad es
116 Ejercicios de programación con Python
@given([Link](min_value=1, max_value=10))
def test_recorrido(n: int) -> None:
a = arbolArbitrario(n)
assert len(preorden(a)) == nNodos(a) + nHojas(a)
# ---------------------------------------------------------------------
# Ejercicio 3.3. Definir la función
# postorden : (Arbol[A]) -> list[A]
# tal que (postorden x) es la lista correspondiente al recorrido postorden
# del árbol x; es decir, primero recorre el subárbol izquierdo, a
# continuación el subárbol derecho y, finalmente, la raíz del
# árbol. Por ejemplo,
# >>> postorden(N(9, N(3, H(2), H(4)), H(7)))
# [2, 4, 3, 7, 9]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.1. 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)))
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
Capítulo 5. Tipos definidos y de datos algebraicos 117
@given([Link](min_value=1, max_value=10))
def test_espejo1(n: int) -> None:
x = arbolArbitrario(n)
assert espejo(espejo(x)) == x
# ---------------------------------------------------------------------
# Ejercicio 4.3. Comprobar con Hypothesis que para todo árbol binario
# x, se tiene que
# reversed(preorden(espejo(x))) = postorden(x)
# ---------------------------------------------------------------------
@given([Link](min_value=1, max_value=10))
def test_espejo2(n: int) -> None:
x = arbolArbitrario(n)
assert list(reversed(preorden(espejo(x)))) == postorden(x)
# ---------------------------------------------------------------------
# Ejercicio 4.4. Comprobar con Hypothesis que para todo árbol x,
# postorden(espejo(x)) = reversed(preorden(x))
# ---------------------------------------------------------------------
@given([Link](min_value=1, max_value=10))
def test_espejo(n: int) -> None:
x = arbolArbitrario(n)
assert postorden(espejo(x)) == list(reversed(preorden(x)))
# ---------------------------------------------------------------------
# Ejercicio 5.1. Definir la función
# takeArbol : (int, Arbol[A]) -> Arbol[A]
# tal que takeArbol(n, t) es el subárbol de t de profundidad n. Por
# 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)))
118 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 5.2. Comprobar con Hypothesis que la profundidad de
# takeArbol(n, x) es menor o igual que n, para todo número natural n
# y todo árbol x.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=12),
[Link](min_value=1, max_value=10))
def test_takeArbol(n: int, m: int) -> None:
x = arbolArbitrario(m)
assert profundidad(takeArbol(n, x)) <= n
# ---------------------------------------------------------------------
# Ejercicio 6.2. 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)
# 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)))
# ---------------------------------------------------------------------
Capítulo 5. Tipos definidos y de datos algebraicos 119
# ---------------------------------------------------------------------
# Ejercicio 6.2. Comprobar con Hypothesis que el número de hojas de
# replicateArbol(n,x) es 2^n, para todo número natural n
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=1, max_value=10),
[Link](min_value=1, max_value=10))
def test_replicateArbol(n: int, x: int) -> None:
assert nHojas(replicateArbol(n, x)) == 2**n
# ---------------------------------------------------------------------
# Ejercicio 7.1. Definir la función
# mapArbol : (Callable[[A], B], Arbol[A]) -> Arbol[B]
# tal que mapArbol(f, x) es el árbol obtenido aplicándole a cada nodo de
# x la función f. Por ejemplo,
# >>> mapArbol(lambda x: 2 * x, N(9, N(3, H(2), H(4)), H(7)))
# N(18, N(6, H(4), H(8)), H(14))
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7.2. Comprobar con Hypothesis que
120 Ejercicios de programación con Python
# list(map(lambda n: 1 + n, preorden(x))) ==
# list(preorden(mapArbol(lambda n: 1 + n, x)))
# ---------------------------------------------------------------------
@given([Link](min_value=1, max_value=10))
def test_map_preorden(n: int) -> None:
x = arbolArbitrario(n)
print(x)
assert list(map(lambda n: 1 + n, preorden(x))) == \
list(preorden(mapArbol(lambda n: 1 + n, x)))
# ---------------------------------------------------------------------
# Comprobación de las propiedades
# ---------------------------------------------------------------------
# La comprobación es
# src> poetry run pytest -q tipos_de_datos_algebraicos_Arboles_binarios.py
# 7 passed in 0.49s
# ---------------------------------------------------------------------
# Librerías auxiliares
# ---------------------------------------------------------------------
Capítulo 5. Tipos definidos y de datos algebraicos 121
A = TypeVar(”A”)
B = TypeVar(”B”)
# ---------------------------------------------------------------------
# Ejercicio 1.1. Los árboles binarios con valores en los nodos se pueden
# definir por
# @dataclass
# class Arbol1(Generic[A]):
# pass
#
# @dataclass
# class H1(Arbol1[A]):
# pass
#
# @dataclass
# class N1(Arbol1[A]):
# x: A
# i: Arbol1
# d: Arbol1
# Por ejemplo, el árbol
# 9
# / \
# / \
# 8 6
# / \ / \
# 3 2 4 5
# se puede representar por
# N1(9,
# N1(8, N1(3, H1(), H1()), N1(2, H1(), H1())),
# N1(6, N1(4, H1(), H1()), N1(5, H1(), H1())))
#
# Definir la función
# sumaArbol : (Arbol1) -> int
# tal sumaArbol(x) es la suma de los valores que hay en el árbol x.
# Por ejemplo,
122 Ejercicios de programación con Python
# >>> sumaArbol(N1(2,
# N1(5, N1(3, H1(), H1()), N1(7, H1(), H1())),
# N1(4, H1(), H1())))
# 21
# ---------------------------------------------------------------------
@dataclass
class Arbol1(Generic[A]):
pass
@dataclass
class H1(Arbol1[A]):
pass
@dataclass
class N1(Arbol1[A]):
x: A
i: Arbol1
d: Arbol1
# ---------------------------------------------------------------------
# Ejercicio 1.2. Definir la función
# mapArbol : (Callable[[A], B], Arbol1[A]) -> Arbol1[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,
# N1(2,
# N1(5, N1(3, H1(), H1()), N1(7, H1(), H1())),
# N1(4, H1(), H1())))
# N1(3, N1(6, N1(4, H1(), H1()), N1(8, H1(), H1())), N1(5, H1(), H1()))
# ---------------------------------------------------------------------
Capítulo 5. Tipos definidos y de datos algebraicos 123
# ---------------------------------------------------------------------
# Ejercicio 1.3. Definir la función
# ramaIzquierda : (Arbol1[A]) -> list[A]
# tal que ramaIzquierda(a) es la lista de los valores de los nodos de
# la rama izquierda del árbol a. Por ejemplo,
# >>> ramaIzquierda(N1(2,
# N1(5, N1(3, H1(), H1()), N1(7, H1(), H1())),
# N1(4, H1(), H1())))
# [2, 5, 3]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.4. 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.
#
# Definir la función
# balanceado : (Arbol1[A]) -> bool
# tal que balanceado(a) se verifica si el árbol a está balanceado. Por
# ejemplo,
# >>> balanceado(N1(5, H1(), N1(3, H1(), H1())))
# True
# >>> balanceado(N1(4,
# N1(3, N1(2, H1(), H1()), H1()),
124 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 2. Los árboles binarios con valores en las hojas se pueden
# definir por
# @dataclass
# class Arbol2(Generic[A]):
# pass
#
# @dataclass
# class H2(Arbol2[A]):
# x: A
#
# @dataclass
# class N2(Arbol2[A]):
# i: Arbol2[A]
# d: Arbol2[A]
# Por ejemplo, los árboles
# árbol1 árbol2 árbol3 árbol4
# o o o o
# / \ / \ / \ / \
Capítulo 5. Tipos definidos y de datos algebraicos 125
# 1 o o 3 o 3 o 1
# / \ / \ / \ / \
# 2 3 1 2 1 4 2 3
# se representan por
# arbol1: Arbol2[int] = N2(H2(1), N2(H2(2), H2(3)))
# arbol2: Arbol2[int] = N2(N2(H2(1), H2(2)), H2(3))
# arbol3: Arbol2[int] = N2(N2(H2(1), H2(4)), H2(3))
# arbol4: Arbol2[int] = N2(N2(H2(2), H2(3)), H2(1))
#
# Definir la función
# igualBorde : (Arbol2[A], Arbol2[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
# ---------------------------------------------------------------------
@dataclass
class Arbol2(Generic[A]):
pass
@dataclass
class H2(Arbol2[A]):
x: A
@dataclass
class N2(Arbol2[A]):
i: Arbol2[A]
d: Arbol2[A]
match a:
case H2(x):
return [x]
case N2(i, d):
return borde(i) + borde(d)
assert False
# ---------------------------------------------------------------------
# Ejercicio 3.1. Los árboles binarios con valores en las hojas y en los
# nodos se definen por
# @dataclass
# class Arbol3(Generic[A]):
# pass
#
# @dataclass
# class H3(Arbol3[A]):
# x: A
#
# @dataclass
# class N3(Arbol3[A]):
# x: A
# i: Arbol3[A]
# d: Arbol3[A]
# 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: Arbol3[int] = N3(5, N3(9, H3(1), H3(4)), N3(7, H3(6), H3(8)))
# ej3arbol2: Arbol3[int] = N3(8, N3(9, H3(1), H3(4)), N3(3, H3(6), H3(2)))
# ej3arbol3: Arbol3[int] = N3(5, N3(9, H3(1), H3(4)), H3(2))
# ej3arbol4: Arbol3[int] = N3(5, H3(4), N3(7, H3(6), H3(2)))
#
# Definir la función
Capítulo 5. Tipos definidos y de datos algebraicos 127
@dataclass
class Arbol3(Generic[A]):
pass
@dataclass
class H3(Arbol3[A]):
x: A
@dataclass
class N3(Arbol3[A]):
x: A
i: Arbol3[A]
d: Arbol3[A]
# ---------------------------------------------------------------------
# Ejercicio 3.2. Definir la función
# algunoArbol3 : (Arbol3[A], Callable[[A], bool]) -> bool
128 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 3.3. Un elemento de un árbol se dirá de nivel k si aparece
# en el árbol a distancia k de la raíz.
#
# Definir la función
# nivel : (int, Arbol3[A]) -> list[A]
# tal que nivel(k, a) es la lista de los elementos de nivel k del árbol
# a. Por ejemplo,
# >>> nivel(0, N3(7, N3(2, H3(5), H3(4)), H3(9)))
# [7]
# >>> nivel(1, N3(7, N3(2, H3(5), H3(4)), H3(9)))
# [2, 9]
# >>> nivel(2, N3(7, N3(2, H3(5), H3(4)), H3(9)))
# [5, 4]
# >>> nivel(3, N3(7, N3(2, H3(5), H3(4)), H3(9)))
# []
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.4. 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) -> Arbol3[int]
# tal que arbolFactorizacion(n) es el árbol de factorización de n. Por
# ejemplo,
# arbolFactorizacion(60) == N3(60,
# N3(6, H3(2), H3(3)),
# N3(10, H3(2), H3(5)))
# arbolFactorizacion(45) == N3(45, H3(5), N3(9, H3(3), H3(3)))
# arbolFactorizacion(7) == H3(7)
# arbolFactorizacion(9) == N3(9, H3(3), H3(3))
# arbolFactorizacion(14) == N3(14, H3(2), H3(7))
130 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# n. Por ejemplo,
# divisoresMedio2(30) == (5,6)
# divisoresMedio2(7) == (1,7)
# divisoresMedio2(16) == (4,4)
def divisoresMedio2(n: int) -> tuple[int, int]:
m = ceil(sqrt(n))
x = [y for y in range(m, n + 1) if n % y == 0][0]
return (n // x, x)
# ---------------------------------------------------------------------
# Ejercicio 4. Se consideran los árboles con operaciones booleanas
# definidos por
# @dataclass
# class ArbolB:
# pass
#
# @dataclass
# class H(ArbolB):
# x: bool
#
# @dataclass
# class Conj(ArbolB):
# i: ArbolB
# d: ArbolB
#
# @dataclass
# class Disy(ArbolB):
# i: ArbolB
# d: ArbolB
#
# @dataclass
# class Neg(ArbolB):
# a: ArbolB
#
132 Ejercicios de programación con Python
@dataclass
class ArbolB:
pass
@dataclass
class H(ArbolB):
x: bool
@dataclass
class Conj(ArbolB):
i: ArbolB
Capítulo 5. Tipos definidos y de datos algebraicos 133
d: ArbolB
@dataclass
class Disy(ArbolB):
i: ArbolB
d: ArbolB
@dataclass
class Neg(ArbolB):
a: ArbolB
# ---------------------------------------------------------------------
# Ejercicio 5. Los árboles generales se pueden representar mediante el
# siguiente tipo de dato
# @dataclass
# class ArbolG(Generic[A]):
# pass
#
134 Ejercicios de programación con Python
# @dataclass
# class NG(ArbolG[A]):
# x: A
# y: list[ArbolG[A]]
# Por ejemplo, los árboles
# 1 3 3
# / \ /|\ / | \
# 2 3 / | \ / | \
# | 5 4 7 5 4 7
# 4 | /\ | | / \
# 6 2 1 6 1 2 1
# / \
# 2 3
# |
# 4
# se representan por
# ejG1: ArbolG[int] = NG(1, [NG(2, []), NG(3, [NG(4, [])])])
# ejG2: ArbolG[int] = NG(3, [NG(5, [NG(6, [])]),
# NG(4, []),
# NG(7, [NG(2, []), NG(1, [])])])
# ejG3: ArbolG[int] = NG(3, [NG(5, [NG(6, [])]),
# NG(4, [NG(1, [NG(2, []),
# NG(3, [NG(4, [])])])]),
# NG(7, [NG(2, []), NG(1, [])])])
#
# Definir la función
# ramifica : (ArbolG[A], ArbolG[A], Callable[[A], bool]) -> ArbolG[A]
# tal que ramifica(a1, a2, p) es el árbol que resulta de añadir una copia
# del árbol a2 a los nodos de a1 que cumplen un predicado p. Por
# ejemplo,
# >>> ramifica(ejG1, NG(8, []), lambda x: x > 4)
# NG(1, [NG(2, []), NG(3, [NG(4, [])])])
# >>> ramifica(ejG1, NG(8, []), lambda x: x > 3)
# NG(1, [NG(2, []), NG(3, [NG(4, [NG(8, [])])])])
# >>> ramifica(ejG1, NG(8, []), lambda x: x > 2)
# NG(1, [NG(2, []), NG(3, [NG(4, [NG(8, [])]), NG(8, [])])])
# >>> ramifica(ejG1, NG(8, []), lambda x: x > 1)
# NG(1, [NG(2, [NG(8, [])]), NG(3, [NG(4, [NG(8, [])]), NG(8, [])])])
# >>> ramifica(ejG1, NG(8, []), lambda x: x > 0)
# NG(1, [NG(2, [NG(8, [])]),
Capítulo 5. Tipos definidos y de datos algebraicos 135
@dataclass
class ArbolG(Generic[A]):
pass
@dataclass
class NG(ArbolG[A]):
x: A
y: list[ArbolG[A]]
# ---------------------------------------------------------------------
# Librerías auxiliares
# ---------------------------------------------------------------------
A = TypeVar(”A”)
B = TypeVar(”B”)
# ---------------------------------------------------------------------
# Ejercicio 6.1. Las expresiones aritméticas básicas pueden
# representarse usando el siguiente tipo de datos
# @dataclass
# class Expr1:
# pass
#
# @dataclass
# class C1(Expr1):
# x: int
#
# @dataclass
# class S1(Expr1):
# x: Expr1
# y: Expr1
#
# @dataclass
# class P1(Expr1):
# x: Expr1
Capítulo 5. Tipos definidos y de datos algebraicos 137
# y: Expr1
# Por ejemplo, la expresión 2*(3+7) se representa por
# P1(C1(2), S1(C1(3), C1(7)))
#
# Definir la función
# valor : (Expr1) -> int:
# tal que valor(e) es el valor de la expresión aritmética e. Por
# ejemplo,
# valor(P1(C1(2), S1(C1(3), C1(7)))) == 20
# ---------------------------------------------------------------------
@dataclass
class Expr1:
pass
@dataclass
class C1(Expr1):
x: int
@dataclass
class S1(Expr1):
x: Expr1
y: Expr1
@dataclass
class P1(Expr1):
x: Expr1
y: Expr1
# ---------------------------------------------------------------------
138 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 7.1. Las expresiones aritméticas construidas con una
# variable (denotada por X), los números enteros y las operaciones de
# sumar y multiplicar se pueden representar mediante el tipo de datos
# Expr2 definido por
# @dataclass
# class Expr2:
# pass
#
# @dataclass
# class X(Expr2):
# pass
#
# @dataclass
# class C2(Expr2):
# x: int
#
# @dataclass
# class S2(Expr2):
# x: Expr2
Capítulo 5. Tipos definidos y de datos algebraicos 139
# y: Expr2
#
# @dataclass
# class P2(Expr2):
# x: Expr2
# y: Expr2
# Por ejemplo, la expresión X*(13+X) se representa por
# P2(X(), S2(C2(13), X()))
#
# Definir la función
# valorE : (Expr2, int) -> int
# tal que valorE(e, n) es el valor de la expresión e cuando se
# sustituye su variable por n. Por ejemplo,
# valorE(P2(X(), S2(C2(13), X())), 2) == 30
# ---------------------------------------------------------------------
@dataclass
class Expr2:
pass
@dataclass
class X(Expr2):
pass
@dataclass
class C2(Expr2):
x: int
@dataclass
class S2(Expr2):
x: Expr2
y: Expr2
@dataclass
class P2(Expr2):
x: Expr2
y: Expr2
case X():
return n
case C2(a):
return a
case S2(e1, e2):
return valorE(e1, n) + valorE(e2, n)
case P2(e1, e2):
return valorE(e1, n) * valorE(e2, n)
assert False
# ---------------------------------------------------------------------
# Ejercicio 7.2. Definir la función
# numVars : (Expr2) -> int
# tal que numVars(e) es el número de variables en la expresión e. Por
# ejemplo,
# numVars(C2(3)) == 0
# numVars(X()) == 1
# numVars(P2(X(), S2(C2(13), X()))) == 2
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.1. Las expresiones aritméticas con variables pueden
# representarse usando el siguiente tipo de datos
# @dataclass
# class Expr3:
# pass
#
Capítulo 5. Tipos definidos y de datos algebraicos 141
# @dataclass
# class C3(Expr3):
# x: int
#
# @dataclass
# class V3(Expr3):
# x: str
#
# @dataclass
# class S3(Expr3):
# x: Expr3
# y: Expr3
#
# @dataclass
# class P3(Expr3):
# x: Expr3
# y: Expr3
#
# Por ejemplo, la expresión 2*(a+5) se representa por
# P3(C3(2), S3(V3('a'), C3(5)))
#
# Definir la función
# valor3 : (Expr3, list[tuple[str, int]]) -> int
# tal que valor3(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,
# λ> valor3(P3(C3(2), S3(V3('a'), V3('b'))), [('a', 2), ('b', 5)])
# 14
# ---------------------------------------------------------------------
@dataclass
class Expr3:
pass
@dataclass
class C3(Expr3):
x: int
@dataclass
class V3(Expr3):
142 Ejercicios de programación con Python
x: str
@dataclass
class S3(Expr3):
x: Expr3
y: Expr3
@dataclass
class P3(Expr3):
x: Expr3
y: Expr3
# ---------------------------------------------------------------------
# Ejercicio 8.2. Definir la función
# sumas : (Expr3) -> int
# tal que sumas(e) es el número de sumas en la expresión e. Por
# ejemplo,
# sumas(P3(V3('z'), S3(C3(3), V3('x')))) == 1
# sumas(S3(V3('z'), S3(C3(3), V3('x')))) == 2
# sumas(P3(V3('z'), P3(C3(3), V3('x')))) == 0
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.3. Definir la función
# sustitucion : (Expr3, list[tuple[str, int]]) -> Expr3
# tal que sustitucion(e s) es la expresión obtenida sustituyendo las
# variables de la expresión e según se indica en la sustitución s. Por
# ejemplo,
# >>> sustitucion(P3(V3('z'), S3(C3(3), V3('x'))), [('x', 7), ('z', 9)])
# P3(C3(9), S3(C3(3), C3(7)))
# >>> sustitucion(P3(V3('z'), S3(C3(3), V3('y'))), [('x', 7), ('z', 9)])
# P3(C3(9), S3(C3(3), V3('y')))
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8.4. Definir la función
# reducible : (Expr3) -> 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,
144 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 9. Las expresiones aritméticas generales se pueden definir
# usando el siguiente tipo de datos
# @dataclass
# class Expr4:
# pass
#
# @dataclass
# class C4(Expr4):
# x: int
#
# @dataclass
# class Y(Expr4):
# pass
#
Capítulo 5. Tipos definidos y de datos algebraicos 145
# @dataclass
# class S4(Expr4):
# x: Expr4
# y: Expr4
#
# @dataclass
# class R4(Expr4):
# x: Expr4
# y: Expr4
#
# @dataclass
# class P4(Expr4):
# x: Expr4
# y: Expr4
#
# @dataclass
# class E4(Expr4):
# x: Expr4
# y: int
# Por ejemplo, la expresión
# 3*y - (y+2)^7
# se puede definir por
# R4(P4(C4(3), Y()), E4(S4(Y(), C4(2)), 7))
#
# Definir la función
# maximo : (Expr4, 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(E4(S4(C4(10), P4(R4(C4(1), Y()), Y())), 2), list(range(-3, 4)))
# (100, [0, 1])
# ---------------------------------------------------------------------
@dataclass
class Expr4:
pass
@dataclass
class C4(Expr4):
x: int
146 Ejercicios de programación con Python
@dataclass
class Y(Expr4):
pass
@dataclass
class S4(Expr4):
x: Expr4
y: Expr4
@dataclass
class R4(Expr4):
x: Expr4
y: Expr4
@dataclass
class P4(Expr4):
x: Expr4
y: Expr4
@dataclass
class E4(Expr4):
x: Expr4
y: int
# ---------------------------------------------------------------------
# Ejercicio 10. 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 Expr5:
# pass
#
# @dataclass
# class C5(Expr5):
# x: int
#
# @dataclass
# class Ap(Expr5):
# o: Op
# x: Expr5
# y: Expr5
# Por ejemplo, la expresión
# (7-3)+(2*5)
# se representa por
# Ap(Op.S, Ap(Op.R, C5(7), C5(3)), Ap(Op.M, C5(2), C5(5)))
#
# Definir la función
# valorEG : (Expr5) -> int
# tal que valorEG(e) es el valor de la expresión e. Por ejemplo,
# >>> valorEG(Ap(Op.S, Ap(Op.R, C5(7), C5(3)), Ap(Op.M, C5(2), C5(5))))
# 14
# >>> valorEG(Ap(Op.M, Ap(Op.R, C5(7), C5(3)), Ap(Op.S, C5(2), C5(5))))
# 28
# ---------------------------------------------------------------------
@dataclass
class Expr5:
pass
@dataclass
class C5(Expr5):
x: int
@dataclass
class Ap(Expr5):
o: Op
x: Expr5
y: Expr5
# ---------------------------------------------------------------------
# Ejercicio 11. 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
Capítulo 5. Tipos definidos y de datos algebraicos 149
#
# @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
class ExpV:
pass
@dataclass
class Vec(ExpV):
x: int
y: int
@dataclass
class Sum(ExpV):
x: ExpV
y: ExpV
150 Ejercicios de programación con Python
@dataclass
class Mul(ExpV):
x: int
y: ExpV
# 1ª solución
# ===========
# 2ª solución
# ===========
Algorítmica
153
Capítulo 6
155
156 Ejercicios de programación con Python
__all__ = [
'Pila',
'vacia',
'apila',
'esVacia',
'cima',
'desapila',
'pilaAleatoria'
]
from [Link] import (Pila, apila, cima, desapila, esVacia,
pilaAleatoria, vacia)
# 4 | 3 | 2 | 5
# >>> [Link]()
# 4
# >>> [Link]()
# >>> print(p)
# 3 | 2 | 5
# >>> [Link]()
# False
# >>> p = Pila()
# >>> [Link]()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> print(vacia())
# -
# >>> print(apila(4, apila(3, apila(2, apila(5, vacia())))))
# 4 | 3 | 2 | 5
# >>> print(cima(apila(4, apila(3, apila(2, apila(5, vacia()))))))
# 4
# >>> print(desapila(apila(4, apila(3, apila(2, apila(5, vacia()))))))
# 3 | 2 | 5
# >>> print(esVacia(apila(4, apila(3, apila(2, apila(5, vacia()))))))
# False
# >>> print(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]):
_elementos: list[A] = field(default_factory=list)
# Generador de pilas
# ==================
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.25s
Capítulo 6. El tipo abstracto de datos de las pilas 161
# >>> print(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]):
_elementos: deque[A] = field(default_factory=deque)
# Generador de pilas
# ==================
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.25s
# ---------------------------------------------------------------------
# Importación de librerías --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# listaApila : (list[A]) -> Pila[A]
# tal que listaApila(xs) es la pila formada por los elementos de xs.
# Por ejemplo,
# >>> print(listaApila([3, 2, 5]))
# 5 | 2 | 3
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return aux(list(reversed(ys)))
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()))
Capítulo 6. El tipo abstracto de datos de las pilas 167
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# pilaALista : (Pila[A]) -> list[A]
# tal que pilaAlista(p) es la lista formada por los elementos de la
# lista p. Por ejemplo,
# >>> ej = apila(5, apila(2, apila(3, vacia())))
# >>> pilaAlista(ej)
# [3, 2, 5]
# >>> print(ej)
# 5 | 2 | 3
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
168 Ejercicios de programación con Python
# ===========
# Comprobación de equivalencia
# ============================
@given(p=pilaAleatoria())
def test_pilaAlista(p: Pila[int]) -> None:
assert pilaAlista(p) == pilaAlista2(p)
assert pilaAlista(p) == pilaAlista3(p)
# ---------------------------------------------------------------------
# Ejercicio 3. Comprobar con Hypothesis que ambas funciones son
# inversas; es decir,
# pilaAlista(listaApila(xs)) == xs
# listaApila(pilaAlista(p)) == p
# ---------------------------------------------------------------------
# La primera propiedad es
@given([Link]([Link]()))
def test_1_listaApila(xs: list[int]) -> None:
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
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
Capítulo 6. El tipo abstracto de datos de las pilas 169
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
if p(cq):
[Link](cq)
return r
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# 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
# ---------------------------------------------------------------------
Capítulo 6. El tipo abstracto de datos de las pilas 171
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
p1 = deepcopy(p)
return mapPila3Aux(f, p1)
# 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
# ---------------------------------------------------------------------
# Ejercicio 6. 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,
# >>> pertenecePila(2, apila(5, apila(2, apila(3, vacia()))))
# True
Capítulo 6. El tipo abstracto de datos de las pilas 173
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
[Link]()
if x == cp:
return True
return False
# La propiedad es
@given(x=[Link](), 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
# ---------------------------------------------------------------------
# Ejercicio 7. 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
# ---------------------------------------------------------------------
# 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
# ===========
# ================================================
# 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
# ---------------------------------------------------------------------
# Ejercicio 8. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 6. El tipo abstracto de datos de las pilas 177
# 3ª solución
# ===========
# 4ª solución
# ===========
q1 = deepcopy(p1)
q2 = deepcopy(p2)
return prefijoPila4Aux(q1, q2)
# 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
# ---------------------------------------------------------------------
# Ejercicio 9. 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()))))
# >>> ej3 = apila(2, apila(7, apila(3, apila(5, vacia()))))
# >>> subPila(ej1, ej2)
# True
# >>> subPila(ej1, ej3)
# False
# ---------------------------------------------------------------------
# 1ª solución
# ===========
dp2 = desapila(p2)
if cp1 == cp2:
return prefijoPila(dp1, dp2) or subPila1(p1, dp2)
return subPila1(p1, dp2)
# 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
# ---------------------------------------------------------------------
# Ejercicio 10. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 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
# ---------------------------------------------------------------------
# Ejercicio 11.1. 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,
# >>> print(ordenaInserPila(apila(4, apila(1, apila(3, vacia())))))
# 1 | 3 | 4
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 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
184 Ejercicios de programación con Python
assert ordenaInserPila3(p) == r
# ---------------------------------------------------------------------
# Ejercicio 11.2. Comprobar con Hypothesis que la pila
# ordenaInserPila(p) está ordenada.
# ---------------------------------------------------------------------
# La propiedad es
@given(p=pilaAleatoria())
def test_ordenadaOrdenaInserPila(p: Pila[int]) -> None:
ordenadaPila(ordenaInserPila1(p))
# ---------------------------------------------------------------------
# Ejercicio 12. 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()))))
# >>> print(ej)
# 3 | 1 | 3 | 5
# >>> print(nubPila1(ej))
# 1 | 3 | 5
# ---------------------------------------------------------------------
# 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
# ---------------------------------------------------------------------
# Ejercicio 13. Definir la función
# maxPila : (Pila[A]) -> A
# tal que maxPila(p) sea el mayor de los elementos de la pila p. Por
# ejemplo,
# >>> maxPila(apila(3, apila(5, apila(1, vacia()))))
# 5
# ---------------------------------------------------------------------
186 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
return r
# 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
assert maxPila4(p) == r
# ---------------------------------------------------------------------
# Comprobación de las propiedades
# ---------------------------------------------------------------------
# La comprobación es
# src> poetry run pytest -v el_TAD_de_las_pilas.py
# test_listaApila PASSED
# test_pilaAlista PASSED
# test_1_listaApila PASSED
# test_2_listaApila PASSED
# test_filtraPila PASSED
# test_mapPila PASSED
# test_pertenecePila PASSED
# test_contenidaPila PASSED
# test_prefijoPila PASSED
# test_subPila PASSED
# test_ordenadaPila PASSED
# test_ordenaInserPila PASSED
# test_ordenadaOrdenaInserPila PASSED
# test_nubPila PASSED
# test_maxPila PASSED
# 15 passed in 2.91s
188 Ejercicios de programación con Python
Capítulo 7
189
190 Ejercicios de programación con Python
__all__ = [
'Cola',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
'colaAleatoria'
]
# >>> [Link](5)
# >>> [Link](2)
# >>> [Link](3)
# >>> [Link](4)
# >>> print(c)
# 5 | 2 | 3 | 4
# >>> [Link]()
# 5
# >>> [Link]()
# >>> print(c)
# 2 | 3 | 4
# >>> [Link]()
# False
# >>> c = Cola()
# >>> [Link]()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> print(vacia())
# -
# >>> print(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5 | 2 | 3 | 4
# >>> primero(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5
# >>> print(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',
192 Ejercicios de programación con Python
'esVacia',
'colaAleatoria'
]
A = TypeVar('A')
@dataclass
class Cola(Generic[A]):
_elementos: list[A] = field(default_factory=list)
”””
return not self._elementos
# Generador de colas
# ==================
@given(c=colaAleatoria(), x=[Link]())
Capítulo 7. El tipo abstracto de datos de las colas 195
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.24s
# >>> print(vacia())
# -
# >>> print(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5 | 2 | 3 | 4
# >>> primero(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5
# >>> print(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]):
Capítulo 7. El tipo abstracto de datos de las colas 197
Parámetro:
- c (Cola): La cola con la que se va a comparar.
if not xs:
[Link](0, y)
# Se invierte la segunda lista y se asigna a la primera
self._primera = ys[::-1]
self._segunda = []
else:
# Si hay elementos en la primera lista, se inserta en la segunda
[Link](0, y)
return c
# Generador de colas
# ==================
@given(c=colaAleatoria(), x=[Link]())
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 [Link]
# 2 passed in 0.40s
# >>> [Link](2)
# >>> [Link](3)
# >>> [Link](4)
# >>> print(c)
# 5 | 2 | 3 | 4
# >>> [Link]()
# 5
# >>> [Link]()
# >>> print(c)
# 2 | 3 | 4
# >>> [Link]()
# False
# >>> c = Cola()
# >>> [Link]()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> print(vacia())
# -
# >>> print(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5 | 2 | 3 | 4
# >>> primero(inserta(4, inserta(3, inserta(2, inserta(5, vacia())))))
# 5
# >>> print(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',
202 Ejercicios de programación con Python
'colaAleatoria'
]
A = TypeVar('A')
@dataclass
class Cola(Generic[A]):
_elementos: deque[A] = field(default_factory=deque)
# Generador de colas
# ==================
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 [Link](_creaCola, [Link]([Link]()))
@given(c=colaAleatoria(), x=[Link]())
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 [Link]
# 1 passed in 0.24s
# ---------------------------------------------------------------------
# Importación de librerías --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.1 Definir la función
# listaAcola : (list[A]) -> Cola[A]
# tal que listaAcola(xs) es la cola formada por los elementos de xs. Por
# ejemplo,
# >>> print(listaAcola([3, 2, 5]))
# 3 | 2 | 5
# ---------------------------------------------------------------------
# 1ª solución
def listaAcola(ys: list[A]) -> Cola[A]:
def aux(xs: list[A]) -> Cola[A]:
if not xs:
return vacia()
return inserta(xs[0], aux(xs[1:]))
return aux(list(reversed(ys)))
# 2ª solución
def listaAcola2(xs: list[A]) -> Cola[A]:
p: Cola[A] = Cola()
for x in xs:
[Link](x)
return p
# Comprobación de equivalencia
@given([Link]([Link]()))
def test_listaAcola(xs: list[int]) -> None:
assert listaAcola(xs) == listaAcola2(xs)
# ---------------------------------------------------------------------
# Ejercicio 1.2. Definir la función
# colaAlista : (Cola[A]) -> list[A]
# tal que colaAlista(c) es la lista formada por los elementos de la cola
# c. Por ejemplo,
# >>> ej = inserta(5, inserta(2, inserta(3, vacia())))
# >>> colaAlista(ej)
# [3, 2, 5]
# >>> print(ej)
# 3 | 2 | 5
Capítulo 7. El tipo abstracto de datos de las colas 207
# ---------------------------------------------------------------------
# 1ª solución
def colaAlista(c: Cola[A]) -> list[A]:
if esVacia(c):
return []
pc = primero(c)
rc = resto(c)
return [pc] + colaAlista(rc)
# 2ª solución
def colaAlista2Aux(c: Cola[A]) -> list[A]:
if [Link]():
return []
pc = [Link]()
[Link]()
return [pc] + colaAlista2Aux(c)
# 3ª solución
def colaAlista3Aux(c: Cola[A]) -> list[A]:
r = []
while not [Link]():
[Link]([Link]())
[Link]()
return r
# Comprobación de equivalencia
@given(p=colaAleatoria())
def test_colaAlista(p: Cola[int]) -> None:
assert colaAlista(p) == colaAlista2(p)
assert colaAlista(p) == colaAlista3(p)
208 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 1.3. Comprobar con Hypothesis que ambas funciones son
# inversas; es decir,
# colaAlista(listaAcola(xs)) == xs
# listaAcola(colaAlista(c)) == c
# ---------------------------------------------------------------------
@given([Link]([Link]()))
def test_1_listaAcola(xs: list[int]) -> None:
assert colaAlista(listaAcola(xs)) == xs
@given(c=colaAleatoria())
def test_2_listaAcola(c: Cola[int]) -> None:
assert listaAcola(colaAlista(c)) == c
# ---------------------------------------------------------------------
# Ejercicio 2. 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
# ---------------------------------------------------------------------
# 1ª solución
def ultimoCola(c: Cola[A]) -> A:
if esVacia(c):
raise ValueError(”cola vacia”)
pc = primero(c)
rc = resto(c)
if esVacia(rc):
return pc
return ultimoCola(rc)
# 2ª solución
def ultimoCola2Aux(c: Cola[A]) -> A:
if [Link]():
raise ValueError(”cola vacia”)
Capítulo 7. El tipo abstracto de datos de las colas 209
pc = primero(c)
[Link]()
if [Link]():
return pc
return ultimoCola2(c)
# 3ª solución
def ultimoCola3(c: Cola[A]) -> A:
if esVacia(c):
raise ValueError(”cola vacia”)
while not esVacia(resto(c)):
c = resto(c)
return primero(c)
# 4ª solución
def ultimoCola4Aux(c: Cola[A]) -> A:
if [Link]():
raise ValueError(”cola vacia”)
r = primero(c)
while not [Link]():
[Link]()
if not [Link]():
r = primero(c)
return r
# 5ª solución
def ultimoCola5(c: Cola[A]) -> A:
if esVacia(c):
raise ValueError(”cola vacia”)
return colaAlista(c)[-1]
# Comprobación de equivalencia
210 Ejercicios de programación con Python
@given(c=colaAleatoria())
def test_ultimoCola(c: Cola[int]) -> None:
assume(not esVacia(c))
r = ultimoCola(c)
assert ultimoCola2(c) == r
assert ultimoCola3(c) == r
assert ultimoCola4(c) == r
assert ultimoCola5(c) == r
# ---------------------------------------------------------------------
# Ejercicio 3. 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
# ---------------------------------------------------------------------
# 1ª solución
def longitudCola1(c: Cola[A]) -> int:
if esVacia(c):
return 0
return 1 + longitudCola1(resto(c))
# 2ª solución
def longitudCola2(c: Cola[A]) -> int:
return len(colaAlista(c))
# 3ª solución
def longitudCola3Aux(c: Cola[A]) -> int:
if [Link]():
return 0
[Link]()
return 1 + longitudCola3Aux(c)
# 4ª solución
Capítulo 7. El tipo abstracto de datos de las colas 211
# 5ª solución
def longitudCola5Aux(c: Cola[A]) -> int:
r = 0
while not [Link]():
r = r + 1
[Link]()
return r
# Comprobación de equivalencia
@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
# ---------------------------------------------------------------------
# Ejercicio 4. 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())))
212 Ejercicios de programación con Python
# False
# ---------------------------------------------------------------------
# 1ª solución
def todosVerifican1(p: Callable[[A], bool], c: Cola[A]) -> bool:
if esVacia(c):
return True
pc = primero(c)
rc = resto(c)
return p(pc) and todosVerifican1(p, rc)
# 2ª solución
def todosVerifican2(p: Callable[[A], bool], c: Cola[A]) -> bool:
return all(p(x) for x in colaAlista(c))
# 3ª solución
def todosVerifican3Aux(p: Callable[[A], bool], c: Cola[A]) -> bool:
if [Link]():
return True
pc = [Link]()
[Link]()
return p(pc) and todosVerifican3Aux(p, c)
# 4ª solución
def todosVerifican4Aux(p: Callable[[A], bool], c: Cola[A]) -> bool:
if [Link]():
return True
pc = [Link]()
[Link]()
return p(pc) and todosVerifican4Aux(p, c)
# 5ª solución
Capítulo 7. El tipo abstracto de datos de las colas 213
# Comprobación de equivalencia
@given(c=colaAleatoria())
def test_todosVerifican(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
# ---------------------------------------------------------------------
# Ejercicio 5. 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
# ---------------------------------------------------------------------
# 1ª solución
def algunoVerifica1(p: Callable[[A], bool], c: Cola[A]) -> bool:
if esVacia(c):
return False
pc = primero(c)
rc = resto(c)
return p(pc) or algunoVerifica1(p, rc)
# 2ª solución
214 Ejercicios de programación con Python
# 3ª solución
def algunoVerifica3Aux(p: Callable[[A], bool], c: Cola[A]) -> bool:
if [Link]():
return False
pc = [Link]()
[Link]()
return p(pc) or algunoVerifica3Aux(p, c)
# 4ª solución
def algunoVerifica4Aux(p: Callable[[A], bool], c: Cola[A]) -> bool:
if [Link]():
return False
pc = [Link]()
[Link]()
return p(pc) or algunoVerifica4Aux(p, c)
# 5ª solución
def algunoVerifica5Aux(p: Callable[[A], bool], c: Cola[A]) -> bool:
while not [Link]():
if p([Link]()):
return True
[Link]()
return False
# Comprobación de equivalencia
Capítulo 7. El tipo abstracto de datos de las colas 215
@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
# ---------------------------------------------------------------------
# Ejercicio 6. 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())))
# >>> print(ej1)
# 2 | 3
# >>> print(ej2)
# 4 | 3 | 5
# >>> print(extiendeCola(ej1, ej2))
# 2 | 3 | 4 | 3 | 5
# >>> print(extiendeCola(ej2, ej1))
# 4 | 3 | 5 | 2 | 3
# ---------------------------------------------------------------------
# 1ª solución
def extiendeCola(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
if esVacia(c2):
return c1
pc2 = primero(c2)
rc2 = resto(c2)
return extiendeCola(inserta(pc2, c1), rc2)
# 2ª solución
def extiendeCola2(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
return listaAcola(colaAlista(c1) + colaAlista(c2))
# 3ª solución
def extiendeCola3Aux(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
216 Ejercicios de programación con Python
if [Link]():
return c1
pc2 = [Link]()
[Link]()
return extiendeCola(inserta(pc2, c1), c2)
# 4ª solución
def extiendeCola4Aux(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
r = c1
while not esVacia(c2):
r = inserta(primero(c2), r)
c2 = resto(c2)
return r
# 5ª solución
def extiendeCola5Aux(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
r = c1
while not [Link]():
[Link](primero(c2))
[Link]()
return r
# Comprobación de equivalencia
@given(c1=colaAleatoria(), c2=colaAleatoria())
def test_extiendeCola(c1: Cola[int], c2: Cola[int]) -> None:
r = extiendeCola(c1, c2)
assert extiendeCola2(c1, c2) == r
Capítulo 7. El tipo abstracto de datos de las colas 217
# ---------------------------------------------------------------------
# Ejercicio 7. 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()))
# >>> ej2 = inserta(0, inserta(7, inserta(4, inserta(9, vacia()))))
# >>> print(intercalaColas(ej1, ej2))
# 5 | 9 | 3 | 4 | 7 | 0
# >>> print(intercalaColas(ej2, ej1))
# 9 | 5 | 4 | 3 | 7 | 0
# ---------------------------------------------------------------------
# 1ª solución
def intercalaColas(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
if esVacia(c1):
return c2
if esVacia(c2):
return c1
pc1 = primero(c1)
rc1 = resto(c1)
pc2 = primero(c2)
rc2 = resto(c2)
return extiendeCola(inserta(pc2, inserta(pc1, vacia())),
intercalaColas(rc1, rc2))
# 2ª solución
def intercalaColas2(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
def aux(d1: Cola[A], d2: Cola[A], d3: Cola[A]) -> Cola[A]:
if esVacia(d1):
return extiendeCola(d3, d2)
if esVacia(d2):
return extiendeCola(d3, d1)
pd1 = primero(d1)
rd1 = resto(d1)
pd2 = primero(d2)
218 Ejercicios de programación con Python
rd2 = resto(d2)
return aux(rd1, rd2, inserta(pd2, inserta(pd1, d3)))
# 3ª solución
def intercalaListas(xs: list[A], ys: list[A]) -> list[A]:
if not xs:
return ys
if not ys:
return xs
return [xs[0], ys[0]] + intercalaListas(xs[1:], ys[1:])
# 4ª solución
def intercalaColas4Aux(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
if [Link]():
return c2
if [Link]():
return c1
pc1 = [Link]()
[Link]()
pc2 = [Link]()
[Link]()
return extiendeCola(inserta(pc2, inserta(pc1, vacia())),
intercalaColas4Aux(c1, c2))
# 5ª solución
def intercalaColas5Aux(c1: Cola[A], c2: Cola[A]) -> Cola[A]:
r: Cola[A] = vacia()
while not esVacia(c1) and not esVacia(c2):
pc1 = primero(c1)
[Link]()
Capítulo 7. El tipo abstracto de datos de las colas 219
pc2 = primero(c2)
[Link]()
r = inserta(pc2, inserta(pc1, r))
if esVacia(c1):
return extiendeCola(r, c2)
return extiendeCola(r, c1)
# Comprobación de equivalencia
@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
assert intercalaColas5(c1, c2) == r
# ---------------------------------------------------------------------
# Ejercicio 8. 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()))))
# >>> print(agrupaColas([ej1]))
# 5 | 2
# >>> print(agrupaColas([ej1, ej2]))
# 5 | 4 | 2 | 7 | 3
# >>> print(agrupaColas([ej1, ej2, ej3]))
# 5 | 6 | 4 | 1 | 2 | 0 | 7 | 9 | 3
# ---------------------------------------------------------------------
# 1ª solución
def agrupaColas1(cs: list[Cola[A]]) -> Cola[A]:
220 Ejercicios de programación con Python
if not cs:
return vacia()
if len(cs) == 1:
return cs[0]
return agrupaColas1([intercalaColas(cs[0], cs[1])] + cs[2:])
# 2ª solución
def agrupaColas2(cs: list[Cola[A]]) -> Cola[A]:
return reduce(intercalaColas, cs, vacia())
# Comprobación de equivalencia
@given([Link](colaAleatoria(), max_size=4))
def test_agrupaCola(cs: list[Cola[int]]) -> None:
assert agrupaColas1(cs) == agrupaColas2(cs)
# ---------------------------------------------------------------------
# Ejercicio 9. 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
# ---------------------------------------------------------------------
# 1ª solución
def perteneceCola(x: A, c: Cola[A]) -> bool:
if esVacia(c):
return False
return x == primero(c) or perteneceCola(x, resto(c))
# 2ª solución
def perteneceCola2(x: A, c: Cola[A]) -> bool:
return x in colaAlista(c)
# 3ª solución
def perteneceCola3Aux(x: A, c: Cola[A]) -> bool:
if [Link]():
return False
Capítulo 7. El tipo abstracto de datos de las colas 221
pc = [Link]()
[Link]()
return x == pc or perteneceCola3Aux(x, c)
# 4ª solución
def perteneceCola4Aux(x: A, c: Cola[A]) -> bool:
while not [Link]():
pc = [Link]()
[Link]()
if x == pc:
return True
return False
# ---------------------------------------------------------------------
# Ejercicio 10. 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
# >>> contenidaCola(ej2, ej3)
222 Ejercicios de programación con Python
# False
# ---------------------------------------------------------------------
# 1ª solución
def contenidaCola1(c1: Cola[A], c2: Cola[A]) -> bool:
if esVacia(c1):
return True
return perteneceCola(primero(c1), c2) and contenidaCola1(resto(c1), c2)
# 2ª solución
def contenidaCola2(c1: Cola[A], c2: Cola[A]) -> bool:
return set(colaAlista(c1)) <= set(colaAlista(c2))
# 3ª solución
def contenidaCola3Aux(c1: Cola[A], c2: Cola[A]) -> bool:
if [Link]():
return True
pc1 = [Link]()
[Link]()
return perteneceCola(pc1, c2) and contenidaCola1(c1, c2)
# 4ª solución
def contenidaCola4Aux(c1: Cola[A], c2: Cola[A]) -> bool:
while not [Link]():
pc1 = [Link]()
[Link]()
if not perteneceCola(pc1, c2):
return False
return True
# ---------------------------------------------------------------------
# Ejercicio 11. 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
# ---------------------------------------------------------------------
# 1ª solución
def prefijoCola(c1: Cola[A], c2: Cola[A]) -> bool:
if esVacia(c1):
return True
if esVacia(c2):
return False
return primero(c1) == primero(c2) and prefijoCola(resto(c1), resto(c2))
# 2ª solución
def esPrefijoLista(xs: list[A], ys: list[A]) -> bool:
return ys[:len(xs)] == xs
# 3ª solución
def prefijoCola3Aux(c1: Cola[A], c2: Cola[A]) -> bool:
if [Link]():
return True
if [Link]():
224 Ejercicios de programación con Python
return False
cc1 = [Link]()
[Link]()
cc2 = [Link]()
[Link]()
return cc1 == cc2 and prefijoCola3(c1, c2)
# 4ª solución
def prefijoCola4Aux(c1: Cola[A], c2: Cola[A]) -> bool:
while not [Link]() and not [Link]():
if [Link]() != [Link]():
return False
[Link]()
[Link]()
return [Link]()
# ---------------------------------------------------------------------
# Ejercicio 12. 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()))
Capítulo 7. El tipo abstracto de datos de las colas 225
# 1ª solución
def subCola1(c1: Cola[A], c2: Cola[A]) -> bool:
if esVacia(c1):
return True
if esVacia(c2):
return False
pc1 = primero(c1)
rc1 = resto(c1)
pc2 = primero(c2)
rc2 = resto(c2)
if pc1 == pc2:
return prefijoCola(rc1, rc2) or subCola1(c1, rc2)
return subCola1(c1, rc2)
# 2ª solución
def sublista(xs: list[A], ys: list[A]) -> bool:
return any(xs == ys[i:i+len(xs)] for i in range(len(ys) - len(xs) + 1))
# 3ª solución
def subCola3Aux(c1: Cola[A], c2: Cola[A]) -> bool:
if [Link]():
return True
if [Link]():
return False
if [Link]() != [Link]():
[Link]()
return subCola3Aux(c1, c2)
q1 = deepcopy(c1)
[Link]()
226 Ejercicios de programación con Python
[Link]()
return prefijoCola(c1, c2) or subCola3Aux(q1, c2)
# ---------------------------------------------------------------------
# Ejercicio 13. 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
# ---------------------------------------------------------------------
# 1ª solución
def ordenadaCola(c: Cola[A]) -> bool:
if esVacia(c):
return True
pc = primero(c)
rc = resto(c)
if esVacia(rc):
return True
prc = primero(rc)
return pc <= prc and ordenadaCola(rc)
# 2ª solución
def ordenadaLista(xs: list[A]) -> bool:
return all((x <= y for (x, y) in zip(xs, xs[1:])))
Capítulo 7. El tipo abstracto de datos de las colas 227
# 3ª solución
def ordenadaCola3Aux(c: Cola[A]) -> bool:
if [Link]():
return True
pc = [Link]()
[Link]()
if [Link]():
return True
return pc <= [Link]() and ordenadaCola3Aux(c)
# 4ª solución
def ordenadaCola4Aux(c: Cola[A]) -> bool:
while not [Link]():
pc = [Link]()
[Link]()
if not [Link]() and pc > [Link]():
return False
return True
# ---------------------------------------------------------------------
228 Ejercicios de programación con Python
# 1ª solución
def maxCola1(c: Cola[A]) -> A:
pc = primero(c)
rc = resto(c)
if esVacia(rc):
return pc
return max(pc, maxCola1(rc))
# 2ª solución
def maxCola2(c: Cola[A]) -> A:
return max(colaAlista(c))
# 3ª solución
def maxCola3Aux(c: Cola[A]) -> A:
pc = [Link]()
[Link]()
if esVacia(c):
return pc
return max(pc, maxCola3Aux(c))
# 4ª solución
def maxCola4Aux(c: Cola[A]) -> A:
r = [Link]()
while not esVacia(c):
pc = [Link]()
if pc > r:
r = pc
[Link]()
Capítulo 7. El tipo abstracto de datos de las colas 229
return r
# ---------------------------------------------------------------------
# Comprobaciones
# ---------------------------------------------------------------------
231
232 Ejercicios de programación con Python
__all__ = [
'CPrioridad',
'vacia',
'inserta',
'primero',
'resto',
'esVacia',
]
# >>> [Link]()
# 2
# >>> [Link]()
# >>> c
# 3 | 4 | 5
# >>> [Link]()
# False
# >>> c = CPrioridad()
# >>> [Link]()
# True
#
# Además se definen las correspondientes funciones. Por ejemplo,
# >>> 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',
]
234 Ejercicios de programación con Python
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
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 [Link]
# 2 passed in 0.54s
238 Ejercicios de programación con Python
Capítulo 9
239
240 Ejercicios de programación con Python
__all__ = [
'Conj',
'vacio',
'inserta',
'menor',
'elimina',
'pertenece',
'esVacio',
'conjuntoAleatorio'
]
# >>> [Link](3)
# False
# >>> [Link]()
# False
# >>> c = Conj()
# >>> [Link]()
# True
# >>> c = Conj()
# >>> [Link](2)
# >>> [Link](5)
# >>> d = Conj()
# >>> [Link](5)
# >>> [Link](2)
# >>> [Link](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.
Capítulo 9. El tipo abstracto de datos de los conjuntos 243
__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)
y separados por ”, ”.
”””
return '{' + ', '.join(str(x) for x in sorted(list(set(self._elementos)))
return x in self._elementos
return [Link](x)
# Generador de conjuntos
# ======================
assert esVacio(vacio())
assert not esVacio(inserta(x, c))
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.33s
# >>> c = Conj()
# >>> [Link]()
# True
# >>> c = Conj()
# >>> [Link](2)
# >>> [Link](5)
# >>> d = Conj()
# >>> [Link](5)
# >>> [Link](2)
# >>> [Link](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__ = [
Capítulo 9. El tipo abstracto de datos de los conjuntos 249
'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 [Link]()
# Generador de conjuntos
# ======================
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.26s
# >>> c = Conj()
# >>> [Link](2)
# >>> [Link](5)
# >>> d = Conj()
# >>> [Link](5)
# >>> [Link](2)
# >>> [Link](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',
Capítulo 9. El tipo abstracto de datos de los conjuntos 255
'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)
”””
Añade el elemento x al conjunto.
”””
if x not in self._elementos:
insort_left(self._elementos, x)
return c
# Generador de conjuntos
# ======================
258 Ejercicios de programación con Python
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.13s
Capítulo 9. El tipo abstracto de datos de los conjuntos 259
# >>> [Link](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'
]
Capítulo 9. El tipo abstracto de datos de los conjuntos 261
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 [Link]()
# Generador de conjuntos
# ======================
# La comprobación es
# > poetry run pytest -q [Link]
# 1 passed in 0.22s
# =====================================================================
# Librerías auxiliares
# =====================================================================
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
B = TypeVar('B', bound=Comparable)
# =====================================================================
# Ejercicios
# =====================================================================
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# listaAconjunto : (list[A]) -> Conj[A]
# tal que
# listaAconjunto(xs) es el conjunto formado por los elementos de xs.
# Por ejemplo,
# >>> listaAconjunto([3, 2, 5])
# {2, 3, 5}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
266 Ejercicios de programación con Python
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()))
def test_listaAconjunto(xs: list[int]) -> None:
r = listaAconjunto(xs)
assert listaAconjunto2(xs) == r
assert listaAconjunto3(xs) == r
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# conjuntoAlista : (Conj[A]) -> list[A]
# tal que 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]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
@given(c=conjuntoAleatorio())
def test_conjuntoAlista(c: Conj[int]) -> None:
r = conjuntoAlista(c)
assert conjuntoAlista2(c) == r
assert conjuntoAlista3(c) == r
268 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 3. Comprobar con Hypothesis que ambas funciones son inversa;
# es decir,
# conjuntoAlista (listaAconjunto xs) = sorted(list(set(xs)))
# listaAconjunto (conjuntoAlista c) = c
# ---------------------------------------------------------------------
# La primera propiedad es
@given([Link]([Link]()))
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
# ---------------------------------------------------------------------
# Ejercicio 4. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
return False
[Link](mc1)
return True
# 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
# ---------------------------------------------------------------------
# Ejercicio 5. 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# unitario :: Ord a => a -> Conj a
# tal que (unitario x) es el conjunto {x}. Por ejemplo,
# unitario 5 == {5}
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
c = elimina(menor(c), c)
return r
# 4ª solución
# ===========
# 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
# ---------------------------------------------------------------------
# Ejercicio 8. 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}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
Capítulo 9. El tipo abstracto de datos de los conjuntos 273
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
274 Ejercicios de programación con Python
# ============================
# 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
# ---------------------------------------------------------------------
# Ejercicio 9. 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}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
r: Conj[A] = vacio()
for c in cs:
r = union(c, r)
return r
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](conjuntoAleatorio(), max_size=10))
def test_unionG(cs: list[Conj[int]]) -> None:
r = unionG(cs)
assert unionG2(cs) == r
assert unionG3(cs) == r
# ---------------------------------------------------------------------
# Ejercicio 10. 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
# c2. Por ejemplo,
# >>> ej1 = inserta(3, inserta(5, inserta(2, vacio())))
# >>> ej2 = inserta(2, inserta(4, inserta(3, vacio())))
# >>> interseccion(ej1, ej2)
# {2, 3}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
276 Ejercicios de programación con Python
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c1=conjuntoAleatorio(), c2=conjuntoAleatorio())
def test_interseccion(c1: Conj[int], c2: Conj[int]) -> None:
r = interseccion(c1, c2)
Capítulo 9. El tipo abstracto de datos de los conjuntos 277
# ---------------------------------------------------------------------
# Ejercicio 11. 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())))
# >>> interseccionG([ej1, ej2, ej3])
# {2, 5}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
278 Ejercicios de programación con Python
# ============================
# La propiedad es
@given([Link](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
# ---------------------------------------------------------------------
# Ejercicio 12. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Comprobación de equivalencia
280 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
# ---------------------------------------------------------------------
# Ejercicio 13. 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)
# {}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 9. El tipo abstracto de datos de los conjuntos 281
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
282 Ejercicios de programación con Python
# 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
# ---------------------------------------------------------------------
# Ejercicio 14. 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}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
_c2 = deepcopy(c2)
while not esVacio(_c1):
mc1 = menor(_c1)
if not pertenece(mc1, c2):
r = inserta(mc1, r)
_c1 = elimina(mc1, _c1)
while not esVacio(_c2):
mc2 = menor(_c2)
if not pertenece(mc2, c1):
r = inserta(mc2, r)
_c2 = elimina(mc2, _c2)
return r
# Comprobación de equivalencia
# ============================
# 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
# ---------------------------------------------------------------------
# Ejercicio 15. 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)
# {5, 7}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return vacio()
mc = menor(c)
rc = elimina(mc, c)
if p(mc):
return inserta(mc, filtra(p, rc))
return filtra(p, rc)
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@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
# ---------------------------------------------------------------------
# Ejercicio 16. 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()))))
# >>> particion(lambda x: x % 2 == 0, ej)
# ({2, 4}, {5, 7})
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
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
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
Capítulo 9. El tipo abstracto de datos de los conjuntos 287
@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
# ---------------------------------------------------------------------
# Ejercicio 17. 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})
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
s: Conj[A] = vacio()
while not esVacio(c):
mc = menor(c)
c = elimina(mc, c)
if mc < x or mc == x:
r = inserta(mc, r)
else:
s = inserta(mc, s)
return (r, s)
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(x=[Link](), c=conjuntoAleatorio())
def test_divide(x: int, c: Conj[int]) -> None:
r = divide(x, c)
Capítulo 9. El tipo abstracto de datos de los conjuntos 289
assert divide2(x, c) == r
assert divide3(x, c) == r
assert divide4(x, c) == r
# ---------------------------------------------------------------------
# Ejercicio 18. Definir la función
# mapC : (Callable[[A], B], Conj[A]) -> Conj[B]
# tal que map(f, c) es el conjunto formado por las imágenes de los
# elementos de c, mediante f. Por ejemplo,
# >>> mapC(lambda x: 2 * x, inserta(3, inserta(1, vacio())))
# {2, 6}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# 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
# ---------------------------------------------------------------------
# Ejercicio 19. Definir la función
# todos : (Callable[[A], bool], Conj[A]) -> bool
# tal que todos(p, c) se verifica si todos los elemsntos de c
# verifican el predicado p. Por ejemplo,
# >>> todos(lambda x: x % 2 == 0, inserta(4, inserta(6, vacio())))
# True
# >>> todos(lambda x: x % 2 == 0, inserta(4, inserta(7, vacio())))
# False
# ---------------------------------------------------------------------
Capítulo 9. El tipo abstracto de datos de los conjuntos 291
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
return False
return True
# Comprobación de equivalencia
# ============================
# 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
# ---------------------------------------------------------------------
# Ejercicio 20. 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())))
# True
# >>> algunos(lambda x: x % 2 == 0, inserta(3, inserta(7, vacio())))
# False
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
Capítulo 9. El tipo abstracto de datos de los conjuntos 293
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(c=conjuntoAleatorio())
294 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 21. 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)}
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 9. El tipo abstracto de datos de los conjuntos 295
# 3ª solución
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
296 Ejercicios de programación con Python
# 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# § Librerías auxiliares --
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
297
298 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 1. Una relación binaria R sobre un conjunto A se puede
# representar mediante un par (u,g) donde u es la lista de los elementos
# de tipo A (el universo de R) y g es la lista de pares de elementos de
# u (el grafo de R).
#
# Definir el tipo de dato (Rel a), para representar las relaciones
# binarias sobre a, y la función
# esRelacionBinaria : (Rel[A]) -> bool
# tal que esRelacionBinaria(r) se verifica si r es una relación
# binaria. Por ejemplo,
# >>> esRelacionBinaria(([1, 3], [(3, 1), (3, 3)]))
# True
# >>> esRelacionBinaria(([1, 3], [(3, 1), (3, 2)]))
# False
#
# Además, definir un generador de relaciones binarias y comprobar que
# las relaciones que genera son relaciones binarias.
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
Capítulo 10. Relaciones binarias homogéneas 299
# ===========
# Comprobación de la propiedad
# ============================
# La propiedad es
@given([Link](min_value=0, max_value=10))
def test_esRelacionBinaria(n: int) -> None:
r = relacionArbitraria(n)
assert esRelacionBinaria(r)
assert esRelacionBinaria2(r)
assert esRelacionBinaria3(r)
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# universo : (Rel[A]) -> list[A]
# tal que universo(r) es el universo de la relación r. Por ejemplo,
# >>> universo(([3, 2, 5], [(2, 3), (3, 5)]))
# [3, 2, 5]
# ---------------------------------------------------------------------
Capítulo 10. Relaciones binarias homogéneas 301
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# grafo : (Rel[A]) -> list[tuple[A, A]]
# tal que grafo(r) es el grafo de la relación r. Por ejemplo,
# >>> grafo(([3, 2, 5], [(2, 3), (3, 5)]))
# [(2, 3), (3, 5)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
# ---------------------------------------------------------------------
# Ejercicio 5. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
(_, g) = r
return all(((y, x) in g for (x, y) in g))
# 2ª solución
# ===========
return aux(g)
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# subconjunto : (list[A], list[A]) -> bool
304 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
@given([Link]([Link]()),
[Link]([Link]()))
def test_filtraAplica(xs: list[int], ys: list[int]) -> None:
r = subconjunto1(xs, ys)
Capítulo 10. Relaciones binarias homogéneas 305
# 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
# ---------------------------------------------------------------------
# Ejercicio 7. 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)])
# ---------------------------------------------------------------------
# 1ª solución
306 Ejercicios de programación con Python
# ===========
# 2ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=0, max_value=10),
[Link](min_value=0, max_value=10))
def test_composicion(n: int, m: int) -> None:
r1 = relacionArbitraria(n)
Capítulo 10. Relaciones binarias homogéneas 307
r2 = relacionArbitraria(m)
res = composicion(r1, r2)
assert composicion2(r1, r2) == res
assert composicion2(r1, r2) == res
# ---------------------------------------------------------------------
# Ejercicio 8. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
return aux(g)
# 3ª solución
# ===========
g = grafo(r)
g1 = list(g)
for (x, y) in g1:
if not all(((x, z) in g for (u,z) in g if u == y)):
return False
return True
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=0, max_value=10))
def test_transitiva(n: int) -> None:
r = relacionArbitraria(n)
res = transitiva1(r)
assert transitiva2(r) == res
assert transitiva3(r) == res
# 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
Capítulo 10. Relaciones binarias homogéneas 309
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# esEquivalencia : (Rel[A]) -> bool
# tal que esEquivalencia(r) se verifica si la relación r es de
# equivalencia. Por ejemplo,
# >>> esEquivalencia (([1,3,5],[(1,1),(1,3),(3,1),(3,3),(5,5)]))
# True
# >>> esEquivalencia (([1,2,3,5],[(1,1),(1,3),(3,1),(3,3),(5,5)]))
# False
# >>> esEquivalencia (([1,3,5],[(1,1),(1,3),(3,3),(5,5)]))
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
310 Ejercicios de programación con Python
return aux(u)
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=0, max_value=10))
def test_irreflexiva(n: int) -> None:
r = relacionArbitraria(n)
res = irreflexiva(r)
assert irreflexiva2(r) == res
assert irreflexiva3(r) == res
# ---------------------------------------------------------------------
# Ejercicio 11. 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
Capítulo 10. Relaciones binarias homogéneas 311
# >>> antisimetrica(([1,2],[(1,1),(2,1)]))
# True
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
return aux(g)
# 5ª solución
312 Ejercicios de programación con Python
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
# ---------------------------------------------------------------------
# Ejercicio 12. 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
# ---------------------------------------------------------------------
# 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([Link](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
# ---------------------------------------------------------------------
# § Clausuras --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 13. 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)]))
Capítulo 10. Relaciones binarias homogéneas 315
# ---------------------------------------------------------------------
# Ejercicio 14. 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)])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 15. Comprobar con Hipothesis que clausuraSimetrica es
# simétrica.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=10))
def test_clausuraSimetrica(n: int) -> None:
r = relacionArbitraria(n)
assert simetrica(clausuraSimetrica(r))
# ---------------------------------------------------------------------
# Ejercicio 16. 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)])
# ---------------------------------------------------------------------
316 Ejercicios de programación con Python
# 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]
# 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
@given([Link](min_value=0, max_value=10))
def test_clausuraTransitiva(n: int) -> None:
r = relacionArbitraria(n)
assert clausuraTransitiva(r) == clausuraTransitiva2(r)
# ---------------------------------------------------------------------
# Ejercicio 17. Comprobar con QuickCheck que clausuraTransitiva es
# transitiva.
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=10))
def test_cla(n: int) -> None:
r = relacionArbitraria(n)
assert transitiva(clausuraTransitiva(r))
319
320 Ejercicios de programación con Python
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
# >>> [Link](6,7)
# 7*x^6 + x^5 + 5*x^2 + 4*x
# >>> [Link](4,7)
# x^5 + 7*x^4 + 5*x^2 + 4*x
# >>> [Link](5,7)
# 8*x^5 + 5*x^2 + 4*x
# >>> ejPol3
# 6*x^4 + 2*x
# >>> [Link]()
# 4
# >>> [Link]()
# 2*x
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> [Link]()
# 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
# x^5 + 5*x^2 + 4*x
# >>> consPol(3,9,ejPol2a)
# x^5 + 9*x^3 + 5*x^2 + 4*x
# >>> consPol(3,2,polCero())
# 2*x^3
# >>> consPol(6,7,ejPol2a)
Capítulo 11. El tipo abstracto de datos de los polinomios 323
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
@dataclass
class Polinomio(Generic[A]):
_coeficientes: list[A] = field(default_factory=list)
if n < m:
return [Link]().consPol(n, b).consPol(m, c)
if b + c == 0:
return Polinomio(list(dropwhile(lambda x: x == 0, xs[1:])))
return Polinomio([b + c] + xs[1:])
# Generador de polinomios
# =======================
@given(p=polinomioAleatorio(),
n=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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 [Link]
#
# [Link]::test_esPolCero1 PASSED
# [Link]::test_esPolCero2 PASSED
# [Link]::test_consPol PASSED
# [Link]::test_grado PASSED
328 Ejercicios de programación con Python
# [Link]::test_coefLider PASSED
# [Link]::test_restoPol PASSED
#
# === 6 passed in 1.64s ===
# >>> [Link]()
# False
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> [Link](3,0)
# x^5 + 5*x^2 + 4*x
# >>> Polinomio().consPol(3,2)
# 2*x^3
# >>> [Link](6,7)
# 7*x^6 + x^5 + 5*x^2 + 4*x
# >>> [Link](4,7)
# x^5 + 7*x^4 + 5*x^2 + 4*x
# >>> [Link](5,7)
# 8*x^5 + 5*x^2 + 4*x
# >>> ejPol3
# 6*x^4 + 2*x
# >>> [Link]()
# 4
# >>> [Link]()
# 2*x
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> [Link]()
# 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)
330 Ejercicios de programación con Python
# False
# >>> ejPol2a
# x^5 + 5*x^2 + 4*x
# >>> consPol(3,9,ejPol2a)
# x^5 + 9*x^3 + 5*x^2 + 4*x
# >>> consPol(3,2,polCero())
# 2*x^3
# >>> consPol(6,7,ejPol2a)
# 7*x^6 + x^5 + 5*x^2 + 4*x
# >>> consPol(4,7,ejPol2a)
# x^5 + 7*x^4 + 5*x^2 + 4*x
# >>> consPol(5,7,ejPol2a)
# 8*x^5 + 5*x^2 + 4*x
# >>> ejPol3a
# 6*x^4 + 2*x
# >>> grado(ejPol3a)
# 4
# >>> restoPol(ejPol3a)
# 2*x
# >>> ejPol2a
# x^5 + 5*x^2 + 4*x
# >>> restoPol(ejPol2a)
# 5*x^2 + 4*x
#
# Finalmente, se define un generador aleatorio de polinomios y se
# comprueba que los polinomios cumplen las propiedades de su
# especificación.
__all__ = [
'Polinomio',
'polCero',
'esPolCero',
'consPol',
'grado',
'coefLider',
'restoPol',
'polinomioAleatorio'
]
Capítulo 11. El tipo abstracto de datos de los polinomios 331
@dataclass
class Polinomio(Generic[A]):
_terminos: list[tuple[int, A]] = field(default_factory=list)
return self
if [Link]():
return Polinomio([(n, b)])
if n > m:
return Polinomio([(n, b)] + xs)
if n < m:
return Polinomio(xs[1:]).consPol(n, b).consPol(m, c)
if b + c == 0:
return Polinomio(xs[1:])
return Polinomio([(n, b + c)] + xs[1:])
return [Link]()
# Generador de polinomios
# =======================
@given(p=polinomioAleatorio(),
n=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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=[Link](min_value=0, max_value=10),
b=[Link]())
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 [Link]
#
# [Link]::test_esPolCero1 PASSED
# [Link]::test_esPolCero2 PASSED
# [Link]::test_consPol PASSED
Capítulo 11. El tipo abstracto de datos de los polinomios 335
# [Link]::test_grado PASSED
# [Link]::test_coefLider PASSED
# [Link]::test_restoPol PASSED
#
# === 6 passed in 1.74s ===
# ---------------------------------------------------------------------
# Importación de librerías --
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# densaAdispersa : (list[A]) -> list[tuple[int, A]]
336 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
Capítulo 11. El tipo abstracto de datos de los polinomios 337
# La propiedad es
@given(xs=densaAleatoria())
def test_densaADispersa(xs: list[int]) -> None:
r = densaAdispersa(xs)
assert densaAdispersa2(xs) == r
assert densaAdispersa3(xs) == r
# La comprobación es
# >>> test_densaADispersa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# dispersaAdensa : (list[tuple[int, A]]) -> list[A]
# tal que 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]
# ---------------------------------------------------------------------
# 1ª solución
# ===========
338 Ejercicios de programación con Python
# 2ª solución
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(ps=dispersaAleatoria())
def test_dispersaAdensa(ps: list[tuple[int, int]]) -> None:
r = dispersaAdensa(ps)
assert dispersaAdensa2(ps) == r
assert dispersaAdensa3(ps) == r
# La comprobación es
# >>> test_dispersaAdensa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 3. Comprobar con Hypothesis que las funciones densaAdispersa
# y dispersaAdensa son inversas.
340 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# La primera propiedad es
@given(xs=densaAleatoria())
def test_dispersaAdensa_densaAdispersa(xs: list[int]) -> None:
assert dispersaAdensa(densaAdispersa(xs)) == xs
# La comprobación es
# >>> test_dispersaAdensa_densaAdispersa()
# >>>
# 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
# >>> test_densaAdispersa_dispersaAdensa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# dispersaApolinomio : (list[tuple[int, A]]) -> Polinomio[A]
# tal 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 11. El tipo abstracto de datos de los polinomios 341
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(ps=dispersaAleatoria())
def test_dispersaApolinomio(ps: list[tuple[int, int]]) -> None:
assert dispersaApolinomio(ps) == dispersaApolinomio2(ps)
# La comprobación es
# >>> test_dispersaApolinomio()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# polinomioAdispersa : (Polinomio[A]) -> list[tuple[int, A]]
# tal polinomioAdispersa(p) es la representación dispersa del polinomio
# p. Por ejemplo,
# >>> ejPol1 = consPol(3, 5, consPol(1, 4, consPol(0, 7, polCero())))
# >>> ejPol = consPol(6, 9, ejPol1)
# >>> ejPol
# 9*x^6 + 5*x^3 + 4*x + 7
# >>> polinomioAdispersa(ejPol)
# [(6, 9), (3, 5), (1, 4), (0, 7)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Comprobar con Hypothesis que ambas funciones son
# inversas.
342 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# La primera propiedad es
@given(ps=dispersaAleatoria())
def test_polinomioAdispersa_dispersaApolinomio(ps: list[tuple[int,
int]]) -> None:
assert polinomioAdispersa(dispersaApolinomio(ps)) == ps
# La comprobación es
# >>> test_polinomioAdispersa_dispersaApolinomio()
# >>>
# La segunda propiedad es
@given(p=polinomioAleatorio())
def test_dispersaApolinomio_polinomioAdispersa(p: Polinomio[int]) -> None:
assert dispersaApolinomio(polinomioAdispersa(p)) == p
# La comprobación es
# >>> test_dispersaApolinomio_polinomioAdispersa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 7. 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,
# >>> ejPol = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol
# x^5 + 5*x^2 + 4*x
# >>> coeficiente(2, ejPol)
# 5
# >>> coeficiente(3, ejPol)
# 0
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# densaApolinomio : (list[A]) -> Polinomio[A]
# tal 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(xs=densaAleatoria())
def test_densaApolinomio(xs: list[int]) -> None:
assert densaApolinomio(xs) == densaApolinomio2(xs)
# La comprobación es
# >>> test_densaApolinomio()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# polinomioAdensa : (Polinomio[A]) -> list[A]
344 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(p=polinomioAleatorio())
def test_polinomioAdensa(p: Polinomio[int]) -> None:
assert polinomioAdensa(p) == polinomioAdensa2(p)
# La comprobación es
# >>> test_polinomioAdensa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 10. Comprobar con Hypothesis que ambas funciones son
# inversas.
# ---------------------------------------------------------------------
Capítulo 11. El tipo abstracto de datos de los polinomios 345
# La primera propiedad es
@given(xs=densaAleatoria())
def test_polinomioAdensa_densaApolinomio(xs: list[int]) -> None:
assert polinomioAdensa(densaApolinomio(xs)) == xs
# La comprobacion es
# >>> test_polinomioAdensa_densaApolinomio()
# >>>
# La segunda propiedad es
@given(p=polinomioAleatorio())
def test_densaApolinomio_polinomioAdensa(p: Polinomio[int]) -> None:
assert densaApolinomio(polinomioAdensa(p)) == p
# La comprobación es
# >>> test_densaApolinomio_polinomioAdensa()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 11. 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([Link](min_value=0, max_value=9),
[Link](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
# >>> test_creaTermino()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 12. 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
# ---------------------------------------------------------------------
# 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
# >>> test_termLider()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 13. 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.
# ---------------------------------------------------------------------
# 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 comprobación es
# >>> test_sumaPol()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 14. Comprobar con Hypothesis las siguientes propiedades:
# + polCero es el elemento neutro de la suma.
# + la suma es conmutativa.
# ---------------------------------------------------------------------
assert sumaPol(polCero(), p) == p
assert sumaPol(p, polCero()) == p
# La comprobación es
# >>> test_neutroSumaPol()
# >>>
# La suma es conmutativa.
@given(p=polinomioAleatorio(), q=polinomioAleatorio())
def test_conmutativaSuma(p: Polinomio[int], q: Polinomio[int]) -> None:
p1 = p
q1 = q
assert sumaPol(p, q) == sumaPol(q1, p1)
# La comprobación es
# >>> test_conmutativaSuma()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 15. 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
# ---------------------------------------------------------------------
a = coefLider(term)
m = grado(pol)
b = coefLider(pol)
r = restoPol(pol)
if esPolCero(pol):
return polCero()
return consPol(n + m, a * b, multPorTerm(term, r))
# ---------------------------------------------------------------------
# Ejercicio 16. Comprobar con Hypothesis las siguientes propiedades
# + El producto de polinomios es conmutativo.
# + El producto es distributivo respecto de la suma.
# ---------------------------------------------------------------------
# La comprobación es
# >>> test_conmutativaProducto()
# >>>
# La comprobación es
# >>> test_distributivaProductoSuma()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 17. 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
# --------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 18. 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)
352 Ejercicios de programación con Python
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 19. 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
# x^5 + 5*x^2 + 4*x
# >>> derivada(ejPol)
# 5*x^4 + 10*x + 4
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 20. Comprobar con Hypothesis que la derivada de la suma es
# la suma de las derivadas.
# ---------------------------------------------------------------------
# La propiedad es
@given(p=polinomioAleatorio(), q=polinomioAleatorio())
def test_derivada(p: Polinomio[int], q: Polinomio[int]) -> None:
assert derivada(sumaPol(p, q)) == sumaPol(derivada(p), derivada(q))
# La comprobación es
# >>> test_derivada()
# >>>
# ---------------------------------------------------------------------
Capítulo 11. El tipo abstracto de datos de los polinomios 353
# ---------------------------------------------------------------------
# Ejercicio 22. 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
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
354 Ejercicios de programación con Python
# ===========
# 3ª solución
# ===========
# Comprobación de equivalencia
# ============================
# La propiedad es
@given(p=polinomioAleatorio(),
n=[Link](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
# >>> test_potencia()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 23. 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
Capítulo 11. El tipo abstracto de datos de los polinomios 355
# ---------------------------------------------------------------------
# Ejercicio 24. 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())))
# >>> ejPol
# 2*x^7 + 5*x^4 + 5*x^2
# >>> integralDef(ejPol, 0, 1)
# 2.916666666666667
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 25. 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
356 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 26. Definir la función
# cociente : (Polinomio[float], Polinomio[float]) -> Polinomio[float]
# tal 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, polCero(
# >>> 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 27. Definir la función
# resto : (Polinomio[float], Polinomio[float]) -> Polinomio[float]
# tal que resto(p, q) es el resto de la división de p entre q. Por ejemplo,
# >>> resto(pol1, pol2)
# 1.0*x + 4
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 28. 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
# 6*x^2 + 2*x
# >>> divisiblePol(pol1, pol2)
# True
# >>> divisiblePol(pol1, pol3)
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 29. 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
358 Ejercicios de programación con Python
# 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]([Link]
# 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
# >>> from fractions import Fraction
# >>> horner(pol1, Fraction('3/2'))
# Fraction(795, 32)
# ---------------------------------------------------------------------
# 1ª solución
# ===========
return hornerAux(polinomioAdensa(p), 0)
Capítulo 11. El tipo abstracto de datos de los polinomios 359
# 2ª solución
# ===========
# Comprobación de propiedades
# ===========================
# La comprobación es
# > poetry run pytest El_TAD_de_polinomios_operaciones.py
# ======= 20 passed in 5.51s =======
# ---------------------------------------------------------------------
# Importación de librerías --
# ---------------------------------------------------------------------
360 Ejercicios de programación con Python
# pylint: disable=unused-import
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# terminoIndep : (Polinomio[A]) -> A
# tal que terminoIndep(p) es el término independiente del polinomio
# p. Por ejemplo,
# >>> ejPol1 = consPol(4, 3, consPol(2, 5, consPol(0, 3, polCero())))
# >>> ejPol1
# 3*x^4 + 5*x^2 + 3
# >>> terminoIndep(ejPol1)
# 3
# >>> ejPol2 = consPol(5, 1, consPol(2, 5, consPol(1, 4, polCero())))
# >>> ejPol2
# x^5 + 5*x^2 + 4*x
# >>> terminoIndep(ejPol2)
# 0
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. 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
Capítulo 11. El tipo abstracto de datos de los polinomios 361
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# cocienteRuffini : (int, Polinomio[int]) -> Polinomio[int]
# tal que cocienteRuffini(r, p) es el cociente de dividir el polinomio p
# por el polinomio x-r. Por ejemplo:
# >>> ejPol = consPol(3, 1, consPol(2, 2, consPol(1, -1, consPol(0, -2, polC
# >>> 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# restoRuffini : (int, Polinomio[int]) -> int
# tal que 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
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Comprobar con Hypothesis que, dado un polinomio p y un
# número entero r, las funciones anteriores verifican la propiedad de la
# división euclídea.
# ---------------------------------------------------------------------
# La propiedad es
@given(r=[Link](), 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
# >>> test_diviEuclidea()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# esRaizRuffini : (int, Polinomio[int]) -> bool
# tal que esRaizRuffini(r, p) se verifica si r es una raiz de p, usando
Capítulo 11. El tipo abstracto de datos de los polinomios 363
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# divisores : (int) -> list[int]
# tal que divisores(n) es la lista de todos los divisores enteros de
# n. Por ejemplo,
# divisores(4) == [1, 2, 4, -1, -2, -4]
# divisores(-6) == [1, 2, 3, 6, -1, -2, -3, -6]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. 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)
364 Ejercicios de programación con Python
# [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)
# [1, -1, -2]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. 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
Capítulo 11. El tipo abstracto de datos de los polinomios 365
# >>> factorizacion(ejPol2)
# [1*x + -1, 1*x + 1, 1*x + 2, 1]
# ---------------------------------------------------------------------
if esPolCero(p):
return [p]
return aux([0] + divisores(terminoIndep(p)))
# Comprobación de propiedades
# ===========================
# La comprobación es
# src> poetry run pytest -v Division_y_factorizacion_de_polinomios.py
# test_diviEuclidea PASSED
# ========= 1 passed in 0.32s ==========
366 Ejercicios de programación con Python
Capítulo 12
367
368 Ejercicios de programación con Python
# 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.
# pylint: disable=unused-import
__all__ = [
'Orientacion',
'Grafo',
'Vertice',
'Peso',
'creaGrafo',
'creaGrafo_',
'dirigido',
'adyacentes',
'nodos',
'aristas',
'aristaEn',
'peso'
]
370 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]):
self._orientacion = _orientacion
self._cotas = _cotas
if _orientacion == [Link]:
simetricas = [((v2, v1), p) for ((v1, v2), p)
in _aristas
if v1 != v2]
self._aristas = sorted(_aristas + simetricas)
else:
self._aristas = sorted(_aristas)
if x <= y]
escribeAristas = str(aristasReducidas) if ponderado \
else str([a for (a, _) in aristasReducidas])
return f”G {escribeOrientacion} ({vs}, {escribeAristas})”
((2,4),55),((2,5),32),
((3,4),61),((3,5),44),
((4,5),93)])
# ----------------------------------------------------------------------
# Importación de librerías --
# ----------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. 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.
378 Ejercicios de programación con Python
#
# Usando el [tipo abstracto de datos de los grafos]([Link]
# 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
# ---------------------------------------------------------------------
# Ejercicio 2. 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)
#
# Definir la función,
# grafoCiclo : (Int) -> Grafo
# tal que grafoCiclo(n) es el grafo ciclo de orden n. Por ejemplo,
Capítulo 12. El tipo abstracto de datos de los grafos 379
# >>> grafoCiclo(3)
# G ND ([1, 2, 3], [(1, 2), (1, 3), (2, 3)])
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_grafoCiclo()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 3. 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_([Link], (2,4), [(1,2),(3,1)]))
# 3
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_nVertices()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 4. En un un grafo g, los incidentes de un vértice v es el
# conjuntos de vértices x de g para los que hay un arco (o una arista)
# de x a v; es decir, que v es adyacente a x.
#
# Definir la función,
# incidentes :: (Ix v,Num p) => (Grafo v p) -> v -> [v]
# tal que (incidentes g v) es la lista de los vértices incidentes en el
# vértice v. Por ejemplo,
# λ> g1 = creaGrafo_(Orientacion.D, (1,3), [(1,2),(2,2),(3,1),(3,2)])
# λ> incidentes(g1,1)
# [3]
# λ> incidentes g1 2
# [1,2,3]
# λ> incidentes g1 3
# []
# λ> g2 = creaGrafo_([Link], (1,3), [(1,2),(2,2),(3,1),(3,2)])
# λ> incidentes g2 1
# [2,3]
# λ> incidentes g2 2
# [1,2,3]
# λ> incidentes g2 3
# [1,2]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_incidentes()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5. 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.
#
# 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_([Link], (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
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# lazos : (Grafo) -> list[tuple[Vertice, Vertice]]
# tal 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_([Link], (1,3), [(2,3),(3,1)])
# >>> lazos(ej1)
# [(1,1),(3,3)]
# >>> lazos(ej2)
# []
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_lazos()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# nLazos : (Grafo) -> int
# tal que nLazos(g) es el número de lazos del grafo g. Por ejemplo,
# >>> nLazos(ej1)
# 2
# >>> nLazos(ej2)
# 0
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_nLazos()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 8. 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
384 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
[(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_([Link], (1,3), [(1,2),(1,3),(2,3),(3,3)])
g4 = creaGrafo_([Link], (1,4), [(1,1),(1,2),(3,3)])
for nAristas_ in [nAristas, nAristas2]:
assert nAristas_(g1) == 8
assert nAristas_(g2) == 7
assert nAristas_(g3) == 4
assert nAristas_(g4) == 3
assert nAristas_(completo(4)) == 6
assert nAristas_(completo(5)) == 10
print(”Verificado”)
# La verificación es
# >>> test_nAristas()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 9. 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.
# ---------------------------------------------------------------------
# La comprobación es
# >>> all(prop_nAristasCompleto(n) for n in range(1, 21))
# True
# ---------------------------------------------------------------------
# Ejercicio 10. El grado positivo de un vértice v de un grafo g es el
# número de vértices de g adyacentes con v.
#
# Definir la función
# gradoPos : (Grafo, Vertice) -> int
# tal que gradoPos(g, v) es el grado positivo del vértice v en el grafo
386 Ejercicios de programación con Python
# g. Por ejemplo,
# g1 = creaGrafo_([Link], (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
# λ> gradoPos(g2, 5)
# 0
# λ> gradoPos(g2, 1)
# 3
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_GradoPos()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 11. El grado negativo de un vértice v de un grafo g es el
# número de vértices de g incidentes con v.
#
# Definir la función
# gradoNeg : (Grafo, Vertice) -> int
# tal que gradoNeg(g, v) es el grado negativo del vértice v en el grafo
Capítulo 12. El tipo abstracto de datos de los grafos 387
# g. Por ejemplo,
# g1 = creaGrafo_([Link], (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)])
# λ> gradoNeg(g1, 5)
# 4
# λ> gradoNeg(g2, 5)
# 3
# λ> gradoNeg(g2, 1)
# 0
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_GradoPosNeg()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 12. Definir un generador de grafos para comprobar
# propiedades de grafos con Hypothesis.
# ---------------------------------------------------------------------
# [(2, 5), (4, 5), (1, 2), (2, 3), (4, 1)]
# >>> gen_aristas(5).example()
# [(3, 4)]
# >>> 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([Link]([Link]([Link](1,n),
[Link](1,n)),
unique=True))
return as_
# ---------------------------------------------------------------------
# Ejercicio 13. 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
# >>> test_sumaGrados()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 14. 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.
#
# 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,
390 Ejercicios de programación con Python
# Verificación
Capítulo 12. El tipo abstracto de datos de los grafos 391
# ============
# La verificación es
# >>> test_grado()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 15. Comprobar con Hypothesis que en todo grafo, el número de
# nodos de grado impar es par.
# ---------------------------------------------------------------------
# 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
392 Ejercicios de programación con Python
# La comprobación es
# >>> test_grado1()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 16. 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.
#
# 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
# >>> test_apreton()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 17. Comprobar con QuickCheck que en todo grafo, el número
# de nodos de grado impar es par.
# ---------------------------------------------------------------------
# La propiedad es
@given(gen_grafo())
def test_numNodosGradoImpar(g: Grafo) -> None:
vs = nodos(g)
m = len([v for v in vs if grado(g, v) % 2 == 1])
assert m % 2 == 0
# La comprobación es
# >>> test_numNodosGradoImpar()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 18. Definir la propiedad
Capítulo 12. El tipo abstracto de datos de los grafos 393
# La propiedad es
@given([Link](min_value=1, max_value=20))
def test_GradoCompleto(n: int) -> None:
g = completo(n)
assert all(grado(g, v) == (n - 1) for v in nodos(g))
# La comprobación es
# >>> test_GradoCompleto()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 19. Un grafo es regular si todos sus vértices tienen el
# mismo grado.
#
# 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_([Link], (1,3), [(1,2),(2,3)]))
# False
# >>> regular(completo(4))
# True
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_regular()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 20. Comprobar que los grafos completos son regulares.
# ---------------------------------------------------------------------
# La comprobación es
# >>> prop_CompletoRegular(1, 30)
# True
# ---------------------------------------------------------------------
# Ejercicio 21. Un grafo es k-regular si todos sus vértices son de
# grado k.
#
# Definir la función,
# regularidad : (Grafo) -> Optional[int]
# tal que regularidad(g) es la regularidad de g. Por ejemplo,
# regularidad(creaGrafo_([Link], (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
# ---------------------------------------------------------------------
if regular(g):
return grado(g, nodos(g)[0])
return None
# Verificación
# ============
# La verificación es
# >>> test_k_regularidad()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 22. Comprobar que el grafo completo de orden n es
# (n-1)-regular (para n de 1 a 20).
# -----------------------------------------------------------
# La propiedad es
def prop_completoRegular(n: int) -> bool:
return regularidad(completo(n)) == n - 1
# La comprobación es
# >>> all(prop_completoRegular(n) for n in range(1, 21))
# True
# ---------------------------------------------------------------------
# Ejercicio 23. Comprobar que el grafo ciclo de orden n es 2-regular
# (para n de 3 a 20).
# -----------------------------------------------------------
396 Ejercicios de programación con Python
# La propiedad es
def prop_cicloRegular(n: int) -> bool:
return regularidad(grafoCiclo(n)) == 2
# La comprobación es
# >>> all(prop_cicloRegular(n) for n in range(3, 21))
# True
# Verificación
# ============
# grafos.
# ----------------------------------------------------------------------
# Librerías auxiliares --
# ----------------------------------------------------------------------
A = TypeVar('A')
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función,
# recorridoEnProfundidad : (Vertice, Grafo) -> list[Vertice]
# tal que recorridoEnProfundidad(i, g) es el recorrido en profundidad
# del grafo g desde el vértice i. Por ejemplo, en el grafo
#
# +---> 2 <---+
# | |
# | |
# 1 --> 3 --> 6 --> 5
# | |
# | |
# +---> 4 <---------+
#
# definido por
# grafo2: Grafo = creaGrafo_(Orientacion.D,
# (1,6),
# [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)])
# entonces
# recorridoEnProfundidad(1, grafo2) == [1,2,3,6,5,4]
# -----------------------------------------------------------
# 1ª solución
398 Ejercicios de programación con Python
# ===========
# 2ª solución
# ===========
# = reverse(rp([1], []))
# = reverse(rp([2,3,4], [1]))
# = reverse(rp([3,4], [2,1]))
# = reverse(rp([6,4], [3,2,1]))
# = reverse(rp([2,5,4], [6,3,2,1]))
# = reverse(rp([5,4], [6,3,2,1]))
# = reverse(rp([4,4], [5,6,3,2,1]))
# = reverse(rp([4], [4,5,6,3,2,1]))
# = reverse(rp([], [4,5,6,3,2,1]))
# = reverse([4,5,6,3,2,1])
# = [1,2,3,6,5,4]
# Verificación
# ============
# La verificación es
# >>> test_recorridoEnProfundidad()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 2. 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
# | |
400 Ejercicios de programación con Python
# | |
# +---> 4 <---------+
#
# definido por
# grafo4: Grafo = creaGrafo_(Orientacion.D,
# (1,6),
# [(1,2),(1,3),(1,4),(3,6),(5,4),(6,2),(6,5)])
# entonces
# recorridoEnAnchura(1, grafo4) == [1,2,3,4,6,5]
# -----------------------------------------------------------
# Verificación
# ============
Capítulo 12. El tipo abstracto de datos de los grafos 401
# La verificación es
# >>> test_recorridoEnAnchura()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 3. El [algoritmo de Kruskal]()[Link]
# 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 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.
#
# Definir la función,
# kruskal : (Grafo) -> list[tuple[Peso, Vertice, Vertice]]
# 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 = creaGrafo ([Link],
# (1,5),
# [((1,2),12),((1,3),34),((1,5),78),
402 Ejercicios de programación con Python
# ((2,4),55),((2,5),32),
# ((3,4),61),((3,5),44),
# ((4,5),93)])
# g2 = creaGrafo ([Link],
# (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 ([Link],
# (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 ([Link],
# (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 ([Link],
(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 ([Link],
(1,5),
Capítulo 12. El tipo abstracto de datos de los grafos 403
[((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 ([Link],
(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 ([Link],
(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)])
for a in vs:
if tb[a] == y:
tb[a] = y_
return tb
cs = list([Link]())
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)
#
# Después de añadir la arista (1,6) de peso 6
# {1:1, 2:1, 3:3, 4:1, 5:1, 6:1, 7:7}
#
# Después de añadir la arista (2,3) de peso 7
# {1:1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:7}
#
# Las posibles aristas a añadir son:
# + la (3,5) con peso 7, que no es posible pues la raíz de 3
# coincide con la raíz de 5, por lo que formaría un ciclo
# + la (3,4) con peso 8, que no es posible pues la raíz de 3
# coincide con la raíz de 4, por lo que formaría un ciclo
# + la (1,3) con peso 9, que no es posible pues la raíz de 3
# coincide con la raíz de 1, por lo que formaría un ciclo
# + la (5,7) con peso 9, que no forma ciclo
#
# Después de añadir la arista (5,7) con peso 9
# {1:1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:1}
#
# No es posible añadir más aristas, pues formarían ciclos.
# Verificación
# ============
# La verificación es
# >>> test_kruskal()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 4. El [algoritmo de Prim]([Link] 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
Capítulo 12. El tipo abstracto de datos de los grafos 407
# á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 el [tipo abstracto de datos de los grafos]([Link]
# 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. Por ejemplo, si g1, g2, g3 y
# g4 son los grafos definidos en el ejercicio anterior,
# entonces
# 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)]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_prim()
# Verificado
# Verificación
# ============
# La comprobación es
# src> poetry run pytest -v Algoritmos_sobre_grafos.py
# === session starts ==================================================
# test_recorridoEnProfundidad PASSED
# test_recorridoEnAnchura PASSED
# test_kruskal PASSED
# test_prim PASSED
# === passed in 0.12s ===================================================
# ----------------------------------------------------------------------
# Librerías auxiliares --
# ----------------------------------------------------------------------
Capítulo 12. El tipo abstracto de datos de los grafos 409
A = TypeVar('A')
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# recorridos : (list[A]) -> list[list[A]]
# tal que recorridos(xs) es la lista de todos los posibles recorridos
# por el grafo cuyo conjunto de vértices es xs y cada vértice se
# 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], [2, 3, 5, 2], [5, 2, 3, 5], [5, 3, 2, 5],
# [3, 2, 5, 3], [3, 5, 2, 3]]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_recorridos()
# Verificado
410 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 2.1. 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.
#
# Definir la función,
# anchura : (Grafo) -> int
# tal que anchuraG(g) es la anchura del grafo g. Por ejemplo,
# anchura(grafo1) == 4
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_anchura()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 2.2. Comprobar experimentalmente que la anchura del grafo
# ciclo de orden n es n-1.
# ---------------------------------------------------------------------
# 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
# ---------------------------------------------------------------------
# Ejercicio 3. 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.
#
# Definir la función,
# conexo :: (Grafo) -> bool
# tal que (conexo g) se verifica si el grafo g es conexo. Por ejemplo,
# conexo (creaGrafo_([Link], (1,3), [(1,2),(3,2)])) == True
# conexo (creaGrafo_([Link], (1,4), [(1,2),(3,2),(4,1)])) == True
# conexo (creaGrafo_([Link], (1,4), [(1,2),(3,4)])) == False
# ---------------------------------------------------------------------
412 Ejercicios de programación con Python
# Verificación
# ============
# La verificación es
# >>> test_conexo()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 4. 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 |
# +----------+----------+
# se pueden representar por
# mapa: Grafo = creaGrafo_([Link],
# (1,7),
Capítulo 12. El tipo abstracto de datos de los grafos 413
# [(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]([Link]
# 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
# ============
# La verificación es
# >>> test_correcta()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5. 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
# grafo7: 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.
#
# 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 grafo7 == [1,2,4]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_aislados()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 6. 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
# grafo8 = 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 grafo8 1 3 == True
# conectados grafo8 1 4 == False
# conectados grafo8 6 2 == False
# conectados grafo8 3 1 == True
# ----------------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_conectados()
# Verificado
# Verificación
# ============
# La verificación es
# src> poetry run pytest -v Ejercicios_sobre_grafos.py
# test_recorridos PASSED
# test_anchura PASSED
# test_conexo PASSED
# test_correcta PASSED
# test_aislados PASSED
# test_conectados PASSED
Capítulo 12. El tipo abstracto de datos de los grafos 417
Procedimiento de divide y
vencerás
# ----------------------------------------------------------------------
# Librerías auxiliares --
# ----------------------------------------------------------------------
P = TypeVar('P')
S = TypeVar('S')
# ---------------------------------------------------------------------
# Definir la función
# divideVenceras(Callable[[P], bool],
419
420 Ejercicios de programación con Python
# 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.
# + pbInicial es el problema inicial
#
# Usando la función DivideVenceras definir las funciones
# ordenaPorMezcla : (list[int]) -> list[int]
# ordenaRapida : (list[int]) -> list[int]
# tales que
# + ordenaPorMezcla(xs) es la lista obtenida ordenando xs por el
# procedimiento de ordenación por mezcla. Por ejemplo,
# >>> ordenaPorMezcla([3,1,4,1,5,9,2,8])
# [1, 1, 2, 3, 4, 5, 8, 9]
# + ordenaRapida(xs) es la lista obtenida ordenando xs por el
# procedimiento de ordenación rápida. Por ejemplo,
# λ> ordenaRapida([3,1,4,1,5,9,2,8])
# [1, 1, 2, 3, 4, 5, 8, 9]
# ---------------------------------------------------------------------
# Verificación
# ============
422 Ejercicios de programación con Python
# La verificación es
# >>> test_divideVenceras()
# Verificado
# ---------------------------------------------------------------------
# Librerías auxiliares
# ---------------------------------------------------------------------
import numpy as np
import [Link] as npt
# ---------------------------------------------------------------------
# Tipos
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Problema inicial
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Definir la función
# tablero(int, Posicion) -> Tablero
# tal que tablero(n, p) es el tablero inicial del problema del triominó
# en un cuadrado nxn en el que se ha eliminado la casilla de la
# posición p. Por ejemplo,
# >>> tablero(4, (3,4))
# array([[ 0, 0, 0, 0],
# [ 0, 0, 0, 0],
# [ 0, 0, 0, -1],
# [ 0, 0, 0, 0]])
# ---------------------------------------------------------------------
def tablero(n: int, p: Posicion) -> Tablero:
(i, j) = p
q = [Link]((n, n), dtype=int)
q[i - 1, j - 1] = -1
return q
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# pbInicial(int, Posicion) -> Problema
# tal que pbInicial(n, p) es el problema inicial del rompecabeza del
# triominó en un cuadrado nxn en el que se ha eliminado la casilla de la
# posición p. Por ejemplo,
# >>> pbInicial(4, (4,4))
# (1, array([[ 0, 0, 0, 0],
# [ 0, 0, 0, 0],
# [ 0, 0, 0, 0],
# [ 0, 0, 0, -1]]))
def pbInicial(n: int, p: Posicion) -> Problema:
Capítulo 13. Procedimiento de divide y vencerás 425
return 1, tablero(n, p)
# ---------------------------------------------------------------------
# Problemas indivisibles
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# ind :: (Problema) -> bool
# ind(pb) se verifica si el problema pb es indivisible. Por ejemplo,
# ind(pbInicial(2, (1,2))) == True
# ind(pbInicial(4, (1,2))) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Resolución de problemas indivisibles
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# posicionHueco(Tablero) -> Posicion
# posicionHueco(t) es la posición del hueco en el tablero t. Por
# ejemplo,
# posicionHueco(tablero(8, (5,2))) == (5,2)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# cuadranteHueco(t: Tablero) -> int
# cuadranteHueco(p) es el cuadrante donde se encuentra el hueco del
# tablero t (donde la numeración de los cuadrantes es 1 el superior
426 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# centralHueco :: (Tablero) -> Posicion
# tal que centralHueco(t) es la casilla central del cuadrante del
# tablero t donde se encuentra el hueco. Por ejemplo,
# centralHueco(tablero(8, (5,2))) == (5,4)
# centralHueco(tablero(8, (4,4))) == (4,4)
# centralHueco(tablero(8, (3,6))) == (4,5)
# centralHueco(tablero(8, (6,6))) == (5,5)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# centralesSinHueco : (Tablero) -> [Posicion]
# tal que centralesSinHueco(t) son las posiciones centrales del tablero
# t de los cuadrantes sin hueco. Por ejemplo,
# centralesSinHueco(tablero(8, (5,2))) == [(4,4),(4,5),(5,5)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# actualiza : (Tablero, list[tuple[Posicion, int]]) -> Tablero
# actualiza(t, ps) es la matriz obtenida cambiando en t los valores del
# las posiciones indicadas en ps por sus correspondientes valores. Por
# ejemplo,
# >>> actualiza([Link](3, dtype=int), [((1,2),4),((3,1),5)])
# array([[1, 4, 0],
# [0, 1, 0],
# [5, 0, 1]])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# triominoCentral : (Problema) -> Tablero
# tal que triominoCentral(n,t) es el tablero obtenido colocando el
# triominó formado por el número n en las posiciones centrales de los 3
# cuadrantes que no contienen el hueco. Por ejemplo,
# >>> triominoCentral((7, tablero(4, (4,4))))
# array([[ 0, 0, 0, 0],
428 Ejercicios de programación con Python
# [ 0, 7, 7, 0],
# [ 0, 7, 0, 0],
# [ 0, 0, 0, -1]])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. Definir la función
# resuelve :: (Problema) -> Tablero
# tal que resuelve(p) es la solución del problema indivisible p. Por
# ejemplo,
# >>> tablero(2, (2,2))
# array([[ 0, 0],
# [ 0, -1]])
# >>> resuelve((5,tablero(2, (2,2))))
# array([[ 5, 5],
# [ 5, -1]])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# División en subproblemas
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11. Definir la función
# divide : (Problema) -> list[Problema]
# tal que divide(n,t) es la lista de de los problemas obtenidos
# colocando el triominó n en las casillas centrales de t que no
# contienen el hueco y dividir el tablero en sus cuatros cuadrantes y
# aumentar en uno el número del correspondiente triominó. Por ejemplo,
# >>> divide((3,tablero(4, (4,4))))
# [(4, array([[0, 0],
# [3, 0]])),
# (5, array([[0, 0],
Capítulo 13. Procedimiento de divide y vencerás 429
# [0, 3]])),
# (6, array([[0, 3],
# [0, 0]])),
# (7, array([[ 0, 0],
# [ 0, -1]]))]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Combinación de soluciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12. Definir la función
# combina : (Problema, list[Tablero]) -> Tablero
# combina(p, ts) es la combinación de las soluciones ts de los
# subproblemas del problema p. Por ejemplo,
# >>> inicial = (1, tablero(4, (4, 4)))
# >>> p1, p2, p3, p4 = divide(inicial)
# >>> s1, s2, s3, s4 = map(resuelve, [p1, p2, p3, p4])
# >>> combina(inicial, [s1, s2, s3, s4])
# array([[ 3, 3, 2, 2],
# [ 3, 1, 1, 2],
# [ 4, 1, 5, 5],
# [ 4, 4, 5, -1]])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Solución mediante divide y vencerás
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 13. 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],
# [10, 7, 7, 11, 11, 8, 8, 12, 11, 8, 8, 12, 12, 9, 9, 13],
Capítulo 13. Procedimiento de divide y vencerás 431
# [10, 10, 11, 11, 11, 11, 12, 12, 11, 11, 12, 12, 12, 12, 13, 13]])
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_triomino()
# Verificado
432 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Referencias
# ---------------------------------------------------------------------
433
434 Ejercicios de programación con Python
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
es = sucesores(cp)
p = reduce(lambda x, y: apila(y, x), es, desapila(p))
return None
Capítulo 14. Problemas con búsquedas en espacio de estados 435
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Las posiciones de las reinas en el tablero se representan
# por su columna y su fila, que son números enteros.
#
# Una solución del problema de las n reinas es una lista de
# posiciones. Su tipo es SolNR.
#
# Definir los tipos Columna, Fila y SolNR.
# ---------------------------------------------------------------------
Columna = int
Fila = int
SolNR = list[tuple[Columna, Fila]]
# ---------------------------------------------------------------------
# Ejercicio 2. 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.
436 Ejercicios de programación con Python
#
# Definir el tipo nNodoNR.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# valida(SolNR, tuple[Columna, Fila]) -> bool
# tal que valida(sp, p) se verifica si la posición p es válida respecto
# de la solución parcial sp; es decir, la reina en la posición p no
# amenaza a ninguna de las reinas de la sp (se supone que están en
# distintas columnas). Por ejemplo,
# valida([(1,1)], (2,2)) == False
# valida([(1,1)], (2,3)) == True
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# sucesoresNR (NodoNR) -> list[NodoNR]
# tal que sucesoresNR(e) es la lista de los sucesores del estado e en el
# problema de las n reinas. Por ejemplo,
# >>> sucesoresNR((1,4,[]))
# [(2,4,[(1,1)]),(2,4,[(1,2)]),(2,4,[(1,3)]),(2,4,[(1,4)])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
Capítulo 14. Problemas con búsquedas en espacio de estados 437
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# solucionesNR : (int) -> list[SolNR]
# tal 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)]]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# primeraSolucionNR : (int) -> SolNR
# tal que 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)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# nSolucionesNR : (int) -> int
# tal que 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
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_nReinas()
# Verificado
#
# Definir las funciones
# buscaAnchura(Callable[[A], list[A]], Callable[[A], bool], A) -> list[A]
# buscaAnchura1(Callable[[A], list[A]], Callable[[A], bool], A) -> Optional[A]
# tales 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.
# + buscaAnchura1(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
# anchura.
# ---------------------------------------------------------------------
A = TypeVar('A')
setrecursionlimit(10**6)
es = sucesores(pc)
c = reduce(lambda x, y: inserta(y, x), es, resto(c))
return None
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Las posiciones de las reinas en el tablero se representan
Capítulo 14. Problemas con búsquedas en espacio de estados 441
Columna = int
Fila = int
SolNR = list[tuple[Columna, Fila]]
# ---------------------------------------------------------------------
# Ejercicio 2. 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.
#
# Definir el tipo nNodoNR.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# valida(SolNR, tuple[Columna, Fila]) -> bool
# tal que valida(sp, p) se verifica si la posición p es válida respecto
# de la solución parcial sp; es decir, la reina en la posición p no
# amenaza a ninguna de las reinas de la sp (se supone que están en
# distintas columnas). Por ejemplo,
# valida([(1,1)], (2,2)) == False
# valida([(1,1)], (2,3)) == True
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# sucesoresNR (NodoNR) -> list[NodoNR]
# tal que sucesoresNR(e) es la lista de los sucesores del estado e en el
# problema de las n reinas. Por ejemplo,
# >>> sucesoresNR((1,4,[]))
# [(2,4,[(1,1)]),(2,4,[(1,2)]),(2,4,[(1,3)]),(2,4,[(1,4)])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# esFinalNR(NodoNR) -> bool
# tal que esFinalNR(e) se verifica si e es un estado final del problema
# de las n reinas.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# solucionesNR : (int) -> list[SolNR]
# tal 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)]]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# primeraSolucionNR : (int) -> SolNR
# tal que primeraSolucionNR(n) es la primera solución del problema de las n
# reinas, por búsqueda en espacio de estados por profundidad. Por
# ejemplo,
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# nSolucionesNR : (int) -> int
# tal que 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
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_nReinas()
444 Ejercicios de programación con Python
# Verificado
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Para solucionar el problema se definen los siguientes
# tipos:
# + Una solución del problema de la mochila es una lista de objetos.
# Solucion = 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.
Capítulo 14. Problemas con búsquedas en espacio de estados 445
Peso = int
Valor = float
Objeto = tuple[Peso, Valor]
Solucion = list[Objeto]
Estado = tuple[Valor, Peso, Peso, list[Objeto], Solucion]
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# inicial : (list[Objeto], Peso) -> Estado
# tal que inicial(os, l) es el estado inicial del problema de la mochila
# para la lista de objetos os y el límite de capacidad l
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# sucesores : (Estado) -> list[Estado]
# tal que sucesores(e) es la lista de los sucesores del estado e en el
# problema de la mochila para la lista de objetos os y el límite de
# capacidad l.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
446 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 5. Usando el procedimiento de búsqueda en profundidad,
# 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)
# ([(3, 4.4), (3, 4.4), (2, 2.8), (2, 2.8)], 14.4)
# ---------------------------------------------------------------------
# Verificación
# ============
([(3,5.0),(3,5.0),(2,3.0),(2,3.0)],16.0)
assert mochila([(2,2.8),(3,4.4),(5,6.1)], 10) == \
([(3,4.4),(3,4.4),(2,2.8),(2,2.8)],14.4)
print(”Verificado”)
# La verificación es
# >>> test_Mochila()
# Verificado
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
448 Ejercicios de programación con Python
es = sucesores(primero(c))
c = reduce(lambda x, y: inserta(y, x), es, resto(c))
return None
# ---------------------------------------------------------------------
Capítulo 14. Problemas con búsquedas en espacio de estados 449
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Para solucionar el problema se usará el tipo Tablero que
# son listas de listas de números enteros (que representan las piezas en
# cada posición y el 0 representa el hueco).
#
# Definir el tipo Tablero.
# ---------------------------------------------------------------------
Tablero = list[list[int]]
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la constante tableroFinalpara representar el
# tablero final del 8 puzzle.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Una posición es un par de enteros.
#
# Definir el tipo Posicion para representar posiciones.
# ---------------------------------------------------------------------
Posicion = tuple[int,int]
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# distancia : (Posicion, Posicion) -> int
# tal que distancia(p1, p2) es la distancia Manhatan entre las posiciones p1 y
# p2. Por ejemplo,
450 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# posicionElemento : (Tablero, int) -> Posicion
# tal que posicionElemento(t, a) es la posición de elemento a en el tablero
# t. Por ejemplo,
# λ> posicionElemento([[2,1,3],[8,0,4],[7,6,5]], 4)
# (1, 2)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# posicionHueco : (Tablero) -> Posicion
# posicionHueco(t) es la posición del hueco en el tablero t. Por
# ejemplo,
# >>> posicionHueco([[2,1,3],[8,0,4],[7,6,5]])
# (1, 1)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# heuristica : (Tablero) -> int
Capítulo 14. Problemas con búsquedas en espacio de estados 451
# ---------------------------------------------------------------------
# Ejercicio 8. Un 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.
#
# Definir el tipo Estado para representar los estados.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# inicial (Tablero) -> Estado
# tal que inicial(t) es el estado inicial del problema del 8 puzzle a
# partir del tablero t.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. Definir la función
# esFinal : (Estado) -> bool
# tal que esFinal(e) se verifica si e es un estado final.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11. Definir la función
# posicionesVecinas : (Posicion) -> list[Posicion]
# tal que posicionesVecinas(p) son las posiciones de la matriz cuadrada
# de dimensión 3 que se encuentran encima, abajo, a la izquierda y a la
# derecha de los posición p. Por ejemplo,
# >>> posicionesVecinas((1,1))
# [(0, 1), (2, 1), (1, 0), (1, 2)]
# >>> posicionesVecinas((0,1))
# [(1, 1), (0, 0), (0, 2)]
# >>> posicionesVecinas((0,0))
# [(1, 0), (0, 1)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12. Definir la función
# intercambia : (Tablero, Posicion, Posicion) -> Tablero
# tal que intercambia(t,p1, p2) es el tablero obtenido intercambiando en
# t los elementos que se encuentran en las posiciones p1 y p2. Por
# ejemplo,
# >>> intercambia([[2,1,3],[8,0,4],[7,6,5]], (0,1), (1,1))
# [[2, 0, 3], [8, 1, 4], [7, 6, 5]]
# ---------------------------------------------------------------------
(i1, j1) = p1
(i2, j2) = p2
t1 = deepcopy(t)
a1 = t1[i1][j1]
a2 = t1[i2][j2]
t1[i1][j1] = a2
t1[i2][j2] = a1
return t1
# ---------------------------------------------------------------------
# Ejercicio 13. Definir la función
# tablerosSucesores : (Tablero) -> list[Tablero]
# tal que tablerosSucesores(t) es la lista de los tablrtos sucesores del
# tablero t. Por ejemplo,
# >>> tablerosSucesores([[2,1,3],[8,0,4],[7,6,5]])
# [[[2, 0, 3], [8, 1, 4], [7, 6, 5]],
# [[2, 1, 3], [8, 6, 4], [7, 0, 5]],
# [[2, 1, 3], [0, 8, 4], [7, 6, 5]],
# [[2, 1, 3], [8, 4, 0], [7, 6, 5]]]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 14. Definir la función
# sucesores : (Estado) -> list[Estado]
# tal que (sucesores e) es la lista de sucesores del estado e. Por
# ejemplo,
# >>> t1 = [[0,1,3],[8,2,4],[7,6,5]]
# >>> es = sucesores((heuristica(t1), 1, [t1]))
# >>> es
# [(4, 2, [[[8, 1, 3],
# [0, 2, 4],
# [7, 6, 5]],
# [[0, 1, 3],
# [8, 2, 4],
# [7, 6, 5]]]),
# (2, 2, [[[1, 0, 3],
454 Ejercicios de programación con Python
# [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]]])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 15. 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],
Capítulo 14. Problemas con búsquedas en espacio de estados 455
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 0, 3],
# [8, 2, 4],
# [7, 6, 5]],
# [[1, 2, 3],
# [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
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_8puzzle()
# Verificado
class Comparable(Protocol):
@abstractmethod
def __lt__(self: A, otro: A) -> bool:
pass
A = TypeVar('A', bound=Comparable)
return None
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Nota. En los ejemplos de los ejercicios usaremos los grafos definidos
# a continuación.
# ---------------------------------------------------------------------
g1 = creaGrafo ([Link],
(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 ([Link],
(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 ([Link],
(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 ([Link],
(1,7),
[((1,2),5),((1,3),9),((1,5),15),((1,6),6),
((2,3),7),
((3,4),8),((3,5),1),
Capítulo 14. Problemas con búsquedas en espacio de estados 459
((4,5),5),
((5,6),3),((5,7),9),
((6,7),11)])
# ---------------------------------------------------------------------
# Ejercicio 1. Las aristas son pares de la forma ((v1, v2), p) donde v1
# es el vértice origen, v2 es el vértice destino y p es el peso de la
# arista.
#
# Definir el tipo Arista para las aristas.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. Un estado es una tupla de la forma (p,t,r,aem) donde p es
# el peso de la última arista añadida el árbol de expansión mínimo
# (aem), t es la lista de nodos del grafo que están en el aem y r es la
# lista de nodos del grafo que no están en el aem.
#
# Definir el tipo Estado para los estados.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# inicial : (Grafo) -> Estado
# tal que inicial(g) es el estado inicial correspondiente al grafo g.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# esFinal : (Estado) -> bool
# esFinal(e) se verifica si e es un estado final; es decir, si no
# queda ningún elemento en la lista de nodos sin colocar en el árbol de
460 Ejercicios de programación con Python
# expansión mínimo.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# sucesores : (Grafo, Estado) -> list[Estado]
# sucesores(g, e) es la lista de los sucesores del estado e en el
# grafo g. Por ejemplo,
# λ> sucesores(g1, (0,[1],[2,3,4,5],[]))
# [(12,[2,1],[3,4,5],[(1,2,12)]),
# (34,[3,1],[2,4,5],[(1,3,34)]),
# (78,[5,1],[2,3,4],[(1,5,78)])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Usando la búsqueda en escalada, 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 ([Link],
# (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 ([Link],
# (1,5),
Capítulo 14. Problemas con búsquedas en espacio de estados 461
# [((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 ([Link],
# (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 ([Link],
# (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)]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_prim()
# Verificado
# ---------------------------------------------------------------------
# Importaciones --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Definir el tipo Orilla con dos constructores I y D que
# representan las orillas izquierda y derecha, respectivamente.
# ---------------------------------------------------------------------
class Orilla(Enum):
Capítulo 14. Problemas con búsquedas en espacio de estados 463
I = 0
D = 1
I = Orilla.I
D = Orilla.D
# ---------------------------------------------------------------------
# Ejercicio 2. Definir el tipo Estado como abreviatura de una tupla que
# representan 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.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# seguro : (Estado) -> bool
# tal que seguro(e) se verifica si el estado e es seguro; es decir, que
# no puede estar en una orilla el lobo con la cabra sin el granjero ni
# la cabra con el repollo sin el granjero. Por ejemplo,
# seguro((I,D,D,I)) == False
# seguro((D,D,D,I)) == True
# seguro((D,D,I,I)) == False
# seguro((I,D,I,I)) == True
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# opuesta : (Orilla) -> Orilla
# tal que (opuesta x) es la opuesta de la orilla x. Por ejemplo
# opuesta(I) == D
464 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# sucesoresE(Estado) -> list[Estado]
# tal que sucesoresE(e) es la lista de los sucesores seguros del estado
# e. Por ejemplo,
# sucesoresE((I,I,I,I)) == [(D,I,D,I)]
# sucesoresE((D,I,D,I)) == [(I,I,D,I),(I,I,I,I)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Nodo es el tipo de los nodos del espacio de búsqueda,
# donde un nodo es una lista de estados
# [e_n, ..., e_2, e_1]
# tal que e_1 es el estado inicial y para cada i (2 <= i <= n), e_i es un
# sucesor de e_(i-1).
#
# Definir el tipo Nodo.
# ---------------------------------------------------------------------
Nodo = list[Estado]
Capítulo 14. Problemas con búsquedas en espacio de estados 465
# ---------------------------------------------------------------------
# Ejercicio 7. Definir
# inicial: Nodo
# tal que inicial es el nodo inicial en el que todos están en la orilla
# izquierda.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# esFinal : (Nodo) -> bool
# tal que esFinal(n) se verifica si n es un nodo final; es decir, su
# primer elemento es el estado final. Por ejemplo,
# esFinal([(D,D,D,D),(I,I,I,I)]) == True
# esFinal([(I,I,D,I),(I,I,I,I)]) == False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# sucesores : (Nodo) -> list[Nodo]
# tal que sucesores(n) es la lista de los sucesores del nodo n. Por
# ejemplo,
# >>> sucesores([(I,I,D,I),(D,I,D,I),(I,I,I,I)])
# [[(D, D, D, I), (I, I, D, I), (D, I, D, I), (I, I, I, I)],
# [(D, I, D, D), (I, I, D, I), (D, I, D, I), (I, I, I, I)]]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 10. Usando el procedimiento de búsqueda en profundidad,
# definir la función
# granjero : () -> list[list[Estado]]
# tal que granjero() son las soluciones del problema del granjero
466 Ejercicios de programación con Python
# # Verificación
# # ============
# La verificación es
# >>> test_granjero()
# Verificado
# +---+---+---+---+---+---+---+
# 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.
#
# El objetivo de esta relación de ejercicios es resolver el problema
# de las fichas mediante búsqueda en espacio de estados, utilizando las
# implementaciones estudiadas en los ejercicios anteriores.
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Definir el tipo Ficha con tres constructores B, V y H que
468 Ejercicios de programación con Python
class Ficha(Enum):
B = 0
V = 1
H = 2
B = Ficha.B
V = Ficha.V
H = Ficha.H
# ---------------------------------------------------------------------
# Ejercicio 2. Definir el tipo Tablero como una lista de fichas que
# representa las fichas colocadas en el tablero.
# ---------------------------------------------------------------------
Tablero = list[Ficha]
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# tableroInicial : (int, int) -> Tablero
# tal que tableroInicial(m, n) representa el tablero inicial del
# problema de las fichas de orden (m,n). Por ejemplo,
# tableroInicial(2, 3) == [B,B,H,V,V,V]
# tableroInicial(3, 2) == [B,B,B,H,V,V]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# tableroFinal : (int, int) -> Tablero
# tal que tableroFinal(m, n) representa el tablero final del problema de
# las fichas de orden (m,n). Por ejemplo,
# tableroFinal(2, 3) == [V,V,V,H,B,B]
Capítulo 14. Problemas con búsquedas en espacio de estados 469
# tableroFinal(3, 2) == [V,V,H,B,B,B]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# posicionHueco : (Tablero) -> int
# tal que posicionHueco(t) es la posición del hueco en el tablero t. Por
# ejemplo,
# posicionHueco(tableroInicial(3, 2)) == 3
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# tablerosSucesores : (Tablero) -> list[Tablero]
# tal que tablerosSucesores(t) es la lista de los sucesores del tablero
# t. Por ejemplo,
# >>> tablerosSucesores([V,B,H,V,V,B])
# [[V,H,B,V,V,B],[H,B,V,V,V,B],[V,B,V,H,V,B],[V,B,V,V,H,B],
# [V,B,B,V,V,H]]
# >>> tablerosSucesores([B,B,B,H,V,V,V])
# [[B,B,H,B,V,V,V],[B,H,B,B,V,V,V],[H,B,B,B,V,V,V],
# [B,B,B,V,H,V,V],[B,B,B,V,V,H,V],[B,B,B,V,V,V,H]]
# ---------------------------------------------------------------------
n = len(t)
return [intercambia(i, j, t)
for i in [j-1,j-2,j-3,j+1,j+2,j+3]
if 0 <= i < n]
# Heurística
# ==========
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# heuristicaT : (Tablero) -> int
# tal que heuristicaT(t) es la heurística del tablero t. Por ejemplo,
# heuristicaT([B,V,B,H,V,V,B]) == 5
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. La clase 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).
#
# Definir la clase Estado.
# ---------------------------------------------------------------------
class Estado(list[Tablero]):
def __lt__(self, e: list[Tablero]) -> bool:
return heuristicaT(self[0]) < heuristicaT(e[0])
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# inicial : (int, int) -> Estado
# tal que inicial(m, n) representa el estado inicial del problema de las
Capítulo 14. Problemas con búsquedas en espacio de estados 471
# ---------------------------------------------------------------------
# Ejercicio 10. Definir la función
# esFinal(int, int, Estado) -> bool
# tal que esFinal(m, n, e) se verifica si e es un estado final del
# problema de las fichas de orden (m,n). Por ejemplo,
# >>> esFinal(2, 1, [[V,H,B,B],[V,B,B,H],[H,B,B,V],[B,B,H,V]])
# True
# >>> esFinal(2, 1, [[V,B,B,H],[H,B,B,V],[B,B,H,V]])
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 11. Definir la función
# sucesores : (Estado) -> list[Estado]
# tal que sucesores(n) es la lista de los sucesores del estado n. Por
# ejemplo,
# >>> sucesores([[H,B,B,V],[B,B,H,V]])
# [[[B,H,B,V],[H,B,B,V],[B,B,H,V]],
# [[V,B,B,H],[H,B,B,V],[B,B,H,V]]]
# >>> sucesores([[B,H,B,V],[H,B,B,V],[B,B,H,V]])
# [[[B,V,B,H],[B,H,B,V],[H,B,B,V],[B,B,H,V]]]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 12. Definir el tipo Busqueda par representar los
# procedimientos de búsqueda.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 13. 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]]
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_fichas()
# Verificado
# 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.
#
# El objetivo de esta relación de ejercicios es resolver el problema
# del calendario mediante búsqueda en espacio de estados, utilizando las
# implementaciones estudiadas en los ejercicios anteriores.
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
import numpy as np
import [Link] as npt
# ---------------------------------------------------------------------
# Ejercicio 1. Definir el tipo Calendario como matrices de enteros.
# ---------------------------------------------------------------------
Calendario = [Link][np.int_]
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# inicial : (int) -> Calendario
# tal que inicial(n) es el estado inicial para el problema del
# calendario con n participantes; es decir, una matriz de n fila y n-1
# columnas con todos sus elementos iguales a 0. Por ejemplo,
# >>> inicial(4)
# array([[0, 0, 0],
# [0, 0, 0],
# [0, 0, 0],
# [0, 0, 0]])
Capítulo 14. Problemas con búsquedas en espacio de estados 475
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# primerHueco : (Calendario) -> tuple[int, int]
# primerHueco(c) es la posición del primer elemento cuyo valor es 0. Si
# todos los valores son distintos de 0, devuelve (-1,-1). Por ejemplo,
# primerHueco([Link]([[1,2,3],[4,5,0],[7,0,0]])) == (1, 2)
# primerHueco([Link]([[1,2,3],[4,5,6],[7,8,0]])) == (2, 2)
# primerHueco([Link]([[1,2,3],[4,5,6],[7,8,9]])) == (-1, -1)
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# libres : (Calendario, int, int) -> list[int]
# tal que libres(c, i, j) es la lista de valores que que pueden poner en
# la posición (i,j) del calendario c. Por ejemplo,
# libres([Link]([[0,0,0],[0,0,0],[0,0,0],[0,0,0]]),0,0) == [2, 3, 4]
# libres([Link]([[2,0,0],[1,0,0],[0,0,0],[0,0,0]]),0,1) == [3, 4]
# libres([Link]([[2,3,0],[1,0,0],[0,1,0],[0,0,0]]),0,2) == [4]
# libres([Link]([[2,3,4],[1,0,0],[0,1,0],[0,0,1]]),1,1) == [4]
# libres([Link]([[2,3,4],[1,4,0],[0,1,0],[0,2,1]]),1,2) == [3]
# ---------------------------------------------------------------------
- set(c[:,j]))
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# setElem : (int, int, int, Calendario) -> Calendario
# setElem(k, i, j, c) es el calendario obtenido colocando en c el valor
# k en la posición (i,j).
# >>> setElem(7,1,2,[Link]([[1,2,3],[4,5,0],[0,0,0]]))
# array([[1, 2, 3],
# [4, 5, 7],
# [0, 0, 0]])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# sucesores : (Calendario) -> list[Calendario]
# tal que sucesores(c) es la lista de calendarios obtenidos poniendo en
# el lugar del primer elemento nulo de c uno de los posibles jugadores
# de forma que se cumplan las condiciones del problema. Por ejemplo,
# >>> sucesores([Link]([[0,0,0],[0,0,0],[0,0,0],[0,0,0]]))
# [array([[2,0,0], [1,0,0], [0,0,0], [0,0,0]]),
# array([[3,0,0], [0,0,0], [1,0,0], [0,0,0]]),
# array([[4,0,0], [0,0,0], [0,0,0], [1,0,0]])]
# >>> sucesores([Link]([[2,0,0],[1,0,0],[0,0,0],[0,0,0]]))
# [array([[2,3,0], [1,0,0], [0,1,0], [0,0,0]]),
# array([[2,4,0], [1,0,0], [0,0,0], [0,1,0]])]
# >>> sucesores([Link]([[2,3,0],[1,0,0],[0,1,0],[0,0,0]]))
# [array([[2,3,4], [1,0,0], [0,1,0], [0,0,1]])]
# >>> sucesores([Link]([[2,3,4],[1,0,0],[0,1,0],[0,0,1]]))
# [array([[2,3,4], [1,4,0], [0,1,0], [0,2,1]])]
# >>> sucesores([Link]([[2,3,4],[1,4,0],[0,1,0],[0,2,1]]))
# [array([[2,3,4], [1,4,3], [0,1,2], [0,2,1]])]
# >>> sucesores([Link]([[2,3,4],[1,4,3],[0,1,2],[0,2,1]]))
# [array([[2,3,4], [1,4,3], [4,1,2], [3,2,1]])]
# >>> sucesores([Link]([[2,3,4],[1,4,3],[4,1,2],[3,2,1]]))
Capítulo 14. Problemas con búsquedas en espacio de estados 477
# []
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# esFinal : (Calendario) -> bool
# esFinal(c) se verifica si c un estado final para el problema
# del calendario con n participantes; es decir, no queda en c ningún
# elemento igual a 0. Por ejemplo,
# >>> esFinal([Link]([[2,3,4],[1,4,3],[4,1,2],[3,2,1]]))
# True
# >>> esFinal([Link]([[2,3,4],[1,4,3],[4,1,2],[3,2,0]]))
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Usando el procedimiento de búsqueda en profundidad,
# 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,
# >>> calendario(6)[0]
# array([[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]])
# >>> len(calendario(6))
# 720
478 Ejercicios de programación con Python
# >>> len(calendario(5))
# 0
# ---------------------------------------------------------------------
# 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”)
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Las fichas son pares de números enteros.
#
# Definir el tipo Ficha para las fichas.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. Un problema está definido por la lista de fichas que hay
# que colocar.
#
# Definir el tipo Problema para los problemas.
# ---------------------------------------------------------------------
Problema = list[Ficha]
# ---------------------------------------------------------------------
# Ejercicio 3. Los estados son los pares formados por la listas sin
# colocar y las colocadas.
#
# Definir el tipo Estado para los estados.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# inicial : (Problema) -> Estado
# tal que inicial(p) es el estado inicial del problema p. Por ejemplo,
# >>> inicial([(1,2),(2,3),(1,4)])
# ([(1, 2), (2, 3), (1, 4)], [])
# ---------------------------------------------------------------------
480 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# esFinal : (Estado) -> bool
# tal que esFinal(e) se verifica si e es un estado final. Por ejemplo,
# >>> esFinal(([], [(4,1),(1,2),(2,3)]))
# True
# >>> esFinal(([(2,3)], [(4,1),(1,2)]))
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 6. Definir la función
# elimina : (Ficha, list[Ficha]) -> list[Ficha]
# tal que elimina(f, fs) es la lista obtenida eliminando la ficha f de
# la lista fs. Por ejemplo,
# >>> elimina((1,2),[(4,1),(1,2),(2,3)])
# [(4, 1), (2, 3)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 7. Definir la función
# sucesores : (Estado) -> list[Estado]
# tal que sucesores(e) es la lista de los sucesores del estado e. Por
# ejemplo,
# >>> sucesores(([(1,2),(2,3),(1,4)],[]))
# [([(2,3),(1,4)],[(1,2)]),
# ([(1,2),(1,4)],[(2,3)]),
# ([(1,2),(2,3)],[(1,4)]),
# ([(2,3),(1,4)],[(2,1)]),
# ([(1,2),(1,4)],[(3,2)]),
# ([(1,2),(2,3)],[(4,1)])]
Capítulo 14. Problemas con búsquedas en espacio de estados 481
# >>> sucesores(([(2,3),(1,4)],[(1,2)]))
# [([(2,3)],[(4,1),(1,2)])]
# >>> sucesores(([(2,3),(1,4)],[(2,1)]))
# [([(1,4)],[(3,2),(2,1)])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir, mediante búsqueda en espacio de estados, la
# función
# soluciones : (Problema) -> list[Estado]
# tal que soluciones(p) es la lista de las soluciones del problema
# p. Por ejemplo,
# >>> soluciones([(1,2),(2,3),(1,4)])
# [([], [(3, 2), (2, 1), (1, 4)]), ([], [(4, 1), (1, 2), (2, 3)])]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. Definir 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)])
# []
482 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# # Verificación
# # ============
# La verificación es
# >>> test_domino()
# Verificado
# ---------------------------------------------------------------------
# Importaciones
Capítulo 14. Problemas con búsquedas en espacio de estados 483
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Los estados son ternas formadas por los números
# seleccionados, su suma y los restantes números.
#
# Definir el tipo Estado para los estados.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. Definir la función
# inicial : (list[int]) -> Estado
# tal que inicial(ns) es el estado inicial dell problema de suma cero a
# partir de los números de ns. Por ejemplo,
# >>> inicial([-7,-3,-2,5,8,-1])
# ([], 0, [-7, -3, -2, 5, 8, -1])
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Definir la función
# esFinal : (Estado) -> bool
# tal es Final(e) se verifica si e es un estado final. Por ejemplo,
# >>> esFinal(([-7, -1, 8], 0, [-3, -2, 5]))
# True
# >>> esFinal(([-3, 8, -1], 4, [-7, -2, 5]))
# False
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
484 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# Ejercicio 5. Usando el procedimiento de búsqueda en profundidad,
# 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
# ---------------------------------------------------------------------
# Importaciones
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. Un problema es una lista de 3 números enteros (a,b,c)
# tales que a es la capacidad de la primera jarra, b es la capacidad de
# la segunda jarra y c es el número de litros que se desea obtener en la
# primera jarra.
#
# Definir el tipo Problema para los problemas.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. Una configuracion es una lista de dos números. El primero
# es el contenido de la primera jarra y el segundo el de la segunda.
#
# Definir el tipo Configuracion para las configuraciones.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3. Inicialmente, las dos jarras están vacías.
#
# Definir
# configuracionInicial: Configuracion
# tal que configuracionInicial es la configuración inicial.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. Definir la función
# esConfiguracionFinal : (Problema, Configuracion) -> bool
Capítulo 14. Problemas con búsquedas en espacio de estados 487
# ---------------------------------------------------------------------
# Ejercicio 5. Definir la función
# sucesorasConfiguracion : (Problema, Configuracion) -> list[Configuracion]
# sucesorasConfiguracion(p, c) son las sucesoras de la configuración c
# del problema p. Por ejemplo,
# sucesorasConfiguracion((4,3,2), (0,0)) == [(4,0),(0,3)]
# sucesorasConfiguracion((4,3,2), (4,0)) == [(4,3),(0,0),(1,3)]
# sucesorasConfiguracion((4,3,2), (4,3)) == [(0,3),(4,0)]
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
488 Ejercicios de programación con Python
Estado = list[Configuracion]
# ---------------------------------------------------------------------
# Ejercicio 7. Definir
# inicial: Estado
# tal que inicial es el estado cuyo único elemento es la configuración
# inicial.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 8. Definir la función
# esFinal(Problema, Estado) -> bool
# tal que esFinal(p, e) se verifica si e es un estado final; es decir,
# su primer elemento es una configuración final.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 9. Definir la función
# sucesores : (Problema, Estado) -> list[Estado]
# sucesores(p, e) es la lista de los sucesores del estado e en el
# problema p. Por ejemplo,
# λ> sucesores((4,3,2), [(0,0)])
# [[(4,0),(0,0)],[(0,3),(0,0)]]
# λ> sucesores((4,3,2), [(4,0),(0,0)])
# [[(4,3),(4,0),(0,0)],[(1,3),(4,0),(0,0)]]
# λ> sucesores((4,3,2), [(4,3),(4,0),(0,0)])
# [[(0,3),(4,3),(4,0),(0,0)]]
# ---------------------------------------------------------------------
Capítulo 14. Problemas con búsquedas en espacio de estados 489
# ---------------------------------------------------------------------
# Ejercicio 10. Usando el procedimiento de búsqueda en anchura,
# 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.
#
# 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))
# []
# ---------------------------------------------------------------------
# Verificación
# ============
# La verificación es
# >>> test_jarras()
# Verificado
Capítulo 15
Programación dinámica
import numpy as np
import [Link] as npt
setrecursionlimit(10**6)
491
492 Ejercicios de programación con Python
# 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
494 Ejercicios de programación con Python
import numpy as np
import [Link] as npt
setrecursionlimit(10**6)
return q
q[i, j] = 1
else:
q[i, j] = q[i - 1, j - 1] + q[i - 1, j]
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
# Verificación
# ============
print(”Verificado”)
# La verificación es
# >>> test_binomial()
# Verificado
# a 0,1,2,2,3,3,3,3,3,4,4]
def matrizLongitudSCM2(xs: str, ys: str) -> list[list[int]]:
n = len(xs)
m = len(ys)
q = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
if xs[i - 1] == ys[j - 1]:
q[i][j] = 1 + q[i - 1][j - 1]
else:
q[i][j] = max(q[i - 1][j], q[i][j - 1])
return q
# 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
setrecursionlimit(10**6)
# ['', 'm', 'am', 'am', 'aa', 'ma', 'ma', 'ma', 'ma', 'ama', 'ama'],
# ['', 'm', 'am', 'am', 'aa', 'ma', 'oma', 'oma', 'oma', 'ama', 'ama'],
# ['', 'm', 'am', 'am', 'aa', 'ma', 'oma', 'oma', 'oma', 'ama', 'ama'],
# ['', 'm', 'am', 'am', 'aam', 'aam', 'oma', 'oma', 'oma', 'aoma', 'aoma']]
# Gráficamente,
# m a t a m o s c a s
# [””,”” ,”” ,”” ,”” ,”” ,”” ,”” ,”” ,”” ,””,
# a ””,”” ,”a” ,”a” ,”a” ,”a” ,”a” ,”a” ,”a” ,”a” ,”a”,
# m ””,”m”,”a” ,”a” ,”a” ,”ma” ,”ma” ,”ma” ,”ma” ,”ma” ,”ma”,
# a ””,”m”,”am”,”am”,”aa” ,”ma” ,”ma” ,”ma” ,”ma” ,”ama” ,”ama”,
# p ””,”m”,”am”,”am”,”aa” ,”ma” ,”ma” ,”ma” ,”ma” ,”ama” ,”ama”,
# o ””,”m”,”am”,”am”,”aa” ,”ma” ,”oma”,”oma”,”oma”,”ama” ,”ama”,
# l ””,”m”,”am”,”am”,”aa” ,”ma” ,”oma”,”oma”,”oma”,”ama” ,”ama”,
# a ””,”m”,”am”,”am”,”aam”,”aam”,”oma”,”oma”,”oma”,”aoma”,”aoma”]
def matrizSCM2(xs: str, ys: str) -> list[list[str]]:
n = len(xs)
m = len(ys)
q = [[”” for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
if xs[i - 1] == ys[j - 1]:
q[i][j] = xs[i - 1] + q[i - 1][j - 1]
else:
q[i][j] = mayor(q[i - 1][j], q[i][j - 1])
return q
# # Comparación de eficiencia
# # =========================
# 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
Capítulo 15. Programación dinámica 503
# Verificación
# ============
# La verificación es
# >>> test_scm()
# Verificado
# levenshtein(”casa”, ”casa”) == 0
# levenshtein(”ana”, ”maria”) == 3
# levenshtein(”agua”, ”manantial”) == 7
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# c a l l e
# 0,1,2,3,4,5,
# c 1,0,1,2,3,4,
# a 2,1,0,1,2,3,
# s 3,2,1,1,2,3,
# a 4,3,2,2,2,3
def matrizLevenshtein(xs: str, ys: str) -> list[list[int]]:
n = len(xs)
m = len(ys)
q = [[0 for _ in range(m + 1)] for _ in range(n + 1)]
for i in range(n + 1):
q[i][0] = i
for j in range(m + 1):
q[0][j] = j
for i in range(1,n + 1):
for j in range(1, m + 1):
if xs[i - 1] == ys[j - 1]:
q[i][j] = q[i - 1][j - 1]
else:
q[i][j] = 1 + min([q[i-1][j], q[i][j-1], q[i-1][j-1]])
return q
# 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
506 Ejercicios de programación con Python
# Verificación
# ============
# [(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)
# 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
Capítulo 15. Programación dinámica 509
# ============
# La verificación es
# >>> test_caminos()
# Verificado
setrecursionlimit(10**6)
n = len(p[0])
q = defaultdict(list)
for i in range(1, m + 1):
for j in range(1, n + 1):
if i == 1:
q[(i, j)] = [[p[0][z-1] for z in range(j, 0, -1)]]
elif j == 1:
q[(i, j)] = [[p[z-1][0] for z in range(i, 0, -1)]]
else:
q[(i, j)] = [[p[i-1][j-1]] + cs for cs in q[(i-1, j)] + q[(i, j-1
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
# ============
assert caminos1([[1,6,11,2],[7,12,3,8],[3,8,4,9]]) == r
assert caminos2([[1,6,11,2],[7,12,3,8],[3,8,4,9]]) == r
print(”Verificado”)
# La verificación es
# >>> test_caminos()
# Verificado
# >>> maximaSuma([[1,6,11,2],[7,12,3,8],[3,8,4,9]])
# 41
# >>> maximaSuma4([list(range(800*n+1, 800*(n+1)+1)) for n in range(800)])
# 766721999
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# 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
# ============
# La verificación es
Capítulo 15. Programación dinámica 517
# >>> test_maximaSuma()
# Verificado
setrecursionlimit(10**6)
# Comparación de eficiencia
# =========================
520 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
521
Capítulo 16
Cálculo numérico
# ----------------------------------------------------------------------
# Librerías auxiliares --
# ----------------------------------------------------------------------
# ---------------------------------------------------------------------
# Diferenciación numérica --
523
524 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1.1. Definir la función
# derivada : (float, Callable[[float], float], float) -> float
# tal que derivada(a, f, x) es el valor de la derivada de la función f
# en el punto x con aproximación a. Por ejemplo,
# derivada(0.001, sin, pi) == -0.9999998333332315
# derivada(0.001, cos, pi) == 4.999999583255033e-4
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_derivada()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 1.2. Definir las funciones
# derivadaBurda : Callable[[Callable[[float], float], float], float]
# derivadaFina : Callable[[Callable[[float], float], float], float]
# derivadaSuper : Callable[[Callable[[float], float], float], float]
# tales que
# * derivadaBurda(f, x) es el valor de la derivada de la función f
# en el punto x con aproximación 0.01,
# * (derivadaFina(f, x) es el valor de la derivada de la función f
# en el punto x con aproximación 0.0001.
# * (derivadaSuper(f, x) es el valor de la derivada de la función f
# en el punto x con aproximación 0.000001.
# Por ejemplo,
# derivadaBurda(cos, pi) == 0.004999958333473664
Capítulo 16. Cálculo numérico 525
# Verificación
# ============
# La comprobación es
# >>> test_derivadas()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 1.3. Definir la función
# derivadaFinaDelSeno : Callable[[float], float]
# tal que derivadaFinaDelSeno(x) es el valor de la derivada fina del
# seno en x. Por ejemplo,
# derivadaFinaDelSeno(pi) == -0.9999999983354435
# ---------------------------------------------------------------------
# Verificación
# ============
526 Ejercicios de programación con Python
# La comprobación es
# >>> test_derivadaFinaSeno()
# Verificado
# ---------------------------------------------------------------------
# Cálculo de la raíz cuadrada --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2.1. En los siguientes apartados de este ejercicio se va a
# calcular la raíz cuadrada de un número basándose 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, por recursión, 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
# ---------------------------------------------------------------------
return raizAux(1)
# Verificación
# ============
# La comprobación es
# >>> test_raiz()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 3.2. Definir el operador
# casiIgual : (float, float) -> bool
# tal que casiIgual(x, y) si |x-y| < 0.001. Por ejemplo,
# casiIgual(3.05, 3.07) == False
# casiIgual(3.00005, 3.00007) == True
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_casiIgual()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 3.3. Comprobar con Hypothesis que si x es positivo,
# entonces
# casiIgual(raiz(x)**2, x)
528 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=100))
def test_cuadradro_de_raiz(x):
assert casiIgual(raiz(x)**2, x)
# La comprobación es
# >>> test_cuadradro_de_raiz()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 3.4. Definir, por iteración, la función
# raizI : (float) -> float
# tal que raizI(x) es la raíz cuadrada de x calculada usando la
# propiedad anterior. Por ejemplo,
# raizI(9) == 3.000000001396984
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_raizI()
# Verificado
Capítulo 16. Cálculo numérico 529
# ---------------------------------------------------------------------
# Ejercicio 3.5. Comprobar con Hypothesis que si x es positivo,
# entonces
# casiIgual(raizI(x)**2, x)
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=100))
def test_cuadrado_de_raizI(x):
assert casiIgual(raizI(x)**2, x)
# La comprobación es
# >>> test_cuadrado_de_raizI()
# >>>
# ---------------------------------------------------------------------
# Ceros de una función --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. 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.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4.1. Definir, por recursión, 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
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_puntoCero()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 4.2. Definir, por iteración, la función
# puntoCeroI : (Callable[[float], float]) -> float
# tal que puntoCeroI(f) es un cero de la función f calculado usando la
# propiedad anterior. Por ejemplo,
# puntoCeroI(cos) == 1.5707963267949576
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_puntoCeroI()
# Verificado
# ---------------------------------------------------------------------
# Funciones inversas --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. En este ejercicio se usará la función puntoCero para
# definir la inversa de distintas funciones.
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5.1. Definir, usando puntoCero, la función
# raizCuadrada : (float) -> float
# tal que raizCuadrada(x) es la raíz cuadrada de x. Por ejemplo,
# raizCuadrada(9) == 3.000000002941184
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
532 Ejercicios de programación con Python
# >>> test_raizCuadrada()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5.2. Comprobar con Hypothesis que si x es positivo,
# entonces
# casiIgual(raizCuadrada(x)**2, x)
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=100))
def test_cuadrado_de_raizCuadrada(x):
assert casiIgual(raizCuadrada(x)**2, x)
# La comprobación es
# >>> test_cuadrado_de_raizCuadrada()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 5.3. Definir, usando puntoCero, la función
# raizCubica : (float) -> float
# tal que raizCubica(x) es la raíz cúbica de x. Por ejemplo,
# raizCubica(27) == 3.0000000000196048
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_raizCubica()
# Verificado
# ---------------------------------------------------------------------
Capítulo 16. Cálculo numérico 533
# La propiedad es
@given([Link](min_value=0, max_value=100))
def test_cubo_de_raizCubica(x):
assert casiIgual(raizCubica(x)**3, x)
# La comprobación es
# >>> test_cubo_de_raizCubica()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 5.5. Definir, usando puntoCero, la función
# arcoseno : (float) -> float
# tal que arcoseno(x) es el arcoseno de x. Por ejemplo,
# arcoseno(1) == 1.5665489428306574
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_arcoseno()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5.6. Comprobar con Hypothesis que si x está entre 0 y 1,
# entonces
# casiIgual(sin(arcoseno(x)), x)
# ---------------------------------------------------------------------
534 Ejercicios de programación con Python
# La propiedad es
@given([Link](min_value=0, max_value=1))
def test_seno_de_arcoseno(x):
assert casiIgual(sin(arcoseno(x)), x)
# La comprobación es
# >>> test_seno_de_arcoseno()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 5.7. Definir, usando puntoCero, la función
# arcocoseno : (float) -> float
# tal que arcoseno(x) es el arcoseno de x. Por ejemplo,
# arcocoseno(0) == 1.5707963267949576
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_arcocoseno()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5.8. Comprobar con Hypothesis que si x está entre 0 y 1,
# entonces
# casiIgual(cos(arcocoseno(x)), x)
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](min_value=0, max_value=1))
def test_coseno_de_arcocoseno(x):
Capítulo 16. Cálculo numérico 535
assert casiIgual(cos(arcocoseno(x)), x)
# La comprobación es
# >>> test_coseno_de_arcocoseno()
# >>>
# ---------------------------------------------------------------------
# Ejercicio 5.7. 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
# ---------------------------------------------------------------------
# Verificación
# ============
# La comprobación es
# >>> test_inversa()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 5.8. Redefinir, usando inversa, las funciones raizCuadrada,
# raizCubica, arcoseno y arcocoseno.
# ---------------------------------------------------------------------
# Verificación
# ============
536 Ejercicios de programación con Python
# La comprobación es
# >>> test_inversas()
# Verificado
# Verificación de todo
# ====================
# La comprobación es
# src> poetry run pytest Calculo_numerico_Diferenciacion_y_metodos_de_Heron_y_
# ===== 20 passed in 0.88s =====
# ---------------------------------------------------------------------
# Librerías auxiliares --
# ---------------------------------------------------------------------
setrecursionlimit(10**6)
# ---------------------------------------------------------------------
# Cálculo de límites --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 1. 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
# ===========
# 2ª solución
# ===========
while True:
y = f(x)
if max(abs(y - f(x + i)) for i in range(1, 101)) < a:
break
x += 1
return y
# 3ª solución
# ===========
# Verificación
# ============
# La comprobación es
# >>> test_limite()
# Verificado
# ---------------------------------------------------------------------
# Ceros de una función por el método de la bisección --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 2. 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:
Capítulo 16. Cálculo numérico 539
# 1ª solución
# ===========
# 2ª solución
540 Ejercicios de programación con Python
# ===========
# Verificación
# ============
# La comprobación es
# >>> test_biseccion()
# Verificado
# ---------------------------------------------------------------------
# Cálculo de raíces enteras --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 3.1. Definir la función
# raizEnt : (int, int) -> int
Capítulo 16. Cálculo numérico 541
# 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
# >>> tiempo('raizEnt3(10**14, 2)')
# 0.00 segundos
#
# >>> raizEnt2(10**50, 2)
# 10000000000000000905969664
# >>> raizEnt3(10**50, 2)
# 10000000000000000000000000
# Verificación
# ============
# La comprobación es
# >>> test_raizEnt()
# Verificado
# ---------------------------------------------------------------------
# Ejercicio 3.2. Comprobar con Hypothesis que para todo número natural
# n, raizEnt(10**(2*n), 2) == 10**n
# ---------------------------------------------------------------------
# La propiedad es
@given([Link](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()
# >>>
# ---------------------------------------------------------------------
# Integración por el método de los rectángulos --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 4. 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 [Link] 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-
544 Ejercicios de programación con Python
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 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, lambda x : x+2) == [3,5,7,9,11,13,15,17,19]
def sucesion(a: float, b: float, s: Callable[[float], float]) -> list[float]:
xs = []
while a <= b:
[Link](a)
a = s(a)
return xs
# 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
# ===========
return 0
return h * f(a+h/2) + integral2(a+h, b, f, h)
# 3ª solución
# ===========
# Verificación
# ============
0.19998333362500054)
assert aproximado(integral3(0, 1, lambda x : 3*x**2 + 4*x**3, 0.01),
1.9999250000000026)
assert aproximado(log(2) - integral3(1, 2, lambda x : 1/x, 0.01),
3.124931644782336e-6)
assert aproximado(pi - 4 * integral3(0, 1, lambda x : 1/(x**2+1), 0.01),
-8.333333331389525e-6)
print(”Verificado”)
# La verificación es
# >>> test_integral()
# Verificado
# ---------------------------------------------------------------------
# Algoritmo de bajada para resolver un sistema triangular inferior --
# ---------------------------------------------------------------------
# ---------------------------------------------------------------------
# Ejercicio 5. 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,
Capítulo 16. Cálculo numérico 547
# Verificación
# ============
# La verificación es
# >>> test_bajada()
# Verificado
# Verificación de todo
# ====================
# La comprobación es
# > poetry run pytest Calculo_numerico_2_Limites_biseccion_e_integrales.py
# ===== passed in 2.36s =====
548 Ejercicios de programación con Python
Capítulo 17
Miscelánea
549
550 Ejercicios de programación con Python
set_int_max_str_digits(30000)
# 1ª solución
# ===========
# 2ª solución
# ===========
while True:
yield seq[0]
[Link](sum(seq))
[Link](0)
# Verificación
# ============
# 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
# =========================
552 Ejercicios de programación con Python
# 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
# =================================
return x == y * y
# 3ª definición de representaciones
# =================================
# Verificación
# ============
# La comprobación es
# >>> test_representaciones()
# Verificado
556 Ejercicios de programación con Python
# La propiedad es
@given([Link](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))')
# 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
# ==========================
# ============================
# La propiedad es
@given([Link](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()
# >>>
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación --
560 Ejercicios de programación con Python
# ============
# La verificación es
# >>> test_primosH()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_primosH_equiv(n: int) -> None:
assert nth(primosH1(), n) == nth(primosH2(), n)
# La comprobación es
# >>> test_primosH_equiv()
# >>>
# Comparación de eficiencia
# =========================
# La comparación es
# >>> tiempo('nth(primosH1(), 3000)')
Capítulo 17. Miscelánea 561
# 2.51 segundos
# >>> tiempo('nth(primosH2(), 3000)')
# 0.05 segundos
setrecursionlimit(10**6)
562 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# Verificación
# ============
Capítulo 17. Miscelánea 563
# La verificación es
# >>> test_factorizacionesH()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_factorizacionesH_equiv(n: int) -> None:
m = 1 + 4 * n
assert factorizacionesH1(m) == factorizacionesH2(m)
# 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
# ==========================
564 Ejercicios de programación con Python
# La propiedad es
@given([Link](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 =====
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
# 1ª solución
# ===========
# 2ª solución
# ===========
return r
# 3ª solución
# ===========
# Verificación
# ============
# La comprobación es
# >>> test_representaciones()
# Verificado
# La propiedad es
@given([Link](min_value=1, max_value=1000))
def test_representaciones_equiv(x: int) -> None:
xs = representaciones1(x)
568 Ejercicios de programación con Python
assert representaciones2(x) == xs
assert representaciones3(x) == xs
# La comprobación es
# >>> test_representaciones_equiv()
# >>>
# La comparación es
# >>> 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([Link](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 [Link]())
# La comprobación es
# >>> test_representaciones_prop()
Capítulo 17. Miscelánea 569
# >>>
# Solución
# ========
# termSerieThueMorse(3) == [0,1,1,0,1,0,0,1]
# termSerieThueMorse(4) == [0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0]
def termSerieThueMorse(n: int) -> list[int]:
if n == 0:
return [0]
xs = termSerieThueMorse(n-1)
return xs + complementaria(xs)
# Verificación
# ============
# La verificación es
# >>> test_serieThueMorse()
# Verificado
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
# [0,1,1,0,1,0,0,1]
# [0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0]
# De esta forma se va formando una sucesión
# 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,...
# que se conoce como la [sucesión de Thue-Morse]([Link]
#
# Definir la sucesión
# sucThueMorse : () -> Iterator[int]
# cuyos elementos son los de la sucesión de Thue-Morse. Por ejemplo,
# >>> list(islice(sucThueMorse(), 30))
# [0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0]
#
# Comprobar con Hypothesis que si s(n) representa el término n-ésimo de
# la sucesión de Thue-Morse, entonces
# s(2n) = s(n)
# s(2n+1) = 1 - s(n)
# ---------------------------------------------------------------------
A = TypeVar('A')
# 1ª solución
# ===========
# termSucThueMorse(1) == 1
# termSucThueMorse(2) == 1
# termSucThueMorse(3) == 0
# termSucThueMorse(4) == 1
def termSucThueMorse(n: int) -> int:
if n == 0:
return 0
k = 1 + floor(log2(n))
return nth(serieThueMorse(), k)[n]
# Comprobación de la propiedad
# ============================
# La propiedad es
@given([Link](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
# ===========
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([Link](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 --
# ---------------------------------------------------------------------
# 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
# ============
# La verificación es
# >>> test_primosYhuecosMaximales()
# Verificado
# Comparación de eficiencia
# =========================
# La comparación es
Capítulo 17. Miscelánea 577
# ---------------------------------------------------------------------
# § Referencias --
# ---------------------------------------------------------------------
# Otras referencias
# + C. Caldwell, ”The gaps between primes” [Link]
# + J.K. Andersen, ”Maximal prime gaps” [Link]
# + N.J.A. Sloane ”Sequence A002386” en OEIS [Link]
# + N.J.A. Sloane ”Sequence A005250” en OEIS [Link]
# + E.W. Weisstein, ”Prime gaps” en MathWorld [Link]
# ---------------------------------------------------------------------
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# =============
# Verificación
# ============
# La verificación es
# >>> test_phi()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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)')
580 Ejercicios de programación con Python
# 3.55 segundos
# >>> tiempo('phi3(10**1000000)')
# 3.37 segundos
# Verificación de la propiedad
# ============================
# La propiedad es
@given([Link](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 =====
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# =============
n = n // 5
r += n
return r
# Verificación
# ============
# La verificación es
# >>> test_cerosDelFactorial()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
# src> poetry run pytest -v Ceros_finales_del_factorial.py
# test_cerosDelFactorial PASSED
# test_cerosDelFactorial_equiv PASSED
# 1ª solución
# ===========
584 Ejercicios de programación con Python
# 2ª solución
# ===========
# 3ª solución
# ===========
# 4ª solución
# ===========
Capítulo 17. Miscelánea 585
# 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
# =========================
# La comparación es
586 Ejercicios de programación con Python
setrecursionlimit(10**6)
# 1ª solución
# ===========
# raizEntera(9) == 3
# raizEntera(10) == 3
def raizEntera1(x: int) -> int:
return list(takewhile(lambda n: n**2 <= x, count(1)))[-1]
# 2ª solución
# ===========
# 3ª solución
# ===========
588 Ejercicios de programación con Python
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_cuadradoCercano()
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link](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
590 Ejercicios de programación con Python
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
Capítulo 17. Miscelánea 591
# 4ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_sumaCadenas()
592 Ejercicios de programación con Python
# Verificado
# '2441000'
# >>> decimalAfactoradico(36288000)
# 'A0000000000'
# >>> list(map(decimalAfactoradico, range(1, 11)))
# ['10', '100', '110', '200', '210', '1000', '1010', '1100', '1110', '1200']
# >>> decimalAfactoradico(37199332678990121746799944815083519999999)
# '3KXWVUTSRQPONMLKJIHGFEDCBA9876543210'
#
# Comprobar con Hypothesis que, para cualquier entero positivo n,
# factoradicoAdecimal(decimalAfactoradico(n)) == n
# ---------------------------------------------------------------------
# 1ª definición de factoradicoAdecimal
# ====================================
# 2ª definición de factoradicoAdecimal
# ====================================
# 3ª definición de factoradicoAdecimal
# ====================================
# 1ª definición de decimalAfactoradico
# ====================================
# 2ª definición de decimalAfactoradico
# ====================================
# 3ª definición de decimalAfactoradico
# ====================================
# Verificación
# ============
# La verificación es
# >>> test_factoradico()
# Verificado
# Propiedad de inverso
# ====================
@given([Link](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()
# >>>
598 Ejercicios de programación con Python
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([Link]([Link]()))
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()
# >>>
600 Ejercicios de programación con Python
set_int_max_str_digits(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
Capítulo 17. Miscelánea 601
# Verificación
# ============
# La verificación es
# >>> test_sumaFilaTrianguloImpares()
# Verificado
# La propiedad es
@given([Link](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)))')
602 Ejercicios de programación con Python
# 2.04 segundos
# >>> tiempo('len(str(sumaFilaTrianguloImpares2(6*10**7)))')
# 0.00 segundos
A = TypeVar('A')
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_sumaReiterada()
# Verificado
# La propiedad es
@given([Link]([Link](), min_size=1))
def test_sumaReiterada_equiv(xs: list[int]) -> None:
r = sumaReiterada1(xs)
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))')
Capítulo 17. Miscelánea 605
# 1.90 segundos
# >>> tiempo('sumaReiterada3(range(4000))')
# 1.97 segundos
set_int_max_str_digits(10**6)
setrecursionlimit(10**6)
# 1ª solución
# ===========
return []
if not xss[0]:
return []
return [xss[0][0]] + diagonal1(list(map((lambda ys : ys[1:]), xss[1:])))
# 2ª solución
# ===========
# 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
setrecursionlimit(10**6)
# 1ª solución
# ===========
# 2ª solución
# ===========
# 3ª solución
Capítulo 17. Miscelánea 609
# ===========
# 4ª solución
# ===========
# 5ª solución
# ===========
# Verificación
# ============
esPotenciaDe4_3, esPotenciaDe4_4,
esPotenciaDe4_5]:
assert esPotenciaDe4(16)
assert not esPotenciaDe4(17)
assert list(islice(potenciasDe4(), 5)) == [1, 4, 16, 64, 256]
print(”Verificado”)
# 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))')
# 2.31 segundos
Capítulo 17. Miscelánea 611
# 1ª solución
# ===========
# 2ª solución
# ===========
612 Ejercicios de programación con Python
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_exponente()
# Verificado
# La propiedad es
@given([Link](min_value=1, max_value=1000),
[Link](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()
# >>>
# 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,
# 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
# ===========
# 2ª solución
# ===========
# 3ª solución
# ===========
616 Ejercicios de programación con Python
# 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
# ============
# La verificación es
# >>> test_mayoresGeneradores()
# Verificado
# Equivalencia de definiciones
# ============================
# La propiedad es
@given([Link](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
# ==========================
print(f”{t:0.2f} segundos”)
# La comprobación es
# >>> tiempo('mayoresGeneradores1(10**5)')
# 4.08 segundos
# >>> 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
setrecursionlimit(10**6)
# 1ª solución
# ===========
Capítulo 17. Miscelánea 619
# 2ª solución
# ===========
# 3ª solución
# ===========
# Verificación
# ============
# La verificación es
# >>> test_maximosLocales()
620 Ejercicios de programación con Python
# Verificado
# Comprobación de equivalencia
# ============================
# La propiedad es
@given([Link]([Link]()))
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
Bibliografía
[5] T. Hall and J. Stacey. Python 3 for absolute beginners. Apress, 2010.
[12] A. Saha. Doing Math with Python: Use Programming to explore algebra,
statistics, calculus, and more! No Starch Press, 2015.
621
622 Ejercicios de programación con Python
[14] R. van Hattem. Mastering Python: Write powerful and efficient code
using the full range of Python’s capabilities. Packt Publishing, 2022.