How to resolve the algorithm Recaman's sequence step by step in the Python programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm Recaman's sequence step by step in the Python programming language

Table of Contents

Problem Statement

The Recamán's sequence generates Natural numbers. Starting from a(0)=0, the n'th term a(n), where n>0, is the previous term minus n i.e a(n) = a(n-1) - n but only if this is both positive and has not been previousely generated. If the conditions don't hold then a(n) = a(n-1) + n.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Recaman's sequence step by step in the Python programming language

1. Recamán's Sequence Class (Recamans):

  • This class represents a generator for Recamán's sequence and keeps track of the sequence's history (a and n) as it generates terms.

2. Generator (Recamans.__call__):

  • It yields the next term in Recamán's sequence.
  • It initializes with 0, stores the sequence in a (set), and increments n (term count).
  • It calculates the next term (nxt) based on the current term (an1) and n.
  • If nxt is negative or already in a, it calculates a new nxt.
  • It adds nxt to a and returns it.

3. Main Execution (if __name__ == '__main__'):

  • Creating a Recamán's sequence object:

    • recamans = Recamans() creates an instance that generates Recamán's sequence.
  • Printing the first 15 terms:

    • print("First fifteen members of Recamans sequence:", list(islice(recamans(), 15))) prints the first 15 terms using the islice function.
  • Finding the first duplicate term:

    • An empty set so_far is used to track seen terms.
    • The generator is iterated over until a term (term) seen in so_far is found.
    • print(f"First duplicate number in series is: a({recamans.n}) = {term}") prints the first duplicate term and its position a(n).
  • Checking if a specified range is covered:

    • A set of integers setn is created for the range 0 .. n.
    • The generator is iterated over until the set setn is a subset of the sequence set recamans.a.
    • print(f"Range 0 ..{n} is covered by terms up to a({recamans.n})") prints the number of terms needed to cover the range.

4. Recaman's Sequence as a Function (recamanUntil):

  • recamanUntil is a function that takes a predicate p and returns the terms of the Recamán sequence until the predicate holds.

5. Recaman's Sequence Successor (recamanSucc):

  • recamanSucc calculates the successor term in Recamán's sequence based on the previous term r, the current term count n, and the set of seen terms seen.

6. Test Cases (main Function):

  • Prints the first 15 Recaman terms.
  • Finds the first duplicated Recaman term.
  • Calculates the number of Recaman terms needed to generate all integers within a given range 0 .. 1000.

7. Generic Functions:

  • enumFromTo: Generates a range of integers from m to n.
  • iterate: Iterates a function f over a value x.
  • snd: Gets the second component of a tuple.
  • take: Gets the first n elements from a list or iterator.
  • until: Applies a function f until a predicate p holds, returning the final result.

8. Iterative Recamán's Sequence (main Function):

  • Creates an infinite iterator for Recamán's sequence as tuples (n, r, blnNew).
  • Uses the recamanTupleSucc function to calculate the next tuple.
  • Prints the first 15 Recaman terms, the first duplicate term, and the number of terms needed to cover the range 0 .. 1000.

Source code in the python programming language

from itertools import islice

class Recamans():
    "Recamán's sequence generator callable class"
    def __init__(self):
        self.a = None   # Set of results so far
        self.n = None   # n'th term (counting from zero)
    
    def __call__(self):
        "Recamán's sequence  generator"
        nxt = 0
        a, n = {nxt}, 0
        self.a = a
        self.n = n
        yield nxt
        while True:
            an1, n = nxt, n + 1
            nxt = an1 - n
            if nxt < 0 or nxt in a:
                nxt = an1 + n
            a.add(nxt)
            self.n = n
            yield nxt

if __name__ == '__main__':
    recamans = Recamans()
    print("First fifteen members of Recamans sequence:", 
          list(islice(recamans(), 15)))

    so_far = set()
    for term in recamans():
        if term in so_far:
            print(f"First duplicate number in series is: a({recamans.n}) = {term}")
            break
        so_far.add(term)
    
    n = 1_000
    setn = set(range(n + 1))    # The target set of numbers to be covered
    for _ in recamans():
        if setn.issubset(recamans.a):
            print(f"Range 0 ..{n} is covered by terms up to a({recamans.n})")
            break


'''Recaman sequence'''


# recamanUntil :: (Int -> Set Int > [Int] -> Bool) -> [Int]
def recamanUntil(p):
    '''All terms of the Recaman series before the
       first term for which the predicate p holds.'''
    n = 1
    r = 0  # First term of series
    rs = [r]
    seen = set(rs)
    blnNew = True
    while not p(seen, n, r, blnNew):
        r = recamanSucc(seen, n, r)
        blnNew = r not in seen
        seen.add(r)
        rs.append(r)
        n = 1 + n
    return rs


