Ir al contenido principal

Primos consecutivos con media capicúa


Definir la list

primosConsecutivosConMediaCapicua :: [(Int,Int,Int)]

formada por las ternas (x,y,z) tales que x e y son primos consecutivos cuya media, z, es capicúa. Por ejemplo,

λ> take 5 primosConsecutivosConMediaCapicua
[(3,5,4),(5,7,6),(7,11,9),(97,101,99),(109,113,111)]
λ> primosConsecutivosConMediaCapicua !! 500
(5687863,5687867,5687865)

Nota: Escribir las soluciones en Haskell y en Python.


Soluciones

A continuación se muestran las soluciones en Haskell y las soluciones en Python.

1. Soluciones en Haskell

import Data.List (genericTake)
import Data.Numbers.Primes (primes)
import Test.Hspec (Spec, describe, hspec, it, shouldBe)

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

primosConsecutivosConMediaCapicua1 :: [(Integer,Integer,Integer)]
primosConsecutivosConMediaCapicua1 =
  [(x,y,z) | (x,y) <- zip primosImpares (tail primosImpares),
             let z = (x + y) `div` 2,
             capicua z]

-- (primo x) se verifica si x es primo. Por ejemplo,
--    primo 7  ==  True
--    primo 8  ==  False
primo :: Integer -> Bool
primo x = [y | y <- [1..x], x `rem` y == 0] == [1,x]

-- primosImpares es la lista de los números primos impares. Por ejemplo,
--    take 10 primosImpares  ==  [3,5,7,11,13,17,19,23,29]
primosImpares :: [Integer]
primosImpares = [x | x <- [3,5..], primo x]

-- (capicua x) se verifica si x es capicúa. Por ejemplo,
--    capicua 232  == True
--    capicua 223  == False
capicua :: Integer -> Bool
capicua x = ys == reverse ys
  where ys = show x

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

primosConsecutivosConMediaCapicua2 :: [(Integer,Integer,Integer)]
primosConsecutivosConMediaCapicua2 =
  [(x,y,z) | (x,y) <- zip primosImpares2 (tail primosImpares2),
             let z = (x + y) `div` 2,
             capicua z]

primosImpares2 :: [Integer]
primosImpares2 = tail (criba [2..])
  where criba (p:ps) = p : criba [n | n <- ps, mod n p /= 0]

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

primosConsecutivosConMediaCapicua3 :: [(Integer,Integer,Integer)]
primosConsecutivosConMediaCapicua3 =
  [(x,y,z) | (x,y) <- zip (tail primos3) (drop 2 primos3),
             let z = (x + y) `div` 2,
             capicua z]

primos3 :: [Integer]
primos3 = 2 : 3 : criba3 0 (tail primos3) 3
  where criba3 k (p:ps) x = [n | n <- [x+2,x+4..p*p-2],
                                 and [n `rem` q /= 0 | q <- take k (tail primos3)]]
                            ++ criba3 (k+1) ps (p*p)

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

primosConsecutivosConMediaCapicua4 :: [(Integer,Integer,Integer)]
primosConsecutivosConMediaCapicua4 =
  [(x,y,z) | (x,y) <- zip (tail primes) (drop 2 primes),
             let z = (x + y) `div` 2,
             capicua z]

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

verifica :: IO ()
verifica = hspec spec

specG :: [(Integer,Integer,Integer)] -> Spec
specG primosConsecutivosConMediaCapicua = do
  it "e1" $
    take 5 primosConsecutivosConMediaCapicua `shouldBe`
    [(3,5,4),(5,7,6),(7,11,9),(97,101,99),(109,113,111)]

spec :: Spec
spec = do
  describe "def. 1" $ specG primosConsecutivosConMediaCapicua1
  describe "def. 2" $ specG primosConsecutivosConMediaCapicua2
  describe "def. 3" $ specG primosConsecutivosConMediaCapicua3
  describe "def. 4" $ specG primosConsecutivosConMediaCapicua4

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

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

-- La propiedad es
prop_primosConsecutivosConMediaCapicua :: Integer -> Bool
prop_primosConsecutivosConMediaCapicua n =
  all (== genericTake n primosConsecutivosConMediaCapicua1)
      [genericTake n primosConsecutivosConMediaCapicua2,
       genericTake n primosConsecutivosConMediaCapicua3,
       genericTake n primosConsecutivosConMediaCapicua4]

