2012-08-29 10 views
6

ho scritto questo codice per generare frazione continua di una radice quadrata N.
Ma fallisce quando N = 139.
L'uscita dovrebbe essere {11,1,3,1,3,7,1,1,2,11,2,1,1,7,3,1,3,1,22}
Mentre il mio codice mi da una sequenza di 394 termini ... di cui i primi termini sono corretti ma quando raggiunge i 22 dà 12!Generazione frazioni continue per radici quadrate

Qualcuno può aiutarmi con questo?

vector <int> f; 
int B;double A; 
A = sqrt(N*1.0); 
B = floor(A); 
f.push_back(B);     
while (B != 2 * f[0])) { 
    A = 1.0/(A - B); 
    B =floor(A);        
    f.push_back(B);  
} 
f.push_back(B); 
+0

Quali sono i tipi di A e B? –

+0

@PaulR A è doppio B è int –

+0

La mia risposta potrebbe essere utile. Genera e stampa la frazione continua per un "doppio" arbitrario. – Mysticial

risposta

11

Il problema di root è che non è possibile rappresentare esattamente la radice quadrata di un non quadrato come un numero in virgola mobile.

Se ξ è il valore esatto e x l'approssimazione (che dovrebbe essere ancora abbastanza buona, in modo che in particolare floor(ξ) = a = floor(x) detiene ancora), quindi la differenza dopo la fase successiva della frazione continua è

ξ' - x' = 1/(ξ - a) - 1/(x - a) = (x - ξ)/((ξ - a)*(x - a)) ≈ (x - ξ)/(ξ - a)^2 

Così vediamo che in ogni passo il valore assoluto della differenza tra l'approssimazione e il valore reale aumenta, dal 0 < ξ - a < 1. Ogni volta che si verifica un grande quoziente parziale (ξ - a è vicino a 0), la differenza aumenta di un fattore elevato. Una volta (il valore assoluto di) la differenza è 1 o maggiore, il successivo quoziente parziale calcolato è garantito errato, ma molto probabilmente il primo quoziente parziale errato si verifica prima.

Charlesmentioned l'approssimazione che, con un'approssimazione originale con n cifre corrette, è possibile calcolare circa n quozienti parziali della frazione continua. Questa è una buona regola empirica, ma come abbiamo visto, qualsiasi grande quoziente parziale costa più precisione e quindi riduce il numero di quozienti parziali ottenibili, e di tanto in tanto si ottengono percentuali parziali errate molto prima.

