Получение непрерывных дробей для квадратных корней

Я написал этот код для генерации непрерывной дроби квадратного корня N.
Но он терпит неудачу, когда N = 139.
Выход должен быть {11,1,3,1,3,7,1,1,2,11,2,1,1,7,3,1,3,1,22}
В то время как мой код дает мне последовательность 394 условиях... из которых первые несколько терминов верны, но когда он достигает 22, он дает 12!

может кто-нибудь помочь мне с этим?

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);

5 ответов


корень проблемы в том, что вы не можете точно представлять квадратный корень не квадратный, как число с плавающей точкой.

если ξ - это точное значение и x приближение (которое должно быть все еще довольно хорошим, так что в частности floor(ξ) = a = floor(x) все еще держится), то разница после следующего шага алгоритма непрерывной дроби составляет

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

таким образом, мы видим, что на каждом шаге абсолютное значение разности между приближением и реальное значение увеличивается, так как 0 < ξ - a < 1. Каждый раз, когда происходит большой частичный фактор (ξ - a близко к 0), разница увеличивается на большой коэффициент. Как только (абсолютное значение) разность равна 1 или больше, следующий вычисленный частичный фактор гарантированно будет неправильным, но очень вероятно, что первый неправильный частичный фактор произойдет раньше.

Чарльз указано приближение, которое с оригинальным приближением с n исправить цифры, вы можете вычислить о n частичные коэффициенты непрерывной дроби. Это хорошее эмпирическое правило, но, как мы видели, любые большие частичные коэффициенты стоят больше точности и, таким образом, уменьшают число достижимых частичных коэффициентов, и иногда вы получаете неправильные частичные коэффициенты намного раньше.

случае √139 является одним с относительно длинным периодом с парой больших частичных частных, поэтому неудивительно, что первый неправильно вычисленный частичный фактор появляется до завершения периода (я довольно удивлен, что это не происходит раньше).

используя арифметику с плавающей запятой, нет никакого способа предотвратить это.

но для случая квадратичных сурдов мы можем избежать этой проблемы, используя только целочисленную арифметику. Скажем, вы хотите вычислить продолжение расширения фракции

ξ = (√D + P) / Q

здесь Q делит D - P² и D > 1 не является идеальным квадратом (если условие делимости не удовлетворенный, вы можете заменить D С D*Q², P С P*Q и Q С ; ваш случай P = 0, Q = 1, где он чрезвычайно доволен). Напишите полные коэффициенты как

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

и обозначим частичные коэффициенты a_k. Тогда

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

и с 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],

так Q_{k+1} = (D - P_{k+1}^2) / Q_k С P_{k+1}^2 - P_k^2 несколько Q_k, по индукции Q_{k+1} является целым числом и Q_{k+1} делит D - P_{k+1}^2.

продолжение дробного расширения действительного числа ξ периодическая, если и только если ξ является квадратичным surd, и период завершается, когда в приведенном выше алгоритме первая пара (P_k, Q_k) повторяется. Случай чистых квадратных корней особенно прост, период завершен, когда first Q_k = 1 на k > 0 и P_k, Q_k всегда неотрицательны.

с R = floor(√D) частичные коэффициенты могут быть вычислены as

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

таким образом, код для вышеуказанного алгоритма становится

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;
}

который легко вычисляет постоянную часть (например,) √7981 С длиной периода 182.


преступник не floor. Виновник расчета A= 1.0 / (A - B); копая глубже, виновником является механизм с плавающей запятой IEEE, который ваш компьютер использует для представления реальных чисел. Вычитание и сложение теряют точность. Многократное вычитание, как ваш алгоритм делает неоднократно теряет точность.

к тому времени, когда вы рассчитали непрерывные члены дроби {11,1,3,1,3,3,7,1,1,1,1,11,2}, ваше значение с плавающей запятой IEEE хорошо только для шести мест, а не пятнадцать или шестнадцать. К тому времени, когда вы доберетесь до {11,1,3,1,3,3,7,1,1,2,11,2,1,1,7,3,1,3,1}, ваше значение A будет чистым мусором. Она потеряла всякую точность.


функция sqrt в математике не является точной. Вместо этого вы можете использовать sympy со сколь угодно высокой точностью. Вот очень простой код для вычисления непрерывных дробей для любого квадратного корня или числа, включенного в 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],

Я установил точность вашего числа до 5000, а затем рассчитал 2000 коэффициентов непрерывной дроби в этом примере кода.


В случае, если кто-то пытается решить это без чисел, вот код из принятого ответа адаптированы для JavaScript.

Примечание два ~~ (операторы пола) были добавлены.

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;
};

Я использовал вас algo в электронной таблице, и я получаю 12, я думаю, что вы, должно быть, ошиблись в своем algo, я попытался для 253 значений, и B не достиг конечного значения.

можете ли вы попытаться объяснить немного больше, что algo должен делать и как это будет работать ?

Я думаю, что я получил ваш Альго, и вы ошиблись в своем вопросе, это должно быть 12. Для дальнейшего использования algo можно найти на этой странице http://en.wikipedia.org/wiki/Continued_fraction и он очень склонен к проблемам с десятичными / числовыми вычислительными проблемами, если обратное значение очень близко к целому числу, которое вы пытаетесь округлить.

при выполнении прототипа в Excel я не мог воспроизвести пример страницы wiki для 3.245, потому что в какой-то момент Floor() опустил число до 3 вместо 4, поэтому требуется некоторая проверка границ для проверки точности ...

в этом случае вероятно, вы хотите добавить максимальное количество итераций, допуск для проверки условия выхода (условие выхода должно быть, что A равно B btw)