Ir al contenido principal

Sumas de 4 primos


La conjetura de Waring sobre los números primos establece que todo número impar es primo o la suma de tres primos. La conjetura de Goldbach afirma que todo número par mayor que 2 es la suma de dos números primos. Ambos problemas siguen abiertos después de más de 200 años. En este problema no se propone su solución, sino una tarea más simple: buscar una manera de expresar los enteros mayores que 7 como suma de exactamente cuatro números primos; es decir, definir la función

suma4primos :: Integer -> (Integer,Integer,Integer,Integer)

tal que (suma4primos n) es una cuádrupla (a,b,c,d) de números primos cuya suma es n (que se supone mayor que 7). Por ejemplo,

suma4primos 24             ==  (2,2,3,17)
suma4primos 1234567890123  ==  (2,3,29,1234567890089)

Comprobar con QuickCheck que suma4primos es correcta; es decir si (suma4primos n) es (a,b,c,d) entonces los números a, b, c y d son primos y su suma es n.

Nota: Para cada n pueden existir distintas cuádruplas que cumplan la especificación. Por ejemplo, para el 16 hay tres: (2,2,5,7), (3,3,3,7) y (3,3,5,5). Cualquiera de ellas se admite como solución.


Soluciones

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

-- 1ª solución: Fuerza bruta exhaustiva
-- ====================================

suma4primos1 :: Integer -> (Integer,Integer,Integer,Integer)
suma4primos1 = head . sumas4primos

sumas4primos :: Integer -> [(Integer,Integer,Integer,Integer)]
sumas4primos n =
  [(a,b,c,d)
  | let as = takeWhile (<n) primes,
    a <- as,
    let bs = takeWhile (<n-a) (dropWhile (<a) as),
    b <- bs,
    let cs = takeWhile (<n-a-b) (dropWhile (<b) bs),
    c <- cs,
    let d = n-a-b-c,
    d `elem` dropWhile (<c) cs]

-- 2ª solución: Basada en Goldbach usando listas de primos precalculadas
-- =====================================================================

suma4primos2 :: Integer -> (Integer,Integer,Integer,Integer)
suma4primos2 n | even n    = (2,2,a,b)
               | otherwise = (2,3,c,d)
  where (a,b) = head (sumas2primos (n-4))
        (c,d) = head (sumas2primos (n-5))

sumas2primos :: Integer -> [(Integer,Integer)]
sumas2primos n = [(x,y)
                 | x <- takeWhile (<n) primes,
                   let y = n-x,
                   x <= y,
                   isPrime y]

-- 3ª solución: Basada en Goldbach con búsqueda perezosa y aritmética directa
-- ==========================================================================

suma4primos3 :: Integer -> (Integer,Integer,Integer,Integer)
suma4primos3 n | even n    = (2,2,a,b)
               | otherwise = (2,3,c,d)
  where (a,b) = suma2primos (n-4)
        (c,d) = suma2primos (n-5)

suma2primos :: Integer -> (Integer,Integer)
suma2primos n
  | n == 4    = (2, 2)
  | otherwise = head [(p, n - p) | p <- [3, 5 .. n `div` 2], isPrime p, isPrime (n - p)]

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

verifica :: IO ()
verifica = hspec spec

specG :: (Integer -> (Integer,Integer,Integer,Integer)) -> Spec
specG suma4primos = do
  it "e1" $
    suma4primos 24 `shouldBe` (2,2,3,17)

spec :: Spec
spec = do
  describe "def. 1" $ specG suma4primos1
  describe "def. 2" $ specG suma4primos2
  describe "def. 3" $ specG suma4primos3

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

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

-- La comparación es
--    λ> suma4primos1 209507
--    (2,3,5,209497)
--    (16.59 secs, 75,450,464 bytes)
--    λ> :r
--    Ok, one module loaded.
--    λ> suma4primos2 209507
--    (2,3,5,209497)
--    (0.02 secs, 808,288 bytes)
--    λ> suma4primos3 209507
--    (2,3,5,209497)
--    (0.01 secs, 807,064 bytes)

-- Propiedad
-- =========

-- La propiedad es
prop_suma4primos :: (Integer -> (Integer,Integer,Integer,Integer)) -> Property
prop_suma4primos f =
  forAll (choose (8, 1000)) $ \n ->
    let (a, b, c, d) = f n
    in all isPrime [a, b, c, d] && (a + b + c + d == n)

-- La comprobación es
--    λ> quickCheck (prop_suma4primos suma4primos1)
--    +++ OK, passed 100 tests.
--    λ> quickCheck (prop_suma4primos suma4primos2)
--    +++ OK, passed 100 tests.
--    λ> quickCheck (prop_suma4primos suma4primos3)
--    +++ OK, passed 100 tests.