# recamanSucc :: Set Int -> Int -> Int
def recamanSucc(seen, n, r):
    '''The successor for a given Recaman term,
       given the set of Recaman terms seen so far.'''
    back = r - n
    return n + r if 0 > back or (back in seen) else back


# ------------------------- TEST -------------------------
# main :: IO ()
def main():
    '''Test'''
    print(
        'First 15 Recaman:\r',
        recamanUntil(
            lambda seen, n, r, _: 15 == n
        )
    )
    print(
        'First duplicated Recaman:\r',
        recamanUntil(
            lambda seen, n, r, blnNew: not blnNew
        )[-1]
    )
    setK = set(enumFromTo(0)(1000))
    print(
        'Number of Recaman terms needed to generate',
        'all integers from [0..1000]:\r',
        len(recamanUntil(
            lambda seen, n, r, blnNew: (
                blnNew and 1001 > r and setK.issubset(seen)
            )
        )) - 1
    )


# ----------------------- GENERIC ------------------------

# enumFromTo :: (Int, Int) -> [Int]
def enumFromTo(m):
    '''Integer enumeration from m to n.'''
    return lambda n: range(m, 1 + n)


if __name__ == '__main__':
    main()


'''Recaman by iteration of a function over a tuple.'''

from itertools import (islice)


# recamanTupleSucc :: Set Int -> (Int, Int, Bool) -> (Int, Int, Bool)
def recamanTupleSucc(seen):
    '''The Nth in a series of Recaman tuples,
       (N, previous term, boolPreviouslySeen?)
       given the set of all terms seen so far.'''
    def go(n, r, _):
        back = r - n
        nxt = n + r if 0 > back or (back in seen) else back
        bln = nxt in seen
        seen.add(nxt)
        return (1 + n, nxt, bln)
    return lambda tpl: go(*tpl)


# ------------------------- TEST -------------------------
# main :: IO()
def main():
    '''First 15, and first duplicated Recaman.'''
    f = recamanTupleSucc(set([0]))
    print(
        'First 15 Recaman:\n',
        list(map(
            snd,
            take(15)(iterate(f)((1, 0, False)))
        ))
    )
    f = recamanTupleSucc(set([0]))
    print(
        'First duplicated Recaman:\n',
        until(lambda x: x[2])(f)(
            (1, 0, False)
        )[1]
    )

    sk = set(enumFromTo(0)(1000))
    sr = set([0])
    f = recamanTupleSucc(sr)
    print(
        'Number of Recaman terms needed to generate',
        'all integers from [0..1000]:\n',
        until(
            lambda x: not x[2] and 1001 > x[1] and sk.issubset(sr)
        )(f)(
            (1, 0, False)
        )[0] - 1
    )


# ----------------- GENERIC ABSTRACTIONS -----------------

# enumFromTo :: (Int, Int) -> [Int]
def enumFromTo(m):
    '''Integer enumeration from m to n.'''
    return lambda n: range(m, 1 + n)


# iterate :: (a -> a) -> a -> Gen [a]
def iterate(f):
    '''An infinite list of repeated
       applications of f to x.
    '''
    def go(x):
        v = x
        while True:
            yield v
            v = f(v)
    return go


# snd :: (a, b) -> b
def snd(tpl):
    '''Second component of a tuple.'''
    return tpl[1]


# take :: Int -> [a] -> [a]
# take :: Int -> String -> String
def take(n):
    '''The prefix of xs of length n,
       or xs itself if n > length xs.'''
    return lambda xs: (
        xs[0:n]
        if isinstance(xs, list)
        else islice(xs, n)
    )


# until :: (a -> Bool) -> (a -> a) -> a -> a
def until(p):
    '''The result of repeatedly applying f until p holds.
       The initial seed value is x.
    '''
    def go(f):
        def g(x):
            v = x
            while not p(v):
                v = f(v)
            return v
        return g
    return go


# MAIN ---
if __name__ == '__main__':
    main()


  

You may also check:How to resolve the algorithm Gotchas step by step in the MIPS Assembly programming language
You may also check:How to resolve the algorithm Inconsummate numbers in base 10 step by step in the APL programming language
You may also check:How to resolve the algorithm Binary search step by step in the Eiffel programming language
You may also check:How to resolve the algorithm Sum and product puzzle step by step in the Ruby programming language
You may also check:How to resolve the algorithm Rosetta Code/Find bare lang tags step by step in the Mathematica/Wolfram Language programming language