Ir al contenido principal

Números de Pentanacci

Los números de Fibonacci se definen mediante las ecuaciones

   F(0) = 0
   F(1) = 1
   F(n) = F(n-1) + F(n-2), si n > 1

Los primeros números de Fibonacci son

   0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, ...

Una generalización de los anteriores son los números de Pentanacci definidos por las siguientes ecuaciones

   P(0) = 0
   P(1) = 1
   P(2) = 1
   P(3) = 2
   P(4) = 4
   P(n) = P(n-1) + P(n-2) + P(n-3) + P(n-4) + P(n-5), si n > 4

Los primeros números de Pentanacci son

  0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525, ...

Definir las funciones

   pentanacci  :: Integer -> Integer
   pentanaccis :: [Integer]

tales que

  • pentanacci n es el n-ésimo número de Pentanacci. Por ejemplo,
     λ> pentanacci 14
     3525
     λ> pentanacci (10^5) `mod` 10^30
     482929150584077921552549215816
     λ> length (show (pentanacci (10^5)))
     29357
  • pentanaccis es la lista de los números de Pentanacci. Por ejemplo,
     λ> take 15 pentanacci
     [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]

Soluciones

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

Soluciones en Haskell

module Numeros_de_Pentanacci where

import Data.List (genericIndex, zipWith5)
import Test.Hspec (Spec, hspec, it, shouldBe)
import Test.QuickCheck (NonNegative (NonNegative), quickCheckWith, maxSize, stdArgs)

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

pentanacci1  :: Integer -> Integer
pentanacci1  0 = 0
pentanacci1  1 = 1
pentanacci1  2 = 1
pentanacci1  3 = 2
pentanacci1  4 = 4
pentanacci1  n = sum [pentanacci1 (n-k) | k <- [1..5]]

pentanaccis1 :: [Integer]
pentanaccis1 = [pentanacci1 n | n <- [0..]]

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

pentanaccis2 :: [Integer]
pentanaccis2 =
  0 : 1 : 1 : 2 : 4 : zipWith5 f (r 0) (r 1) (r 2) (r 3) (r 4)
  where f a b c d e = a+b+c+d+e
        r n         = drop n pentanaccis2

pentanacci2  :: Integer -> Integer
pentanacci2 n = pentanaccis2 `genericIndex` n

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

pentanaccis3 :: [Integer]
pentanaccis3 = p (0, 1, 1, 2, 4)
  where p (a, b, c, d, e) = a : p (b, c, d, e, a + b + c + d + e)

pentanacci3  :: Integer -> Integer
pentanacci3 n = pentanaccis3 `genericIndex` n

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

pentanaccis4 :: [Integer]
pentanaccis4 = 0: 1: 1: 2: 4: p pentanaccis4
  where p (a:b:c:d:e:xs) = (a+b+c+d+e): p (b:c:d:e:xs)

pentanacci4  :: Integer -> Integer
pentanacci4 n = pentanaccis4 `genericIndex` n

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

verifica :: IO ()
verifica = hspec spec

spec :: Spec
spec = do
  it "ej1" $
    take 15 pentanaccis1 `shouldBe`
    [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
  it "ej2" $
    take 15 pentanaccis2 `shouldBe`
    [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
  it "ej3" $
    take 15 pentanaccis3 `shouldBe`
    [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]
  it "ej4" $
    take 15 pentanaccis4 `shouldBe`
    [0,1,1,2,4,8,16,31,61,120,236,464,912,1793,3525]

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

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

-- La propiedad es
prop_pentanaccis :: NonNegative Int -> Bool
prop_pentanaccis (NonNegative n) =
  all (== pentanaccis1 !! n)
      [pentanaccis1 !! n,
       pentanaccis2 !! n,
       pentanaccis3 !! n]

-- La comprobación es
--    λ> quickCheckWith (stdArgs {maxSize=25}) prop_pentanaccis
--    +++ OK, passed 100 tests.

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

-- La comparación es
--    λ> pentanacci1 25
--    5976577
--    (4.64 secs, 1,865,626,496 bytes)
--    λ> pentanacci2 25
--    5976577
--    (0.00 secs, 578,584 bytes)
--
--    λ> length (show (pentanacci2 (10^5)))
--    29357
--    (1.16 secs, 2,543,272,136 bytes)
--    λ> length (show (pentanacci3 (10^5)))
--    29357
--    (1.00 secs, 2,560,881,608 bytes)
--    λ> length (show (pentanacci4 (10^5)))
--    29357
--    (1.03 secs, 2,592,078,744 bytes)

Soluciones en Python

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

set_int_max_str_digits(30000)

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

def pentanacci1(n: int) -> int:
    if n == 0:
        return 0
    if n == 1:
        return 1
    if n == 2:
        return 1
    if n == 3:
        return 2
    if n == 4:
        return 4
    return sum((pentanacci1(n-k) for k in range(1, 6)))

def pentanaccis1() -> Iterator[int]:
    return (pentanacci1(n) for n in count())

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

def pentanaccis2() -> Iterator[int]:
    seq = [0, 1, 1, 2, 4]
    while True:
        yield seq[0]
        seq.append(sum(seq))
        seq.pop(0)

def nth(i: Iterator[int], n: int) -> int:
    return list(islice(i, n, n+1))[0]

def pentanacci2(n: int) -> int:
    return nth(pentanaccis2(), n)

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

def test_pentanacci() -> None:
    assert pentanacci1(14) == 3525
    assert list(islice(pentanaccis1(), 15)) == \
        [0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525]
    assert pentanacci2(14) == 3525
    assert list(islice(pentanaccis2(), 15)) == \
        [0, 1, 1, 2, 4, 8, 16, 31, 61, 120, 236, 464, 912, 1793, 3525]
    print("Verificado")

# La verificación es
#    >>> test_pentanacci()
#    Verificado

# Comprobación de equivalencia
# ============================

# La propiedad es
def test_pentanacci_equiv() -> bool:
    return list(islice(pentanaccis1(), 25)) == list(islice(pentanaccis2(), 25))

# La comprobación es
#    >>> test_pentanacci_equiv()
#    True

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

# 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('pentanacci1(28)')
#    8.24 segundos
#    >>> tiempo('pentanacci2(28)')
#    0.00 segundos