生成平方根的连续分数

Loe*_*rio 9 c++ math square-root

我写了这个代码用于生成平方根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);
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 18

根本问题是您无法将非正方形的平方根精确表示为浮点数.

如果?是精确值和x近似值(假设仍然非常好,特别是floor(?) = a = floor(x)仍然保持),则继续分数算法的下一步之后的差异是

?' - x' = 1/(? - a) - 1/(x - a) = (x - ?) / ((? - a)*(x - a)) ? (x - ?) / (? - a)^2
Run Code Online (Sandbox Code Playgroud)

因此,我们看到在每一步中,近似值与实际值之间的差值的绝对值增加,因为0 < ? - a < 1.每当出现大的部分商(? - a接近0)时,差异就会增大.一旦(绝对值)差值为1或更大,下一个计算出的部分商保证是错误的,但很可能先发生了第一个错误的部分商.

查尔斯 提到了近似值,通过使用n正确数字的原始近似值,您可以计算n连续分数的部分商.这是一个很好的经验法则,但正如我们所看到的,任何大的部分商都会花费更多的精确度,从而减少可获得的部分商的数量,有时你会更早地得到错误的部分商.

这种情况?139是一个具有相对较长周期且具有几个大的部分商的情况,因此在周期完成之前出现第一个错误计算的部分商并不奇怪(我很惊讶它不会更早发生).

使用浮点运算,没有办法阻止它.

但对于二次方程式的情况,我们可以通过仅使用整数运算来避免这个问题.假设您想要计算持续的分数扩展

? = (?D + P) / Q
Run Code Online (Sandbox Code Playgroud)

在哪里Q划分D - P²并且D > 1不是一个完美的正方形(如果不满足可分性条件,你可以DD*Q²,替换,PP*QQ替换;你的情况是P = 0, Q = 1,在它很容易满足的情况下).写完整的商作为

?_k = (?D + P_k) / Q_k (with ?_0 = ?, P_0 = P, Q_0 = Q)
Run Code Online (Sandbox Code Playgroud)

并表示部分商a_k.然后

?_k - a_k = (?D - (a_k*Q_k - P_k)) / Q_k
Run Code Online (Sandbox Code Playgroud)

和,有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],
Run Code Online (Sandbox Code Playgroud)

所以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)重复.纯平方根的情况特别简单,期间在第一次Q_k = 1为a 时完成k > 0,并且P_k, Q_k总是非负的.

使用时R = floor(?D),部分商可以计算为

a_k = floor((R + P_k) / Q_k)
Run Code Online (Sandbox Code Playgroud)

所以上面算法的代码就变成了

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;
}
Run Code Online (Sandbox Code Playgroud)

这可以很容易地计算出(例如)?7981周期长度为182 的连续分数.

  • 它们的标题(与《数论概论》足够相似)在每本书中或多或少地描述了它们(以不同的深度)。根据我自己的经验,我可以推荐经典的Hardy / Wright,Hua Loo Keng的书,以及-Don Donmond的书,但要注意。(请注意,至少在我阅读的版本中,雷德蒙德的书有很多排版错误,例如,“ 13·27”显示为“ 1327”,10平方显示为102等,这通常很烦人。除此之外,这是一本非常不错的书,并且比其他两本书更广泛地介绍了CF。) (2认同)

Dav*_*men 5

罪魁祸首不是floor。罪魁祸首是计算A= 1.0 / (A - B);深入挖掘,罪魁祸首是计算机用来表示实数的 IEEE 浮点机制。减法和加法会失去精度。当你的算法重复执行时,重复减法会失去精度。

当您计算出连分数项 {11,1,3,1,3,7,1,1,2,11,2} 时,A 的 IEEE 浮点值仅适用于 6 位,而不是人们预计十五或十六。当您到达 {11,1,3,1,3,7,1,1,2,11,2,1,1,7,3,1,3,1} 时,您的 A 值纯粹是垃圾。它已经失去了所有的精确性。