Home > Project Euler, python > Problem Euler #64

Problem Euler #64

23 Gennaio 2013

Project Euler problem #64

All square roots are periodic when written as continued fractions
...
It can be seen that the sequence is repeating.
For conciseness, we use the notation √23 = [4;(1,3,1,8)],
to indicate that the block (1,3,1,8) repeats indefinitely.
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?

Analisi:

Solo grazie a Wikipedia sono giunto alla soluzione.
Facendo riferimento a questo link, e precisamente:
“Sometimes what is desired is finding not the numerical value of a square root,
but rather its continued fraction expansion.
The following iterative algorithm can be used for this purpose
(S is any natural number that is not a perfect square):…”

In pratica il punto di partenza dell’algoritmo è:
m0 = 0
d0 = 1
a0 = la parte intera della radice quadrata del numero analizzato

>>> a0 = int(5**0.5)
>>> a0
2

a0 in sostanza è il primo termine della notazione classica delle frazioni
continue

Poi continuiamo con l’algoritmo secondo le formule:

m1 = d0 * a0 - m0
d1 = (n - m1 **2) / d0
a1 = int((a0 + m1)/ d1)

Nota: solo nel calcolo di ax, utilizziamo sempre a0, ovvero:

m2 = d1 * a1 - m1
d2 = (n - m2 **2) / d1
a2 = int((a0 + m2)/ d2)

es. con n = 114:

m0 = 0
d0 = 1
a0 = 10 (la radice quadrata di 114 = 10.677, quindi la parte intera è 10)
notazione = [10; ...]

avanti…

m1 = d0 * a0 - m0 = 1*10 - 0 = 10
d1 = (n - m1 **2) / d0 = (114 - 100) / 14 = 14
a1 = int((a0 + m1)/ d1) = int((10 + 10)/ 14 = int(20/14) = 1
notazione = [10; 1, ...]

avanti…

m2 = d1 * a1 - m1 = 14*1 - 10 = 4
d2 = (n - m2 **2) / d1 = (114 - 16) / 14 = 7
a2 = int((a0 + m2)/ d2) = int((10 + 4)/ 7 = int(17/7) = 2
notazione = [10; 1, 2, ...]

avanti…

m3 = d2 * a2 - m2 = 7*2 - 4 = 10
d3 = (n - m3 **2) / d2 = (114 - 100) / 7 = 2
a3 = int((a0 + m3)/ d3) = int((10 + 10)/ 2 = int(20/2) = 10
notazione = [10; 1, 2, 10, ...]

avanti…

m4 = d3 * a3 - m3 = 2*10 - 10 = 10
d4 = (n - m4 **2) / d3 = (114 - 100) / 2 = 7
a4 = int((a0 + m4)/ d4) = int((10 + 10)/ 7 = int(20/7) = 2
notazione = [10; 1, 2, 10, 2, ...]

avanti…

m5 = d4 * a4 - m4 = 7*2 - 10 = 4
d5 = (n - m5 **2) / d4 = (114 - 16) / 7 = 14
a5 = int((a0 + m5)/ d5) = int((10 + 4)/ 14 = int(14/14) = 1
notazione = [10; 1, 2, 10, 2, 1, ...]

avanti…

m6 = d5 * a5 - m5 = 14*1 - 4 = 10
d6 = (n - m6 **2) / d5 = (114 - 100) / 14 = 1 ** Bingo: d = 1
a6 = int((a0 + m6)/ d6) = int((10 + 10)/ 1 = int(20/1) = 20 ** Bingo: 2 * a0
notazione = [10; 1, 2, 10, 2, 1, 20, ...]

Qui, per la soluzione del nostro problema, ci fermiamo, poichè le cifre
comincerebbero a ripetersi:

m7 = d6 * a6 - m6 = 1*20 - 10 = 10 **** Uguale ad m1
d7 = (n - m7 **2) / d6 = (114 - 100) / 1 = 14 **** Uguale a d1
a7 = int((a0 + m7)/ d7) = int((10 + 10)/ 14 = int(20/14) = 1 **** Uguale a d1
notazione = [10; (1, 2, 10, 2, 1, 20)]

Quindi, ci interromperemo quando dx = 1 o quando ax = 2 * a0 (grazie Google,
anche se proseguendo in profondità e provando qualche numero, si giungerebbe
a questa soluzione anche senza il nostro immancabile amico).

Ecco il tutto messo insieme, per la risoluzione, con python, dell’odioso problema:

import time

st = time.time()
res = 0
 
for n in range(2, 10000):
    a0 = int(n ** 0.5)
    if a0 * a0 == n: continue # saltato perche' quadrato perfetto
    a, m, d = a0, 0, 1
    period = 0
    while a != 2 * a0 or d != 1: # esco dal ciclo
        m = d * a - m
        d = (n - m * m) / d
        a = int((a0 + m) / d)
        period += 1
    if period % 2 == 1: res += 1
 
print "euler 64: {}\n elapsed time: {}sec".format(res, time.time() - st)

oppure usando una funzione:

import time

st = time.time()
 
def odd_period(n):
    a0 = int(n ** 0.5)
    if a0 * a0 == n: return False # quadrato perfetto
    a, m, d = a0, 0, 1
    period = 0
    while a != 2 * a0 or d != 1:
        m = d * a - m # es. m2 = d1 * a1 - m1
        d = (n - m * m) / d # es. d2 = (n - m2*m2)/d1
        a = int((a0 + m) / d) # es. a2 = int((a0 + m2)/d2) sempre a0!
        period += 1
    if period % 2 == 1: return True

res = len([n for n in xrange(2, 10000) if odd_period(n)])
 
print "euler 64: {}\n elapsed time: {}sec".format(res, time.time() - st)

Quest’ultima è anche più veloce…
0.21sec contro 0.32sec della prima.

Categorie:Project Euler, python Tag:
I commenti sono chiusi.