Il caso di √139 è uno con un periodo relativamente lungo, con un paio di grandi quozienti parziali, quindi non è sorprendente che il primo quoziente parziale erroneamente calcolato appare prima del periodo è stata completata (sono piuttosto sorpreso che doesn' t si verificano prima).

Utilizzando l'aritmetica in virgola mobile, non c'è modo di impedirlo.

Ma per il caso di conti quadrati, possiamo evitare questo problema utilizzando solo l'aritmetica dei numeri interi. Dire che si desidera calcolare l'espansione frazione continua di

ξ = (√D + P)/Q 

dove Q divide D - P² e D > 1 non è un quadrato perfetto (se la condizione divisibilità non è soddisfatta, è possibile sostituire D con D*Q², P con P*Q e Q con ; il tuo caso è P = 0, Q = 1, dove è banalmente soddisfatto). Scrivi i quozienti completi come

ξ_k = (√D + P_k)/Q_k (with ξ_0 = ξ, P_0 = P, Q_0 = Q) 

e indicare i quozienti parziali a_k. Poi

ξ_k - a_k = (√D - (a_k*Q_k - P_k))/Q_k 

e, con P_{k+1} = a_k*Q_k - P_k,

ξ_{k+1} = 1/(ξ_k - a_k) = Q_k/(√D - P_{k+1}) = (√D + P_{k+1})/[(D - P_{k+1}^2)/Q_k], 

così Q_{k+1} = (D - P_{k+1}^2)/Q_k — dal P_{k+1}^2 - P_k^2 è un multiplo di Q_k, per induzione Q_{k+1} é un intero ed Q_{k+1} divide D - P_{k+1}^2.

L'espansione in frazione continua di un numero reale ξ è periodico se e solo se ξ è un irrazionale quadratico, e il periodo è completata quando nell'algoritmo sopra, la prima coppia (P_k, Q_k) ripetizioni. Il caso delle radici quadrate pure è particolarmente semplice, il periodo è completato quando il primo Q_k = 1 per un k > 0 e lo P_k, Q_k sono sempre non negativi.

Con R = floor(√D), i quozienti parziali può essere calcolato come

a_k = floor((R + P_k)/Q_k) 

modo che il codice per l'algoritmo di cui sopra diventa

std::vector<unsigned long> sqrtCF(unsigned long D) { 
    // sqrt(D) may be slightly off for large D. 
    // If large D are expected, a correction for R is needed. 
    unsigned long R = floor(sqrt(D)); 
    std::vector<unsigned long> f; 
    f.push_back(R); 
    if (R*R == D) { 
     // Oops, a square 
     return f; 
    } 
    unsigned long a = R, P = 0, Q = 1; 
    do { 
     P = a*Q - P; 
     Q = (D - P*P)/Q; 
     a = (R + P)/Q; 
     f.push_back(a); 
    }while(Q != 1); 
    return f; 
} 

che calcola facilmente la frazione continua di (ad esempio) √7981 con un periodo lunghezza di 182.

+0

Grazie mille. È stato un grande aiuto. Ma puoi fornirmi un link per la carta o qualcosa che descriva gli algoritmi CF con una spiegazione intensiva –

+1

Vengono descritti (con profondità diverse) in più o meno ogni libro il cui titolo è sufficientemente simile a "Introduzione alla teoria dei numeri". Dalla mia esperienza personale, posso consigliare il classico Hardy/Wright, il libro di Hua Loo Keng e, con un avvertimento, il libro di Don Redmond. (L'avvertenza è che, almeno nell'edizione che ho letto, il libro di Redmond aveva molti errori di composizione, dove ad esempio "13 · 27" appariva come "1327", 10 al quadrato come 102 ecc., Che è spesso irritante. è un libro molto buono e tratta i CF in una larghezza maggiore rispetto agli altri due.) –

+0

Ottima soluzione. Puoi spiegare perché a = (R + P)/Q? Non riesco a capirlo. – Confuse

0

ho usato si Algo in un foglio di calcolo e ottengo 12 così, penso che è necessario aver fatto un errore nella vostra algo, ho provato per 253 valori, e B non ha raggiunto il valore finale di esso.

Puoi provare a spiegare un po 'di più cosa dovrebbe fare l'algo e come funzionerebbe?

Penso di avere il tuo algo e hai fatto un errore nella tua domanda, dovrebbe essere 12. Per riferimento futuro, l'algo può essere trovato in questa pagina http://en.wikipedia.org/wiki/Continued_fraction ed è molto incline a problemi con problemi computazionali decimali/numerici se il valore inverso è molto vicino al numero intero a cui stai cercando di arrotondare.

Quando si esegue il prototipo in Excel, non è stato possibile riprodurre l'esempio della pagina wiki per 3.245, perché ad un certo punto Floor() ha eseguito il flooring del numero su 3 anziché su 4, quindi è necessario un controllo dei limiti per verificare l'accuratezza ...

In questo caso probabilmente si desidera aggiungere un numero massimo di iterazione, una tolleranza per il controllo della condizione di uscita (la condizione di uscita dovrebbe essere che a è uguale a B btw)

3

L'isn colpevole' t floor. Il colpevole è il calcolo A= 1.0/(A - B); Scavando più a fondo, il colpevole è il meccanismo a virgola mobile IEEE che il tuo computer usa per rappresentare numeri reali. La sottrazione e l'aggiunta perdono la precisione. Sottrarre ripetutamente mentre il tuo algoritmo sta facendo ripetutamente perde precisione.

Nel momento in cui sono stati calcolati i termini di frazione continua {11,1,3,1,3,7,1,1,2,11,2}, il valore in virgola mobile IEEE di A vale per solo sei luoghi piuttosto che i quindici o sedici che ci si aspetterebbe. Quando arrivi a {11,1,3,1,3,7,1,1,2,11,2,1,1,7,3,1,3,1} il tuo valore di A è pura spazzatura . Ha perso ogni precisione.

+1

Per una buona prima approssimazione (una coincidenza che funziona solo nella base 10) su n termini di frazione continua possono essere estratti da un numero con n cifre di precisione. Se provi a prenderne di più, otterrai spazzatura, come hai sottolineato. – Charles

1

La funzione sqrt in matematica non è precisa. Puoi usare sympy invece con una precisione arbitrariamente alta.Ecco un codice molto facile calcolare frazioni continue per qualsiasi radice quadrata o numero incluso nel sympy:

from __future__ import division #only needed when working in Python 2.x 
import sympy as sp 

p=sp.N(sp.sqrt(139), 5000) 

n=2000 
x=range(n+1) 
a=range(n) 
x[0]=p 

for i in xrange(n): 
    a[i] = int(x[i]) 
    x[i+1]=1/(x[i]-a[i]) 
    print a[i], 

ho impostato la precisione del vostro numero a 5000 e quindi calcolato 2.000 continuato coefficienti frazione in questo codice di esempio.

+0

Sì, questo algoritmo funziona nel caso generale, ma come mostra la risposta di Daniel Fischer, è possibile determinare l'esatta frazione periodica continua per i numeri quadratici senza ricorrere ad aritmetica di precisione arbitraria. Ovviamente, se si desidera valutare la frazione continua con un'alta precisione, è necessario qualcosa di meglio della doppia precisione standard. A proposito, probabilmente non ha molto senso pubblicare il codice Python su una domanda codificata in C++, OTOH il tuo codice non usa "trucchi" di Python per cui dovrebbe essere abbastanza chiaro per chiunque stia leggendo questa pagina. –

0

Nel caso in cui qualcuno stia tentando di risolvere questo problema in una lingua senza numeri interi, ecco il codice dalla risposta accettata adattata per JavaScript.

Nota due ~~ (operatori di piano) sono stati aggiunti.

export const squareRootContinuedFraction = D =>{ 
    let R = ~~Math.sqrt(D); 
    let f = []; 
    f.push(R); 
    if (R*R === D) { 
     return f; 
    } 
    let a = R, P = 0, Q = 1; 
    do { 
     P = a*Q - P; 
     Q = ~~((D - P *P)/Q); 
     a = ~~((R + P)/Q); 
     f.push(a); 
    } while (Q != 1); 
    return f; 
};