# Odd period square roots (Euler Problem 64)¶

Back to overview.

https://projecteuler.net/problem=64

The first ten continued fraction representations of (irrational) square roots are:

√2=[1;(2)], period=1

√3=[1;(1,2)], period=2

√5=[2;(4)], period=1

√6=[2;(2,4)], period=2

√7=[2;(1,1,1,4)], period=4

√8=[2;(1,4)], period=2

√10=[3;(6)], period=1

√11=[3;(3,6)], period=2

√12= [3;(2,6)], period=2

√13=[3;(1,1,1,1,6)], period=5

Exactly four continued fractions, for N ≤ 13, have an odd period.

How many continued fractions for N ≤ 10000 have an odd period?

In [1]:

    import math

    def get_floor_sqrt(n):
        return math.floor(math.sqrt(n))

    assert(get_floor_sqrt(5) == 2)
    assert(get_floor_sqrt(23) == 4)

In [2]:

    def next_expansion(current_a, current_nominator, current_denominator, original_number):
        # Less typing
        cn = current_nominator
        cd = current_denominator

        # Step 1: Multiply the fraction so that we can use the third binomial formula.
        # Make sure we can reduce the nominator.
        assert((original_number - cd * cd) % cn == 0)
        # The new nominator is the denominator since we multiply with (x + cd) and then
        # reduce the previous nominator.
        # The new denominator is calculated by applying the third binomial formula and
        # then by divided by the previous nominator.
        cn, cd = cd, (original_number - cd * cd) // cn

        # Step 2: Calculate the next a by finding the next floor square root.
        next_a = math.floor((math.sqrt(original_number) + cn) // cd)

        # Step 3: Remove next a from the fraction by substracting it.
        cn = cn - next_a * cd
        cn *= -1

        return next_a, cn, cd

    next_expansion(1, 7, 3, 23)

Out[2]:

    (3, 3, 2)

In [3]:

    def get_continued_fraction_sequence(n):

        # If number is a square number we return it.
        floor_sqrt = get_floor_sqrt(n)
        if n == floor_sqrt * floor_sqrt:
            return ((floor_sqrt), [])

        # Otherwise, we calculate the next expansion till we
        # encounter a step a second time.
        a = floor_sqrt
        cn = a
        cd = 1
        sequence = []
        previous_steps = []

        while not (a, cn, cd) in previous_steps :
            #print("a: {} cn: {} cd: {}".format(a, cn, cd))
            previous_steps.append((a, cn, cd))
            a, cn, cd = next_expansion(a, cd, cn, n)
            sequence.append(a)
        sequence.pop()
        return ((floor_sqrt), sequence)

    assert(get_continued_fraction_sequence(1) == ((1), []))
    assert(get_continued_fraction_sequence(4) == ((2), []))
    assert(get_continued_fraction_sequence(25) == ((5), []))
    assert(get_continued_fraction_sequence(2) == ((1), [2]))
    assert(get_continued_fraction_sequence(3) == ((1), [1,2]))
    assert(get_continued_fraction_sequence(5) == ((2), [4]))
    assert(get_continued_fraction_sequence(13) == ((3), [1,1,1,1,6]))

In [4]:

    def get_period(n):
        _, sequence = get_continued_fraction_sequence(n)
        return len(sequence)

    assert(get_period(23) == 4)

In [5]:

    s = len([n for n in range(1, 10001) if get_period(n) % 2 != 0])
    print(s)
    assert(s == 1322)

    1322

In [ ]: