Binary recurrence sequences - Combinatorics

Skip to content
Back to top
View this page
Edit this page

# Binary recurrence sequences

This class implements several methods relating to general linear binary recurrence sequences, including a sieve to find perfect powers in integral linear binary recurrence sequences.

EXAMPLES:

Sage

    sage: R = BinaryRecurrenceSequence(1,1)        #the Fibonacci sequence
    sage: R(137)        #the 137th term of the Fibonacci sequence
    19134702400093278081449423917
    sage: R(137) == fibonacci(137)
    True
    sage: [R(i) % 4 for i in range(12)]
    [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
    sage: R.period(4)        #the period of the fibonacci sequence modulo 4
    6
    sage: R.pthpowers(2, 10**10)        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06]
    [0, 1, 2, 12]

    sage: S = BinaryRecurrenceSequence(8,1)  #a Lucas sequence
    sage: S.period(73)
    148
    sage: S(5) % 73 == S(5 +148) %73
    True
    sage: S.pthpowers(3, 10**10)    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10
    [0, 1, 2]

    sage: T = BinaryRecurrenceSequence(2,0,1,2)
    sage: [T(i) for i in range(10)]
    [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
    sage: T.is_degenerate()
    True
    sage: T.is_geometric()
    True
    sage: T.pthpowers(7, 10**30)                                                        # needs sage.symbolic
    Traceback (most recent call last):
    ...
    ValueError: the degenerate binary recurrence sequence is geometric or quasigeometric
    and has many pth powers

Python

    >>> from sage.all import *
    >>> R = BinaryRecurrenceSequence(Integer(1),Integer(1))        #the Fibonacci sequence
    >>> R(Integer(137))        #the 137th term of the Fibonacci sequence
    19134702400093278081449423917
    >>> R(Integer(137)) == fibonacci(Integer(137))
    True
    >>> [R(i) % Integer(4) for i in range(Integer(12))]
    [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
    >>> R.period(Integer(4))        #the period of the fibonacci sequence modulo 4
    6
    >>> R.pthpowers(Integer(2), Integer(10)**Integer(10))        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS06]
    [0, 1, 2, 12]

    >>> S = BinaryRecurrenceSequence(Integer(8),Integer(1))  #a Lucas sequence
    >>> S.period(Integer(73))
    148
    >>> S(Integer(5)) % Integer(73) == S(Integer(5) +Integer(148)) %Integer(73)
    True
    >>> S.pthpowers(Integer(3), Integer(10)**Integer(10))    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10
    [0, 1, 2]

    >>> T = BinaryRecurrenceSequence(Integer(2),Integer(0),Integer(1),Integer(2))
    >>> [T(i) for i in range(Integer(10))]
    [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
    >>> T.is_degenerate()
    True
    >>> T.is_geometric()
    True
    >>> T.pthpowers(Integer(7), Integer(10)**Integer(30))                                                        # needs sage.symbolic
    Traceback (most recent call last):
    ...
    ValueError: the degenerate binary recurrence sequence is geometric or quasigeometric
    and has many pth powers

AUTHORS:

- Isabel Vogt (2013): initial version

See [SV2013], [BMS2006], and [SS1983].

class sage.combinat.binary_recurrence_sequences.BinaryRecurrenceSequence(b, c, u0=0, u1=1)

  Bases: `SageObject`

  Create a linear binary recurrence sequence defined by initial conditions \(u_0\) and \(u_1\) and recurrence relation \(u_{n+2} = b*u_{n+1}+c*u_n\).

  INPUT:

  - `b` – integer; (partially determining the recurrence relation)

  - `c` – integer; (partially determining the recurrence relation)

  - `u0` – integer; (the \(0\)-th term of the binary recurrence sequence)

  - `u1` – integer; (the \(1\)-st term of the binary recurrence sequence)

  OUTPUT: an integral linear binary recurrence sequence defined by \(u_0\), \(u_1\), and \(u_{n+2} = b u_{n+1}+c u_n\)

  See also

  `fibonacci()`, `lucas_number1()`, `lucas_number2()`

  EXAMPLES:

  Sage

      sage: R = BinaryRecurrenceSequence(3,3,2,1)
      sage: R
      Binary recurrence sequence defined by: u_n = 3 * u_{n-1} + 3 * u_{n-2};
      With initial conditions: u_0 = 2, and u_1 = 1

  Python

      >>> from sage.all import *
      >>> R = BinaryRecurrenceSequence(Integer(3),Integer(3),Integer(2),Integer(1))
      >>> R
      Binary recurrence sequence defined by: u_n = 3 * u_{n-1} + 3 * u_{n-2};
      With initial conditions: u_0 = 2, and u_1 = 1

  is_arithmetic()

    Decide whether the sequence is degenerate and an arithmetic sequence.

    The sequence is arithmetic if and only if \(u_1 - u_0 = u_2 - u_1 = u_3 - u_2\).

    This corresponds to the matrix \(F = [[0,1],[c,b]]\) being nondiagonalizable and \(\alpha/\beta = 1\).

    EXAMPLES:

    Sage

        sage: S = BinaryRecurrenceSequence(2,-1)
        sage: [S(i) for i in range(10)]
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        sage: S.is_arithmetic()
        True

    Python

        >>> from sage.all import *
        >>> S = BinaryRecurrenceSequence(Integer(2),-Integer(1))
        >>> [S(i) for i in range(Integer(10))]
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        >>> S.is_arithmetic()
        True

  is_degenerate()

    Decide whether the binary recurrence sequence is degenerate.

    Let \(\alpha\) and \(\beta\) denote the roots of the characteristic polynomial \(p(x) = x^2-bx -c\). Let \(a = u_1-u_0\beta/(\beta - \alpha)\) and \(b = u_1-u_0\alpha/(\beta - \alpha)\). The sequence is, thus, given by \(u_n = a \alpha^n - b\beta^n\). Then we say that the sequence is nondegenerate if and only if \(a*b*\alpha*\beta \neq 0\) and \(\alpha/\beta\) is not a root of unity.

    More concretely, there are 4 classes of degeneracy, that can all be formulated in terms of the matrix \(F = [[0,1], [c, b]]\).

    - \(F\) is singular – this corresponds to `c` = 0, and thus \(\alpha*\beta = 0\). This sequence is geometric after term `u0` and so we call it `quasigeometric`

    - \(v = [[u_0], [u_1]]\) is an eigenvector of \(F\) – this corresponds to a `geometric` sequence with \(a*b = 0\)

    - \(F\) is nondiagonalizable – this corresponds to \(\alpha = \beta\). This sequence will be the point-wise product of an arithmetic and geometric sequence.

    - \(F^k\) is scalar, for some \(k>1\) – this corresponds to \(\alpha/\beta\) a \(k\) th root of unity. This sequence is a union of several geometric sequences, and so we again call it `quasigeometric`.

    EXAMPLES:

    Sage

        sage: S = BinaryRecurrenceSequence(0,1)
        sage: S.is_degenerate()
        True
        sage: S.is_geometric()
        False
        sage: S.is_quasigeometric()
        True

        sage: R = BinaryRecurrenceSequence(3,-2)
        sage: R.is_degenerate()
        False

        sage: T = BinaryRecurrenceSequence(2,-1)
        sage: T.is_degenerate()
        True
        sage: T.is_arithmetic()
        True

    Python

        >>> from sage.all import *
        >>> S = BinaryRecurrenceSequence(Integer(0),Integer(1))
        >>> S.is_degenerate()
        True
        >>> S.is_geometric()
        False
        >>> S.is_quasigeometric()
        True

        >>> R = BinaryRecurrenceSequence(Integer(3),-Integer(2))
        >>> R.is_degenerate()
        False

        >>> T = BinaryRecurrenceSequence(Integer(2),-Integer(1))
        >>> T.is_degenerate()
        True
        >>> T.is_arithmetic()
        True

  is_geometric()

    Decide whether the binary recurrence sequence is geometric - ie a geometric sequence.

    This is a subcase of a degenerate binary recurrence sequence, for which \(ab=0\), i.e. \(u_{n}/u_{n-1}=r\) for some value of \(r\).

    See `is_degenerate()` for a description of degeneracy and definitions of \(a\) and \(b\).

    EXAMPLES:

    Sage

        sage: S = BinaryRecurrenceSequence(2,0,1,2)
        sage: [S(i) for i in range(10)]
        [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
        sage: S.is_geometric()
        True

    Python

        >>> from sage.all import *
        >>> S = BinaryRecurrenceSequence(Integer(2),Integer(0),Integer(1),Integer(2))
        >>> [S(i) for i in range(Integer(10))]
        [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
        >>> S.is_geometric()
        True

  is_quasigeometric()

    Decide whether the binary recurrence sequence is degenerate and similar to a geometric sequence, i.e. the union of multiple geometric sequences, or geometric after term `u0`.

    If \(\alpha/\beta\) is a \(k\) th root of unity, where \(k>1\), then necessarily \(k = 2, 3, 4, 6\). Then \(F = [[0,1],[c,b]\) is diagonalizable, and \(F^k = [[\alpha^k, 0], [0,\beta^k]]\) is a diagonal matrix. Thus for all values of \(j\) mod \(k\), the \(j\) mod \(k\) terms of \(u_n\) form a geometric series.

    If \(\alpha\) or \(\beta\) is zero, this implies that \(c=0\). This is the case when \(F\) is singular. In this case, \(u_1, u_2, u_3, ...\) is geometric.

    EXAMPLES:

    Sage

        sage: S = BinaryRecurrenceSequence(0,1)
        sage: [S(i) for i in range(10)]
        [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
        sage: S.is_quasigeometric()
        True

        sage: R = BinaryRecurrenceSequence(3,0)
        sage: [R(i) for i in range(10)]
        [0, 1, 3, 9, 27, 81, 243, 729, 2187, 6561]
        sage: R.is_quasigeometric()
        True

    Python

        >>> from sage.all import *
        >>> S = BinaryRecurrenceSequence(Integer(0),Integer(1))
        >>> [S(i) for i in range(Integer(10))]
        [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
        >>> S.is_quasigeometric()
        True

        >>> R = BinaryRecurrenceSequence(Integer(3),Integer(0))
        >>> [R(i) for i in range(Integer(10))]
        [0, 1, 3, 9, 27, 81, 243, 729, 2187, 6561]
        >>> R.is_quasigeometric()
        True

  period(m, eventual)

    Return the period of the binary recurrence sequence modulo an integer `m`.

    If \(n_1\) is congruent to \(n_2\) modulo `period(m)`, then \(u_{n_1}\) is is congruent to \(u_{n_2}\) modulo `m`.

    INPUT:

    - `m` – integer; modulo which the period of the recurrence relation is calculated

    - `eventual` – boolean (default: `False`); if `True`, allow the sequence to be eventually periodic, rather than requiring it to be purely periodic. So \(n_1\) might not be congruent to \(n_2\) modulo `period(m)` unless \(n_1\) and \(n_2\) are large.

    OUTPUT: integer (the period of the sequence modulo m)

    EXAMPLES:

    If \(p = \pm 1 \mod 5\), then the period of the Fibonacci sequence mod \(p\) is \(p-1\) (c.f. Lemma 3.3 of [BMS2006]).

    Sage

        sage: R = BinaryRecurrenceSequence(1,1)
        sage: R.period(31)
        30

        sage: [R(i) % 4 for i in range(12)]
        [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
        sage: R.period(4)
        6

    Python

        >>> from sage.all import *
        >>> R = BinaryRecurrenceSequence(Integer(1),Integer(1))
        >>> R.period(Integer(31))
        30

        >>> [R(i) % Integer(4) for i in range(Integer(12))]
        [0, 1, 1, 2, 3, 1, 0, 1, 1, 2, 3, 1]
        >>> R.period(Integer(4))
        6

    This function works for degenerate sequences as well.

    Sage

        sage: S = BinaryRecurrenceSequence(2,0,1,2)
        sage: S.is_degenerate()
        True
        sage: S.is_geometric()
        True
        sage: [S(i) % 17 for i in range(16)]
        [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
        sage: S.period(17)
        8

    [Python]

        >>> from sage.all import *
        >>> S = BinaryRecurrenceSequence(Integer(2),Integer(0),Integer(1),Integer(2))
        >>> S.is_degenerate()
        True
        >>> S.is_geometric()
        True
        >>> [S(i) % Integer(17) for i in range(Integer(16))]
        [1, 2, 4, 8, 16, 15, 13, 9, 1, 2, 4, 8, 16, 15, 13, 9]
        >>> S.period(Integer(17))
        8

    Letting `eventual` be `True` allows us to find the period of a sequence that is not purely periodic.

    Sage

        sage: T = BinaryRecurrenceSequence(5,12,u0=0,u1=1)
        sage: [T(n) % 10 for n in range(20)]
        [0, 1, 5, 7, 5, 9, 5, 3, 5, 1, 5, 7, 5, 9, 5, 3, 5, 1, 5, 7]
        sage: T.period(10)
        Traceback (most recent call last):
        ...
        ValueError: Binary recurrence sequence modulo 10 is not a purely
        periodic sequence.
        sage: T.period(10,eventual=True)
        8

    Python

        >>> from sage.all import *
        >>> T = BinaryRecurrenceSequence(Integer(5),Integer(12),u0=Integer(0),u1=Integer(1))
        >>> [T(n) % Integer(10) for n in range(Integer(20))]
        [0, 1, 5, 7, 5, 9, 5, 3, 5, 1, 5, 7, 5, 9, 5, 3, 5, 1, 5, 7]
        >>> T.period(Integer(10))
        Traceback (most recent call last):
        ...
        ValueError: Binary recurrence sequence modulo 10 is not a purely
        periodic sequence.
        >>> T.period(Integer(10),eventual=True)
        8

    Note

    The answer is cached.

  pthpowers(p, Bound)

    Find the indices of proveably all \(p\)-th powers in the recurrence sequence bounded by Bound.

    Let \(u_n\) be a binary recurrence sequence. A `p` th power in \(u_n\) is a solution to \(u_n = y^p\) for some integer \(y\). There are only finitely many `p` th powers in any recurrence sequence [SS1983].

    INPUT:

    - `p` – a rational prime integer (the fixed p in \(u_n = y^p\))

    - `Bound` – a natural number (the maximum index \(n\) in \(u_n = y^p\) that is checked)

    OUTPUT:

    A list of the indices of all `p` th powers less bounded by `Bound`. If the sequence is degenerate and there are many `p` th powers, raises `ValueError`.

    EXAMPLES:

    Sage

        sage: R = BinaryRecurrenceSequence(1,1)        #the Fibonacci sequence
        sage: R.pthpowers(2, 10**10)        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS2006]_
        [0, 1, 2, 12]

        sage: S = BinaryRecurrenceSequence(8,1) #a Lucas sequence
        sage: S.pthpowers(3,10**10)    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10
        [0, 1, 2]

        sage: Q = BinaryRecurrenceSequence(3,3,2,1)
        sage: Q.pthpowers(11,10**10)          # long time (7.5 seconds)
        [1]

    Python

        >>> from sage.all import *
        >>> R = BinaryRecurrenceSequence(Integer(1),Integer(1))        #the Fibonacci sequence
        >>> R.pthpowers(Integer(2), Integer(10)**Integer(10))        # long time (7 seconds) -- in fact these are all squares, c.f. [BMS2006]_
        [0, 1, 2, 12]

        >>> S = BinaryRecurrenceSequence(Integer(8),Integer(1)) #a Lucas sequence
        >>> S.pthpowers(Integer(3),Integer(10)**Integer(10))    # long time (3 seconds) -- provably finds the indices of all 3rd powers less than 10^10
        [0, 1, 2]

        >>> Q = BinaryRecurrenceSequence(Integer(3),Integer(3),Integer(2),Integer(1))
        >>> Q.pthpowers(Integer(11),Integer(10)**Integer(10))          # long time (7.5 seconds)
        [1]

    If the sequence is degenerate, and there are no `p` th powers, returns \([]\). Otherwise, if there are many `p` th powers, raises `ValueError`.

    Sage

        sage: T = BinaryRecurrenceSequence(2,0,1,2)
        sage: T.is_degenerate()
        True
        sage: T.is_geometric()
        True
        sage: T.pthpowers(7, 10**30)                                                # needs sage.symbolic
        Traceback (most recent call last):
        ...
        ValueError: the degenerate binary recurrence sequence is geometric or
        quasigeometric and has many pth powers

        sage: L = BinaryRecurrenceSequence(4,0,2,2)
        sage: [L(i).factor() for i in range(10)]
        [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17]
        sage: L.is_quasigeometric()
        True
        sage: L.pthpowers(2, 10**30)                                                # needs sage.symbolic
        []

    [Python]

        >>> from sage.all import *
        >>> T = BinaryRecurrenceSequence(Integer(2),Integer(0),Integer(1),Integer(2))
        >>> T.is_degenerate()
        True
        >>> T.is_geometric()
        True
        >>> T.pthpowers(Integer(7), Integer(10)**Integer(30))                                                # needs sage.symbolic
        Traceback (most recent call last):
        ...
        ValueError: the degenerate binary recurrence sequence is geometric or
        quasigeometric and has many pth powers

        >>> L = BinaryRecurrenceSequence(Integer(4),Integer(0),Integer(2),Integer(2))
        >>> [L(i).factor() for i in range(Integer(10))]
        [2, 2, 2^3, 2^5, 2^7, 2^9, 2^11, 2^13, 2^15, 2^17]
        >>> L.is_quasigeometric()
        True
        >>> L.pthpowers(Integer(2), Integer(10)**Integer(30))                                                # needs sage.symbolic
        []

    Note

    This function is primarily optimized in the range where `Bound` is much larger than `p`.