-- La comprobación es
--    λ> prop_primosConsecutivosConMediaCapicua 25 {-# SCC "" #-}
--    True

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

-- La comparación es
--    λ> primosConsecutivosConMediaCapicua !! 30
--    (12919,12923,12921)
--    (4.60 secs, 1,877,064,288 bytes)
--    λ> primosConsecutivosConMediaCapicua2 !! 30
--    (12919,12923,12921)
--    (0.69 secs, 407,055,848 bytes)
--    λ> primosConsecutivosConMediaCapicua3 !! 30
--    (12919,12923,12921)
--    (0.07 secs, 18,597,104 bytes)
--    λ> primosConsecutivosConMediaCapicua4 !! 30
--    (12919,12923,12921)
--    (0.01 secs, 10,065,784 bytes)
--
--    λ> primosConsecutivosConMediaCapicua2 !! 40
--    (29287,29297,29292)
--    (2.67 secs, 1,775,554,576 bytes)
--    λ> primosConsecutivosConMediaCapicua3 !! 40
--    (29287,29297,29292)
--    (0.09 secs, 32,325,808 bytes)
--    λ> primosConsecutivosConMediaCapicua4 !! 40
--    (29287,29297,29292)
--    (0.01 secs, 22,160,072 bytes)
--
--    λ> primosConsecutivosConMediaCapicua3 !! 150
--    (605503,605509,605506)
--    (3.68 secs, 2,298,403,864 bytes)
--    λ> primosConsecutivosConMediaCapicua4 !! 150
--    (605503,605509,605506)
--    (0.24 secs, 491,917,240 bytes)

2. Soluciones en Python

from itertools import count, islice
from timeit import Timer, default_timer
from typing import Iterator

from sympy import isprime

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

# primo(x) se verifica si x es primo. Por ejemplo,
#    primo(7)  ==  True
#    primo(8)  ==  False
def primo(x: int) -> bool:
    return [y for y in range(1, x + 1) if x % y == 0] == [1, x]

# primosImpares() genera la lista de los números primos impares. Por
# ejemplo,
#    >>> list(islice(primosImpares(), 10))
#    [3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
def primosImpares() -> Iterator[int]:
    return (x for x in count(3, 2) if primo(x))

# capicua(x) se verifica si x es capicúa. Por ejemplo,
#    capicua(232) == True
#    capicua(223) == False
def capicua(x: int) -> bool:
    ys = str(x)
    return ys == ys[::-1]

def primosConsecutivosConMediaCapicua1() -> Iterator[tuple[int, int, int]]:
    return (
        (x, y, z)
        for x, y in zip(primosImpares(), islice(primosImpares(), 1, None))
        if (z := (x + y) // 2) and capicua(z)
    )

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

# Generador de números primos usando la criba de Eratóstenes.
def primos2() -> Iterator[int]:
    no_primos = {}
    for n in count(2):
        if n not in no_primos:
            yield n
            no_primos[n * n] = [n]
        else:
            for p in no_primos[n]:
                no_primos.setdefault(p + n, []).append(p)
            del no_primos[n]

def primosImpares2() -> Iterator[int]:
    return islice(primos2(), 1, None)

def primosConsecutivosConMediaCapicua2() -> Iterator[tuple[int, int, int]]:
    return (
        (x, y, z)
        for x, y in zip(primosImpares2(), islice(primosImpares2(), 1, None))
        if (z := (x + y) // 2) and capicua(z)
    )

# 3ª solución
# ===========

def primosImpares3() -> Iterator[int]:
    return (n for n in count(3, 2) if isprime(n))

def primosConsecutivosConMediaCapicua3() -> Iterator[tuple[int, int, int]]:
    return (
        (x, y, z)
        for x, y in zip(primosImpares3(), islice(primosImpares3(), 1, None))
        if (z := (x + y) // 2) and capicua(z)
    )

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

def test_primosConsecutivosConMediaCapicua() -> None:
    for primosConsecutivosConMediaCapicua \
        in[primosConsecutivosConMediaCapicua1,
           primosConsecutivosConMediaCapicua2,
           primosConsecutivosConMediaCapicua3]:
        assert list(islice(primosConsecutivosConMediaCapicua(), 5)) == \
            [(3, 5, 4), (5, 7, 6), (7, 11, 9), (97, 101, 99), (109, 113, 111)]
        print(f"Verificado {primosConsecutivosConMediaCapicua.__name__}")

# La verificación es
#    >>> test_primosConsecutivosConMediaCapicua()
#    Verificado primosConsecutivosConMediaCapicua1
#    Verificado primosConsecutivosConMediaCapicua2
#    Verificado primosConsecutivosConMediaCapicua3

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

# nth(i, n) es el n-ésimo elemento del iterador i. Por ejemplo,
#    nth(primos2(), 4) == 11
def nth(i: Iterator[int], n: int) -> int:
    return list(islice(i, n, n+1))[0]

# La propiedad es
def test_primosConsecutivosConMediaCapicua_equiv() -> None:
    assert list(islice(primosConsecutivosConMediaCapicua1(), 30)) == \
        list(islice(primosConsecutivosConMediaCapicua2(), 30))
    assert list(islice(primosConsecutivosConMediaCapicua2(), 100)) == \
        list(islice(primosConsecutivosConMediaCapicua3(), 100))
    print("Verificado")

# La comprobación es
#    >>> test_primosConsecutivosConMediaCapicua_equiv()
#    Verificado

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

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

# La comparación es
#    >>> tiempo('nth(primosConsecutivosConMediaCapicua1(), 30)')
#    4.17 segundos
#    >>> tiempo('nth(primosConsecutivosConMediaCapicua2(), 30)')
#    0.02 segundos
#    >>> tiempo('nth(primosConsecutivosConMediaCapicua3(), 30)')
#    0.04 segundos
#
#    >>> tiempo('nth(primosConsecutivosConMediaCapicua2(), 200)')
#    1.54 segundos
#    >>> tiempo('nth(primosConsecutivosConMediaCapicua3(), 200)')
#    3.33 segundos