Primos equidistantes
Definir la función
primosEquidistantes :: Integer -> [(Integer,Integer)]
tal que (primosEquidistantes k)
es la lista de los pares de primos cuya diferencia es k
. Por ejemplo,
take 3 (primosEquidistantes 2) == [(3,5),(5,7),(11,13)] take 3 (primosEquidistantes 4) == [(7,11),(13,17),(19,23)] take 3 (primosEquidistantes 6) == [(23,29),(31,37),(47,53)] take 3 (primosEquidistantes 8) == [(89,97),(359,367),(389,397)] primosEquidistantes 4 !! (10^5) == (18467047,18467051)
Nota: Escribir las soluciones en Haskell, en Python y en Common Lisp.
Soluciones
A continuación se muestran
- las soluciones en Haskell,
- las soluciones en Python y
- las soluciones en Common Lisp
1. Soluciones en Haskell
module Primos_equidistantes where import Data.Numbers.Primes (primes) import Test.Hspec (Spec, describe, hspec, it, shouldBe) -- 1ª solución -- =========== primosEquidistantes1 :: Integer -> [(Integer,Integer)] primosEquidistantes1 k = aux primos where aux (x:y:ps) | y - x == k = (x,y) : aux (y:ps) | otherwise = aux (y:ps) -- (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] -- primos es la lista de los números primos. Por ejemplo, -- take 10 primos == [2,3,5,7,11,13,17,19,23,29] primos :: [Integer] primos = 2 : [x | x <- [3,5..], primo x] -- 2ª solución -- =========== primosEquidistantes2 :: Integer -> [(Integer,Integer)] primosEquidistantes2 k = aux primos2 where aux (x:y:ps) | y - x == k = (x,y) : aux (y:ps) | otherwise = aux (y:ps) primos2 :: [Integer] primos2 = criba [2..] where criba (p:ps) = p : criba [n | n <- ps, mod n p /= 0] -- 3ª solución -- =========== primosEquidistantes3 :: Integer -> [(Integer,Integer)] primosEquidistantes3 k = [(x,y) | (x,y) <- zip primos2 (tail primos2) , y - x == k] -- 4ª solución -- =========== primosEquidistantes4 :: Integer -> [(Integer,Integer)] primosEquidistantes4 k = aux primes where aux (x:y:ps) | y - x == k = (x,y) : aux (y:ps) | otherwise = aux (y:ps) -- 5ª solución -- =========== primosEquidistantes5 :: Integer -> [(Integer,Integer)] primosEquidistantes5 k = [(x,y) | (x,y) <- zip primes (tail primes) , y - x == k] -- Verificación -- ============ verifica :: IO () verifica = hspec spec specG :: (Integer -> [(Integer,Integer)]) -> Spec specG primosEquidistantes = do it "e1" $ take 3 (primosEquidistantes 2) `shouldBe` [(3,5),(5,7),(11,13)] it "e2" $ take 3 (primosEquidistantes 4) `shouldBe` [(7,11),(13,17),(19,23)] it "e3" $ take 3 (primosEquidistantes 6) `shouldBe` [(23,29),(31,37),(47,53)] it "e4" $ take 3 (primosEquidistantes 8) `shouldBe` [(89,97),(359,367),(389,397)] spec :: Spec spec = do describe "def. 1" $ specG primosEquidistantes1 describe "def. 2" $ specG primosEquidistantes2 describe "def. 3" $ specG primosEquidistantes3 describe "def. 4" $ specG primosEquidistantes4 describe "def. 5" $ specG primosEquidistantes5 -- La verificación es -- λ> verifica -- 20 examples, 0 failures -- Comprobación de equivalencia -- ============================ -- La propiedad es prop_primosEquidistantes :: Int -> Integer -> Bool prop_primosEquidistantes n k = all (== take n (primosEquidistantes1 k)) [take n (f k) | f <- [primosEquidistantes2, primosEquidistantes3, primosEquidistantes4, primosEquidistantes5]] -- La comprobación es -- λ> prop_primosEquidistantes 100 4 -- True -- Comparación de eficiencia -- ========================= -- La comparación es -- λ> primosEquidistantes1 4 !! 200 -- (9829,9833) -- (2.60 secs, 1,126,458,272 bytes) -- λ> primosEquidistantes2 4 !! 200 -- (9829,9833) -- (0.44 secs, 249,622,048 bytes) -- λ> primosEquidistantes3 4 !! 200 -- (9829,9833) -- (0.36 secs, 207,549,592 bytes) -- λ> primosEquidistantes4 4 !! 200 -- (9829,9833) -- (0.02 secs, 4,012,848 bytes) -- λ> primosEquidistantes5 4 !! 200 -- (9829,9833) -- (0.01 secs, 7,085,072 bytes) -- -- λ> primosEquidistantes2 4 !! 600 -- (41617,41621) -- (5.67 secs, 3,340,313,480 bytes) -- λ> primosEquidistantes3 4 !! 600 -- (41617,41621) -- (5.43 secs, 3,090,994,096 bytes) -- λ> primosEquidistantes4 4 !! 600 -- (41617,41621) -- (0.03 secs, 15,465,824 bytes) -- λ> primosEquidistantes5 4 !! 600 -- (41617,41621) -- (0.04 secs, 28,858,232 bytes) -- -- λ> primosEquidistantes4 4 !! (10^5) -- (18467047,18467051) -- (3.99 secs, 9,565,715,488 bytes) -- λ> primosEquidistantes5 4 !! (10^5) -- (18467047,18467051) -- (7.95 secs, 18,712,469,144 bytes)
2. Soluciones en Python
from itertools import chain, count, islice, tee 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] # primos() es la lista de los números primos. Por ejemplo, # >>> list(islice(primos(), 10)) # [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] def primos() -> Iterator[int]: return chain([2], (x for x in count(3, 2) if primo(x))) def primosEquidistantes1(k: int) -> Iterator[tuple[int,int]]: a, b = tee(primos()) next(b, None) return ((x,y) for (x,y) in zip(a, b) if y - x == k) # 2ª solución # =========== def primos2() -> Iterator[int]: return (n for n in count() if isprime(n)) def primosEquidistantes2(k: int) -> Iterator[tuple[int,int]]: a, b = tee(primos2()) next(b, None) return ((x,y) for (x,y) in zip(a, b) if y - x == k) # Verificación # ============ def test_primosEquidestantes() -> None: for primosEquidistantes in [primosEquidistantes1, primosEquidistantes2]: assert list(islice(primosEquidistantes(2), 3)) == \ [(3, 5), (5, 7), (11, 13)] assert list(islice(primosEquidistantes(4), 3)) == \ [(7, 11), (13, 17), (19, 23)] assert list(islice(primosEquidistantes(6), 3)) == \ [(23, 29), (31, 37), (47, 53)] assert list(islice(primosEquidistantes(8), 3)) == \ [(89, 97), (359, 367), (389, 397)] print("Verificado") # La verificación es # >>> test_primosEquidestantes() # Verificado # Comprobación de equivalencia # ============================ # La propiedad es def primosEquidistantes_equiv(n: int, k: int) -> bool: return list(islice(primosEquidistantes1(k), n)) == \ list(islice(primosEquidistantes2(k), n)) # La comprobación es # >>> primosEquidistantes_equiv(100, 4) # True # Comparación de eficiencia # ========================= 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(primosEquidistantes1(4), 300))') # 3.19 segundos # >>> tiempo('list(islice(primosEquidistantes2(4), 300))') # 0.01 segundos
3. Soluciones en Common Lisp
(ql:quickload "fiveam" :silent t) (defpackage :primos-equidistantes (:use :cl :fiveam)) (in-package :primos-equidistantes) ;;; Listas infinitas ;;; ================ ;;; En las soluciones se usarán las siguientes funciones para trabajar ;;; con listas infinitas (ver "Streams in Common Lisp" ;;; https://tinyurl.com/266685bz). ;;; (delay e) retrasa la evaluación de la función e. Por ejemplo, ;;; > (delay (+ 2 3)) ;;; #<FUNCTION (LAMBDA ()) {5363B46B}> (defmacro delay (e) `(lambda () ,e)) ;;; (force e) evalúa la expresión retrasada retrasada e. Por ejemplo, ;;; > (force (retrasa (+ 2 3))) ;;; 5 (defun force (e) (funcall e)) ;;; (s-cons x y) crea una lista perezosa cuyo primer elemento es x ;;; y el resto es la expresión y retrasada. Por ejemplo, ;;; > (setf ej-lp (s-cons 1 (s-cons 2 nil))) ;;; (1 . #<FUNCTION (LAMBDA ()) {5368A37B}>) (defmacro s-cons (x y) `(cons ,x (delay ,y))) ;;; (s-first s) es el primer elemento de la lista perezosa s. Por ;;; ejemplo, ;;; > (s-first (s-cons 1 (s-cons 2 nil))) ;;; 1 (defun s-first (s) (car s)) ;;; (s-rest s) es el resto de la lista perezosa s. Por ejmplo, ;;; > (s-rest (s-cons 1 (s-cons 2 nil))) ;;; (2 . #<FUNCTION (LAMBDA ()) {5363B00B}>) (defun s-rest (s) (force (cdr s))) ;;; (enteros n) es la lista infinita de los números enteros comenzando ;;; desde n (por defecto 1). Por ejemplo, ;;; > (enteros) ;;; (1 . #<FUNCTION (LAMBDA () :IN ENTEROS) {100215088B}>) (defun enteros (&optional (n 1)) (s-cons n (enteros (1+ n)))) ;;; (s-take n s) es la lista de los n primeros elementos de la lista ;;; infinita s. ;;; > (take 10 (enteros)) ;;; (1 2 3 4 5 6 7 8 9 10) ;;; > (take 10 (enteros 2)) ;;; (2 3 4 5 6 7 8 9 10 11) (defun s-take (n s) (if (zerop n) nil (cons (s-first s) (s-take (1- n) (s-rest s))))) ;;; (s-filter p s) es la sublista infinita de s con los elementos que ;;; cumplen el predicado p. Por ejemplo, ;;; > (s-take 5 (s-filter #'evenp (enteros))) ;;; (2 4 6 8 10) (defun s-filter (p s) (if (funcall p (s-first s)) (s-cons (s-first s) (s-filter p (s-rest s))) (s-filter p (s-rest s)))) ;;; (s-zip xs ys) es la lista perezosa donde cada elemento es un par ;;; formado por el primer elemento de xs y el primer elemento de ys, ;;; seguido por el segundo elemento de xs y el segundo de ys, y así ;;; sucesivamente. Por ejemplo, ;;; > (s-take 5 (s-zip (enteros) (s-rest (enteros)))) ;;; ((1 2) (2 3) (3 4) (4 5) (5 6)) (defun s-zip (xs ys) (s-cons (list (s-first xs) (s-first ys)) (s-zip (s-rest xs) (s-rest ys)))) ;;; 1ª solución ;;; =========== ;;; (primo1 x) se verifica si x es primo. Por ejemplo, ;;; > (primo1 7) ;;; T ;;; > (primo1 8) ;;; NIL (defun primo1 (x) (equal (loop for y from 1 to x when (= (mod x y) 0) collect y) (list 1 x))) ;;; (primos1) es la lista de los números primos. Por ejemplo, ;;; > (s-take 10 (primos1)) ;;; (2 3 5 7 11 13 17 19 23 29) (defun primos1 () (s-filter #'primo1 (enteros 2))) (defun primos-equidistantes1 (k) (labels ((aux (ps) (if (null (s-rest ps)) nil (let ((x (s-first ps)) (y (s-first (s-rest ps)))) (if (= (- y x) k) (s-cons (list x y) (aux (s-rest ps))) (aux (s-rest ps))))))) (aux (primos1)))) ;;; 2ª solución ;;; =========== ;;; (primo2 x) se verifica si x es primo. Por ejemplo, ;;; > (primo2 7) ;;; T ;;; > (primo2 8) ;;; NIL (defun primo2 (x) (and (>= x 2) (loop for y from 2 to (isqrt x) when (= (mod x y) 0) do (return nil) finally (return t)))) ;;; (impares n) es la lista infinita de los números impares comenzando ;;; desde n (por defecto 1). Por ejemplo, ;;; > (s-take 10 (impares)) ;;; (1 3 5 7 9 11 13 15 17 19) ;;; > (s-take 10 (impares 3)) ;;; (3 5 7 9 11 13 15 17 19 21) (defun impares (&optional (n 1)) (s-cons n (impares (+ n 2)))) ;;; (primos2) es la lista de los números primos. Por ejemplo, ;;; > (s-take 10 (primos2)) ;;; (2 3 5 7 11 13 17 19 23 29) (defun primos2 () (s-cons 2 (s-filter #'primo2 (impares 3)))) (defun primos-equidistantes2 (k) (labels ((aux (ps) (if (null (s-rest ps)) nil (let ((x (s-first ps)) (y (s-first (s-rest ps)))) (if (= (- y x) k) (s-cons (list x y) (aux (s-rest ps))) (aux (s-rest ps))))))) (aux (primos2)))) ;;; 3ª solución ;;; =========== ;;; (primo3 x) se verifica si x es primo. Por ejemplo, ;;; > (primo3 7) ;;; T ;;; > (primo3 8) ;;; NIL (defun primo3 (x) (cond ((< x 2) nil) ((= x 2) t) ((evenp x) nil) (t (loop for y from 3 to (isqrt x) by 2 when (= (mod x y) 0) do (return nil) finally (return t))))) ;;; (primos3) es la lista de los números primos. Por ejemplo, ;;; > (s-take 10 (primos2)) ;;; (2 3 5 7 11 13 17 19 23 29) (defun primos3 () (s-cons 2 (s-filter #'primo3 (impares 3)))) (defun primos-equidistantes3 (k) (labels ((aux (ps) (if (null (s-rest ps)) nil (let ((x (s-first ps)) (y (s-first (s-rest ps)))) (if (= (- y x) k) (s-cons (list x y) (aux (s-rest ps))) (aux (s-rest ps))))))) (aux (primos3)))) ;;; 4ª solución ;;; =========== (defun primos4 () (defun criba (s) (s-cons (s-first s) (criba (s-filter (lambda (x) (/= (mod x (s-first s)) 0)) (s-rest s))))) (criba (enteros 2))) (defun primos-equidistantes4 (k) (labels ((aux (ps) (let ((x (s-first ps)) (y (s-first (s-rest ps)))) (if (= (- y x) k) (s-cons (list x y) (aux (s-rest ps))) (aux (s-rest ps)))))) (aux (primos4)))) ;;; 5ª solución ;;; =========== (defun primos-equidistantes5 (k) (s-filter (lambda (par) (= (- (second par) (first par)) k)) (s-zip (primos3) (s-rest (primos3))))) ;;; Verificación ;;; ============ (test primos-equidistantes (mapc (lambda (primos-equidistantes) (is (equal (s-take 3 (funcall primos-equidistantes 2)) '((3 5) (5 7) (11 13)))) (is (equal (s-take 3 (funcall primos-equidistantes 4)) '((7 11) (13 17) (19 23)))) (is (equal (s-take 3 (funcall primos-equidistantes 6)) '((23 29) (31 37) (47 53))))) '(primos-equidistantes1 primos-equidistantes2 primos-equidistantes3 primos-equidistantes4 primos-equidistantes5 ))) (defun verifica () (run 'primos-equidistantes)) ;;; La verificación es ;;; > (verifica) ;;; ;;; Running test PRIMOS-EQUIDISTANTES ............... ;;; Comprobación de equivalencia ;;; ============================ (defun primos-equidistantes-equiv (n k) (every (lambda (ys) (equal ys (s-take n (primos-equidistantes1 k)))) (mapcar (lambda (f) (s-take n (funcall f k))) '(primos-equidistantes2 primos-equidistantes3 primos-equidistantes4 primos-equidistantes5)))) ;;; La comprobación es ;;; > (primos-equidistantes-equiv 100 4) ;;; T ;;; Comparación de eficiencia ;;; ========================= ;;; La comparación es ;;; > (time (last (s-take 201 (primos-equidistantes1 4)))) ;;; 2.499 seconds of real time ;;; > (time (last (s-take 201 (primos-equidistantes2 4)))) ;;; 0.030 seconds of real time ;;; > (time (last (s-take 201 (primos-equidistantes3 4)))) ;;; 0.020 seconds of real time ;;; > (time (last (s-take 201 (primos-equidistantes4 4)))) ;;; 0.164 seconds of real time ;;; > (time (last (s-take 201 (primos-equidistantes5 4)))) ;;; 0.012 seconds of real time ;;; ;;; > (time (last (s-take 601 (primos-equidistantes2 4)))) ;;; 0.061 seconds of real time ;;; > (time (last (s-take 601 (primos-equidistantes3 4)))) ;;; 0.069 seconds of real time ;;; > (time (last (s-take 601 (primos-equidistantes4 4)))) ;;; 3.182 seconds of real time ;;; > (time (last (s-take 601 (primos-equidistantes5 4)))) ;;; 0.047 seconds of real time ;;; ;;; > (time (last (s-take (expt 10 4) (primos-equidistantes2 4)))) ;;; 4.805 seconds of real time ;;; > (time (last (s-take (expt 10 4) (primos-equidistantes3 4)))) ;;; 2.524 seconds of real time ;;; > (time (last (s-take (expt 10 4) (primos-equidistantes5 4)))) ;;; 1.653 seconds of real time