PFH: La semana en Exercitium (9 de septiembre de 2022)
Esta semana he publicado en Exercitium las soluciones de los siguientes problemas:
- 1. Intercambio de componentes de un par
- 2. Distancia entre dos puntos
- 3. Permutación cíclica
- 4. Mayor número con dos dígitos dados
- 5. Número de raíces de la ecuación de segundo grado
A continuación se muestran las soluciones.
1. Intercambio de componentes de un par
Definir la función
1 |
intercambia :: (a,b) -> (b,a) |
tal que (intercambia p)
es el punto obtenido intercambiando las coordenadas del punto p
. Por ejemplo,
1 2 |
intercambia (2,5) == (5,2) intercambia (5,2) == (2,5) |
Soluciones en Haskell
1 2 3 4 5 6 7 8 9 10 11 12 |
import Test.QuickCheck intercambia :: (a,b) -> (b,a) intercambia (x,y) = (y,x) -- La propiedad es prop_intercambia :: (Int,Int) -> Bool prop_intercambia p = intercambia (intercambia p) == p -- La comprobación es -- λ> quickCheck prop_intercambia -- +++ OK, passed 100 tests. |
El código se encuentra en GitHub.
Soluciones en Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from typing import TypeVar from hypothesis import given, strategies as st A = TypeVar('A') B = TypeVar('B') def intercambia(p: tuple[A, B]) -> tuple[B, A]: (x, y) = p return (y, x) # La propiedad de es @given(st.tuples(st.integers(), st.integers())) def test_equiv_intercambia(p): assert intercambia(intercambia(p)) == p # La comprobación es # src> poetry run pytest -q intercambio_de_componentes_de_un_par.py # 1 passed in 0.15s |
El código se encuentra en GitHub.
Comentarios
- En Haskell se pone el patrón en los argumentos y en Python hay que extraerlos mediante un asignación.
2. Distancia entre dos puntos
Definir la función
1 |
distancia :: (Double,Double) -> (Double,Double) -> Double |
tal que (distancia p1 p2)
es la distancia entre los puntos p1
y p2
. Por ejemplo,
1 |
distancia (1,2) (4,6) == 5.0 |
Comprobar con QuickCheck que se verifica la propiedad triangular de la distancia; es decir, dados tres puntos p1
, p2
y p3
, la distancia de p1
a p3
es menor o igual que la suma de la distancia de p1
a p2
y la de p2
a p3
.
Soluciones en Haskell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import Test.QuickCheck distancia :: (Double,Double) -> (Double,Double) -> Double distancia (x1,y1) (x2,y2) = sqrt((x1-x2)^2+(y1-y2)^2) -- La propiedad es prop_triangular :: (Double,Double) -> (Double,Double) -> (Double,Double) -> Property prop_triangular p1 p2 p3 = all acotado [p1, p2, p3] ==> distancia p1 p3 <= distancia p1 p2 + distancia p2 p3 where acotado (x, y) = abs x < cota && abs y < cota cota = 2^30 -- La comprobación es -- ghci> quickCheck prop_triangular -- +++ OK, passed 100 tests. -- Nota: Por problemas de redondeo, la propiedad no se cumple en -- general. Por ejemplo, -- λ> p1 = (0, 9147936743096483) -- λ> p2 = (0, 3) -- λ> p3 = (0, 2) -- λ> distancia p1 p3 <= distancia p1 p2 + distancia p2 p3 -- False -- λ> distancia p1 p3 -- 9.147936743096482e15 -- λ> distancia p1 p2 + distancia p2 p3 -- 9.14793674309648e15 |
El código se encuentra en GitHub.
Soluciones en Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
from math import sqrt from hypothesis import given, strategies as st def distancia(p1: tuple[float, float], p2: tuple[float, float]) -> float: (x1, y1) = p1 (x2, y2) = p2 return sqrt((x1-x2)**2+(y1-y2)**2) # La propiedad es cota = 2 ** 30 @given(st.tuples(st.integers(min_value=0, max_value=cota), st.integers(min_value=0, max_value=cota)), st.tuples(st.integers(min_value=0, max_value=cota), st.integers(min_value=0, max_value=cota)), st.tuples(st.integers(min_value=0, max_value=cota), st.integers(min_value=0, max_value=cota))) def test_triangular(p1, p2, p3): assert distancia(p1, p3) <= distancia(p1, p2) + distancia(p2, p3) # La comprobación es # src> poetry run pytest -q distancia_entre_dos_puntos.py # 1 passed in 0.38s # Nota: Por problemas de redondeo, la propiedad no se cumple en # general. Por ejemplo, # λ> p1 = (0, 9147936743096483) # λ> p2 = (0, 3) # λ> p3 = (0, 2) # λ> distancia(p1, p3) <= distancia(p1, p2) + distancia (p2. p3) # False # λ> distancia(p1, p3) # 9147936743096482.0 # λ> distancia(p1, p2) + distancia(p2, p3) # 9147936743096480.05 |
El código se encuentra en GitHub.
Comentarios
- La raíz cuadrada de
x
se escribe- en Haskell, como
sqrt x
y - en Python, como
sqrt(x)
y hay que importarla del módulomath
.
- en Haskell, como
3. Permutación cíclica
Definir la función
1 |
ciclo :: [a] -> [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,
1 2 3 |
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
.
Soluciones en Haskell
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import Test.QuickCheck ciclo :: [a] -> [a] ciclo [] = [] ciclo xs = last xs : init xs -- La propiedad es prop_ciclo :: [Int] -> Bool prop_ciclo xs = length (ciclo xs) == length xs -- La comprobación es -- λ> quickCheck prop_ciclo -- +++ OK, passed 100 tests. |
El código se encuentra en GitHub
Soluciones en Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from typing import TypeVar from hypothesis import given, strategies as st A = TypeVar('A') def ciclo(xs: list[A]) -> list[A]: if xs: return [xs[-1]] + xs[:-1] return [] # La propiedad de es @given(st.lists(st.integers())) def test_equiv_ciclo(xs): assert len(ciclo(xs)) == len(xs) # La comprobación es # src> poetry run pytest -q permutacion_ciclica.py # 1 passed in 0.39s |
El código se encuentra en GitHub.
4. Mayor número con dos dígitos dados
Definir la función
1 |
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,
1 2 |
numeroMayor 2 5 == 52 numeroMayor 5 2 == 52 |
Soluciones en Haskell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
-- 1ª definición: numeroMayor1 :: Int -> Int -> Int numeroMayor1 x y = 10 * max x y + min x y -- 2ª definición: numeroMayor2 :: Int -> Int -> Int numeroMayor2 x y | x > y = 10*x+y | otherwise = 10*y+x -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_numeroMayor :: Bool prop_numeroMayor = and [numeroMayor1 x y == numeroMayor2 x y | x <- [0..9], y <- [0..9]] -- La comprobación es -- λ> prop_numeroMayor -- True |
El código se encuentra en GitHub.
Soluciones en Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# 1ª definición def numeroMayor1(x: int, y: int) -> int: return 10 * max(x, y) + min(x, y) # 2ª definición def numeroMayor2(x: int, y: int) -> int: if x > y: return 10 * x + y return 10 * y + x # La propiedad de equivalencia de las definiciones es def test_equiv_numeroMayor(): # type: () -> bool return all(numeroMayor1(x, y) == numeroMayor2(x, y) for x in range(10) for y in range(10)) # La comprobación es # >>> test_equiv_numeroMayor() # True |
El código se encuentra en GitHub.
5. Número de raíces de la ecuación de segundo grado
Definir la función
1 |
numeroDeRaices :: (Num t, Ord t) => t -> t -> t -> Int |
tal que (numeroDeRaices a b c)
es el número de raíces reales de la ecuación . Por ejemplo,
1 2 3 |
numeroDeRaices 2 0 3 == 0 numeroDeRaices 4 4 1 == 1 numeroDeRaices 5 23 12 == 2 |
Soluciones en Haskell
1 2 3 4 5 |
numeroDeRaices :: (Num t, Ord t) => t -> t -> t -> Int numeroDeRaices a b c | d < 0 = 0 | d == 0 = 1 | otherwise = 2 where d = b^2-4*a*c |
El código se encuentra en GitHub.
Soluciones en Python
1 2 3 4 5 6 7 |
def numeroDeRaices(a: float, b: float, c: float) -> float: d = b ** 2 - 4 * a * c if d < 0: return 0 if d == 0: return 1 return 2 |
El código se encuentra en GitHub.
Comentarios
- En Haskell se usa un entorno local con
where d = b^2-4*a*c
y en Python se usa la asignaciónd = b ** 2 - 4 * a * c
.