Exercitium

Primos cubanos
Publicado el 4 de febrero de 2024 por José A. Alonso

Índice

1. Enunciado

Un primo cubano es un número primo que se puede escribir como diferencia de dos cubos consecutivos. Por ejemplo, el 61 es un primo cubano porque es primo y 61 = 5³-4³.

Definir la sucesión

cubanos :: [Integer]

tal que sus elementos son los números cubanos. Por ejemplo,

λ> take 15 cubanos
[7,19,37,61,127,271,331,397,547,631,919,1657,1801,1951,2269]

2. Soluciones con Haskell

module Primos_cubanos where

import Data.Numbers.Primes (isPrime)
import Test.QuickCheck
import Test.Hspec (Spec, describe, hspec, it, shouldBe)

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

cubanos1 :: [Integer]
cubanos1 = filter isPrime (zipWith (-) (tail cubos) cubos)

-- cubos es la lista de los cubos. Por ejemplo,
--    λ> take 10 cubos
--    [1,8,27,64,125,216,343,512,729,1000]
cubos :: [Integer]
cubos = map (^3) [1..]

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

cubanos2 :: [Integer]
cubanos2 = filter isPrime [(x+1)^3 - x^3 | x <- [1..]]

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

cubanos3 :: [Integer]
cubanos3 = filter isPrime [3*x^2 + 3*x + 1 | x <- [1..]]

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

verifica :: IO ()
verifica = hspec spec

specG :: [Integer] -> Spec
specG cubanos = do
  it "e1" $
    take 15 cubanos `shouldBe`
    [7,19,37,61,127,271,331,397,547,631,919,1657,1801,1951,2269]

spec :: Spec
spec = do
  describe "def. 1" $ specG cubanos1
  describe "def. 2" $ specG cubanos2
  describe "def. 3" $ specG cubanos3

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

-- Comprobación de equivalencia
-- ============================

-- La propiedad es
prop_cubanos :: NonNegative Int -> Bool
prop_cubanos (NonNegative n) =
  all (== cubanos1 !! n)
      [cubanos2 !! n,
       cubanos3 !! n]

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

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

-- La comparación es
--    λ> cubanos1 !! 3000
--    795066361
--    (4.21 secs, 16,953,612,192 bytes)
--    λ> cubanos2 !! 3000
--    795066361
--    (4.27 secs, 16,962,597,288 bytes)
--    λ> cubanos3 !! 3000
--    795066361
--    (4.29 secs, 16,956,085,672 bytes)

3. Soluciones con Python

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

from sympy import isprime

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

# cubos() es la lista de los cubos. Por ejemplo,
#    >>> list(islice(cubos(), 10))
#    [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
def cubos() -> Iterator[int]:
    return (x**3 for x in count(1))

# parejasDeCubos() es la lista de las parejas de cubos consecutivos. Por
# ejemplo,
#    >>> list(islice(parejasDeCubos(), 5))
#    [(1, 8), (8, 27), (27, 64), (64, 125), (125, 216)]
def parejasDeCubos() -> Iterator[tuple[int, int]]:
    a, b = tee(cubos())
    next(b, None)
    return zip(a, b)

# diferenciasDeCubos() es la lista de las diferencias de cubos
# consecutivos. Por ejemplo,
#    >>> list(islice(diferenciasDeCubos(), 5))
#    [7, 19, 37, 61, 91]
def diferenciasDeCubos() -> Iterator[int]:
    return (j - i for i, j in parejasDeCubos())

def cubanos1() -> Iterator[int]:
    return (x for x in diferenciasDeCubos() if isprime(x))

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

def cubanos2() -> Iterator[int]:
    return ((x+1)**3 - x**3 for x in count(1) if isprime((x+1)**3 - x**3))

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

def cubanos3() -> Iterator[int]:
    return (y for x in count(1) if isprime((y := (x+1)**3 - x**3)))

# 4ª solución
# ===========

def cubanos4() -> Iterator[int]:
    return (y for x in count(1) if isprime((y := 3*x**2 + 3*x + 1)))

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

def test_cubanos() -> None:
    for cubanos in [cubanos1, cubanos2, cubanos3, cubanos4]:
        assert list(islice(cubanos(), 15)) == \
            [7,19,37,61,127,271,331,397,547,631,919,1657,1801,1951,2269]
    print ("Verificado")

# 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
# =========================

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('list(islice(cubanos1(), 30000))')
#    1.54 segundos
#    >>> tiempo('list(islice(cubanos1(), 40000))')
#    2.20 segundos
#    >>> tiempo('list(islice(cubanos2(), 40000))')
#    2.22 segundos
#    >>> tiempo('list(islice(cubanos3(), 40000))')
#    2.19 segundos
#    >>> tiempo('list(islice(cubanos4(), 40000))')
#    2.15 segundos

Exercitium

José A. Alonso Jiménez

Sevilla, 07 de abril del 2024

Licencia: Creative Commons.