Problem 230

completed February 12, 2021

Comments

Very interesting problem. It was a fun one to work on during this snowy day.

The trick was to figure out whether the digit was part of an A or a B. Once we have that information, it is simple to be able to find the digit.

I started by writing out the values of these “Fibonacci words” and noticed that other than the first two letters, the remaining letters were always the same. From here the trick was to figure out which row first houses the letter, then modulo off the first half of the word until the location is either a 1 or a 2.

For example, if we are looking for the 18th letter in the Fibonacci word, we notice that the first row that has more than 18 letters is the 8th row:

1:  A
2:  B
3:  AB
4:  BAB
5:  ABBAB
6:  BABABBAB
7:  ABBABBABABBAB
8:  BABABBABABBABBABABBAB

Next, we chop off everything from the previous row (the 7th) leaving just BABABBAB. The highlighted letter is now the 5th letter in this Fibonacci word.

1:  A
2:  B
3:  AB
4:  BAB
5:  ABBAB

Repeating the process one more time, we find that the highlighted letter is the second letter of the third row. We can now easily deduce that it is the letter B.

Code

A = '1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679'
B = '8214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196'

def memoize(fn):
    _cache = {}
    def wrap(n):
        if n in _cache:
            return _cache[n]
        ret = fn(n)
        _cache[n] = ret
        return ret
    return wrap

@memoize
def fib(n):
    if n < 2:
        return 1
    f1 = f2 = i = 1
    while i < n:
        i += 1
        f1, f2 = f2, f1 + f2
    return f2

def findletter(n):
    f, i = n, 0
    while f > 2:
        i = 0
        while fib(i) < f:
            i += 1
        f %= fib(i-1)
    if f == 1:
        if (i + 1) % 2 == 0:
            return 'B'
        return 'A'
    if (i + 1) % 2 == 1:
        return 'B'
    return 'A'

def solve(n):

    letters = {'A': A, 'B': B}
    lena = len(A)

    def d(n):
        d, m = divmod(n, lena)
        l = findletter(d+(m>0))
        return int(letters[l][(n-1)%lena])

    total, ten, seven = 0, 1, 1
    for i in range(n + 1):
        num = (127 + 19*i) * seven
        total += d(num) * ten
        ten *= 10
        seven *= 7
    return total

if __name__ == '__main__':
    import sys
    n = eval(sys.argv[1])
    print(solve(n))

Tests

import pytest, problem
from problem import solve

_test_solve = (
        (0,            3),
        (1,           93),
        (2,          393),
        (3,         9393),
        (4,        19393),
        (5,       519393),
        (6,      4519393),
        (7,     64519393),
        (8,    364519393),
        (9,   2364519393),
        (10, 12364519393),
)

@pytest.mark.parametrize('n,expect', _test_solve)
def test_solve(n, expect):
    problem.A = '1415926535'
    problem.B = '8979323846'
    assert expect == solve(n)