Получение непрерывных дробей для квадратных корней
Я написал этот код для генерации непрерывной дроби квадратного корня 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
С 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)