引入

连分数可以将实数表示为一个收敛的有理数数列的极限。这个数列中的有理数易于计算,而且提供了这个实数的最佳逼近,因而在算法竞赛中常常会用到连分数。除此之外,连分数还和欧几里得算法密切相关,因而可以应用到一系列数论问题中。

关于连分数相关的算法实现

本文会提供一系列的连分数的算法实现,其中部分算法可能无法保证计算中间过程所涉及的整数都在 32 位或 64 位整型变量的取值范围内。对于这种情形,请参考相应的 Python 的实现,或将 C++ 实现中的整型变量替换为 高精度整数类。为突出重点,本文行文过程中的部分代码可能会调用前文实现过的函数而不再重复给出实现。

连分数

连分数(continued fraction)本身只是一种形式记号。

有限连分数

对于数列 ,连分数 表示展开式

连分数有意义,当且仅当对应的展开式有意义。这些 称为连分数的 (term)或 系数(coefficient)。

记号

更一般的连分数允许展开式中的分子不恒为 ,相应的连分数记号也需要修改,这超出了本文的范畴。另外,有些文献中会将第一个逗号「」写作分号「」,这与本文的记号在含义上没有差异。

当然,连分数还可以推广到无穷数列的情形。

无限连分数

对于无穷数列 ,连分数 表示极限

连分数有意义,当且仅当对应的极限有意义。其中, 称为 的第 渐近分数(convergent)或 收敛子,而 称为 的第 余项完全商(complete quotient)。相应地,项 有时也称为第 部分商(partial quotient)。

简单连分数

数论中,主要考虑连分数的项都是整数的情形。

简单连分数

对于连分数 ,如果 是整数, 都是正整数,则称它为 简单连分数(simple continued fraction),也简称 连分数。如果数列 有限,则称为 有限(简单)连分数;否则称为 无限(简单)连分数。而且, 称为它的 整数部分(integer part)。

除非特别说明,本文提到的连分数都指的是简单连分数。可以证明,无限的简单连分数必然是收敛的,而且简单连分数的余项也一定是正的。

连分数有如下基本性质:

性质

设实数 。那么,成立如下性质:

  1. 对于任意 ,有
  2. 对实数 ,有 ,且它的倒数

有限连分数对应的是有理数。每个有理数都有且仅有两种方式可以表示成连分数,长度必然一奇一偶。这两种方式唯一的区别在于最后一项是否为 ,即

这两个连分数称为有理数 连分数表示(continued fraction representation)。其中,末项不为一的称为标准表示,末项为一的称为非标准表示。1

无限连分数对应的是无理数。而且,每个无理数仅有唯一的方式表示为连分数,称为无理数的连分数表示。

连分数表示的求法

要求某个实数 的连分数表示,只需要注意到它的余项 如果不是整数,就一定满足

而且,。因此,可以从 开始递归地计算

这个过程产生的数列 总是唯一确定的,除非某个余项 成为整数。如果出现了 是整数,则说明过程应当终止,可以选择输出相应的标准表示或者非标准表示。

在算法竞赛中,往往处理的都是有理数 的情形。此时,每个余项 都是有理数 ,而且对于 ,因为 ,就总有 。具体计算上述递推关系,可以发现

此时的计算过程实际上是对 辗转相除法。这也说明,对于有理数 ,连分数表示的长度是 的。计算有理数 的复杂度也是 的。

参考实现

给定分数的分子 和分母 ,输出连分数的系数序列

[list2tab]

  • C++

    // Find the continued fraction representation of P/Q.
    auto fraction(int p, int q) {
      std::vector<int> a;
      while (q) {
        a.push_back(p / q);
        std::tie(p, q) = std::make_pair(q, p % q);
      }
      return a;
    }
  • Python

    # Find the continued fraction representation of P/Q.
    def fraction(p, q):
        a = []
        while q:
            a.append(p // q)
            p, q = q, p % q
        return a

渐近分数

在连分数的定义中介绍了渐近分数的概念。实数的渐近分数就是它的连分数表示的渐近分数:在实数 的连分数表示中,只保留前 个项,得到的连分数 就称为实数 的第 个渐近分数。实数 的渐近分数 都是有理数,而且序列 收敛于实数

这些渐近分数趋近于相应的实数,所以可以用于逼近该实数。为此,有必要了解渐近分数的性质。

递推关系

首先,要解决这些渐近分数的计算问题。虽然渐近分数总是在连分数的后面添加一项,但是并不需要每次都重新计算它的值。其实,渐近分数有如下递推关系:

递推公式

对于连分数 ,设它的第 个渐近分数 可以写成分数 。那么,有

递推的起点是(形式)分数

记号

本文将渐近分数 记作 时,总是默认分子 由上面的递推关系给出。下文还要说明,这样总能得到渐近分数的既约表示。

这个递推式说明

介于 之间。

作为渐近分数的递推关系的推论,成立如下的反序定理和倒数定理:

反序定理

设实数 的第 个渐近分数是 ,则相邻两个渐近分数的分子和分母的比值分别为

如果 ,则第一个连分数应当理解为在倒数第二项处截断,即

倒数定理

实数 的渐近分数的倒数是 的渐近分数。

利用本节得到的递推关系,可以得到计算渐近分数的算法如下:

参考实现

给定连分数的系数 ,求渐近分数的分子和分母序列

[list2tab]

  • C++

    // Find the convergents of a continued fraction A.
    // Numerators and denominators stored separately in P and Q.
    auto convergents(std::vector<int> a) {
      std::vector<int> p = {0, 1};
      std::vector<int> q = {1, 0};
      for (auto it : a) {
        p.push_back(p.back() * it + p.end()[-2]);
        q.push_back(q.back() * it + q.end()[-2]);
      }
      return std::make_pair(p, q);
    }
  • Python

    # Find the convergents of a continued fraction A.
    # Numerators and denominators stored separately in P and Q.
    def convergents(a):
        p = [0, 1]
        q = [1, 0]
        for it in a:
            p.append(p[-1] * it + p[-2])
            q.append(q[-1] * it + q[-2])
        return p, q

误差估计

利用渐近分数的递推公式,可以估计用渐近分数逼近实数产生的误差。

首先,可以计算相邻的渐近分数的差值:

渐近分数的差分

是实数 的第 个渐近分数。那么,有

因此,相邻两项的渐近分数的差分是

因而,奇数项渐近分数总是大于相邻两项,偶数项渐近分数总是小于相邻两项:渐近分数是交错变化的。

如果只看偶数项(奇数项)渐近分数,序列也是单调递增(递减)的。这是因为

为偶数(奇数)时为正(负)。同时,因为成立递推关系 ,分母 的增长速度不会慢于斐波那契数列的速度。所以,相邻两项的差一定趋近于零。这就说明,偶数项和奇数项渐近分数分别自下而上和自上而下地逼近同一极限。这就证明了无限简单连分数一定收敛。渐近分数趋近于相应实数的动态可以见下图:

上(下)渐近分数

对于实数 和它的渐近分数 ,如果 ),就称 上(下)渐近分数(upper (lower) convergent)。

前面已经说明,上渐近分数就是奇数项渐近分数,下渐近分数就是偶数项渐近分数。

利用差分公式,可以将实数 写成交错级数的形式:

连分数定义中的渐近分数和余项就是该级数的部分和和余项。

利用差分公式,还可以直接对渐近分数逼近实数产生的误差做出估计:

误差

是实数 的第 个渐近分数。那么,有

其中, 是实数 的第 个余项。进而,有

本节的差分公式还有一个简单推论:渐近分数 都是既约的。

推论

对于任何实数 ,且渐近分数 的分子和分母由递推公式给出,则 是既约分数,即

其实,二元一次不定方程的解可以通过连分数的方法求解。

二元一次不定方程的求解

给定 。查找 ,使得 成立。

丢番图逼近

连分数理论的一个重要应用就是丢番图逼近理论。丢番图逼近(Diophantine approximation)是指用有理数逼近实数。当然,由于有理数的稠密性,如果不加以限制,可以得到误差任意小的逼近。因此,需要对可以使用的有理数做出限制,比如只能选择分母小于某个值的有理数。本节就讨论了这种限制下的最佳逼近和连分数的关系。

用渐近分数逼近实数

首先,利用渐近分数的误差估计,立刻得到如下结果:

定理(Dirichlet)

对于无理数 ,存在无穷多个既约分数 使得

成立。

这个定理也可以看作是 Dirichlet 逼近定理 的推论。这几乎已经是最好的结果了。不等式右侧分母中的指数 已经不能再改进,但是常数上可以做得更好。Hurwitz 定理说明,不等式右侧可以缩小到 ,且这是最好的界。

Hurwitz 定理

对于无理数 ,存在无穷多个既约分数 使得

成立,而且不等式右侧的 不能换成更大的实数。

这些定理的证明说明,渐近分数提供了相当好的丢番图逼近。但是,这未必是最佳逼近。要讨论最佳逼近,需要说明逼近程度的度量。这常常有两种选择。

可能存在最佳逼近相关结论不成立的情形

接下来的两节,会叙述一些关于最佳逼近的结果。这些结果可能对个别无趣的情形并不成立。比如,最佳逼近的两类定义都要求严格不等号,但是对于半奇数 ,它的连分数的形式可以是 。此时,它的前两个渐近分数 的分母都是 ,而且到 的距离是一样的。这说明,它们都不是最佳逼近。对于本节的结论的叙述,读者应当默认这样的情形已经排除在外。如果读者不关心最末尾的几个渐近分数,抑或是只关心无理数的逼近,那么不必理会这些额外的复杂情形。

第一类最佳逼近:中间分数

第一类最佳逼近使用

衡量逼近的程度。

第一类最佳逼近

对于实数 和有理数 ,如果对于任意的 都有

就称有理数 是实数 第一类最佳逼近(best approximation of the first kind)。

第一类最佳逼近未必是渐近分数,而是一类更宽泛的分数。

中间分数

设实数 有渐近分数 ,且整数 满足 4,则分数 称为 中间分数(intermediate fraction)、半收敛子(semiconvergent)或 次渐近分数(secondary convergent)。5

类似于渐近分数的情形,大于(小于) 的中间分数称为 上(下)中间分数(upper (lower) semiconvergent)。

根据递推公式,中间分数可以写成

它必然是既约分数,而且位于渐近分数 之间。随着 增大,它也逐渐向 靠拢:(以 为偶数的情形为例)

因为渐近分数的分子和分母都是递增的,中间分数 )的分子和分母落在了 之间。如果将这些分数按照分母大小排列,中间分数就是位于相邻的渐近分数中间的一些分数。

所有的第一类最佳逼近都是中间分数,但是并不是所有的中间分数都是第一类最佳逼近。

定理

所有的第一类最佳逼近都是中间分数。

反过来,并不能断言所有的中间分数都是第一类最佳逼近。但是,的确可以给出中间分数成为第一类最佳逼近的条件。

定理

所有渐近分数都是第一类最佳逼近。除此之外,设 ,则中间分数 是第一类最佳逼近,当且仅当 或者

所以,如果将实数 的所有第一类最佳逼近按照分母自小到大的顺序排列,那么它会根据与 的大小关系分成若干段。每一段总是由若干个(可以是零个)连续的同阶的中间分数组成,且总以渐近分数结尾。段内总能保持在实数 的一侧,段与段之间则交错排列在 两侧。

第二类最佳逼近

第二类最佳逼近使用 来衡量逼近的程度。

第二类最佳逼近

对于实数 和有理数 ,如果对于任意的 都有

就称有理数 是实数 第二类最佳逼近(best approximation of the second kind)。

第二类最佳逼近的条件等价于

因为 ,所以第二类最佳逼近的条件比第一类最佳逼近更为严苛。

第二类最佳逼近能且仅能是渐近分数。

定理

所有的第二类最佳逼近一定是渐近分数,所有的渐近分数也一定是第二类最佳逼近。

这个性质表明,渐近分数确实是相当好的丢番图逼近。

渐近分数的判定

第二类最佳逼近提供了判断某个分数是否是渐近分数的充分必要条件。这说明,可以通过检查某个分数逼近的相对程度来判断它是否是渐近分数。Legendre 判别法则提供了根据逼近的绝对程度来判断渐近分数的方法。Legendre 判别法的原始表述提供了充分必要条件,但是它的形式并不实用。本节提供了 Legendre 判别法的简化版本,并说明它并没有漏掉太多的渐近分数。

定理(Legendre)

对于实数 与分数 ,如果有

那么 一定是 的渐近分数。

这个判别法说明,只要逼近的程度足够好,就一定是渐近分数。下一个定理说明,这样好的渐近分数足够多:至少有一半的渐近分数都符合这个条件。

定理(Valhen)

实数 的相邻两个渐近分数中至少有一个满足

几何解释

连分数理论有着优美的几何解释。

如图所示,对于实数 ,直线 将第一象限(包括 坐标轴上的点但不包括原点,下同)上的整点(lattice point)分成上下两部分。对于有理数 的情形,直线 上的点既算作直线上方的点,又算作直线下方的点。考虑这两部分的点的凸包。那么,奇数项渐近分数是上半部分的凸包的顶点,偶数项渐近分数是下半部分的凸包的顶点。凸包上两个相邻顶点之间的连线上的整点就是中间分数。图中展示了 的渐近分数和中间分数(灰点)。

前文关于连分数的大部分结论都有相应的几何解释:

这样得到的上下两个凸包称为 Klein 多边形。在高维空间内也可以做类似定义,得到 Klein 多面体(Klein polyhedron),它可以将连分数的概念推广到高维空间内。

连分数的树

主条目:Stern–Brocot 树与 Farey 序列

Stern–Brocot 树是存储了所有位于 之间的分数的 [二叉搜索树](https://leetcode.com/problems/二叉搜索树 & 平衡树/)。有限连分数实际上编码了 Stern–Brocot 树上从根到某个分数所在位置的路径。也就是说,有理数 的连分数表示 意味着从树根 开始,需要先向右子节点移动 次,再向左子节点移动 次,交替方向移动,直到向某个方向移动了 次为止。应当注意,此处只能使用末尾为 的连分数表示。

将连分数表示理解为 Stern–Brocot 树上的路径,可以得到比较连分数大小的算法。

连分数大小比较

给定连分数 ,比较两者大小。

想要了解更多 Stern–Brocot 树的性质和应用,可以参考其主条目页面。

分式线性变换

和连分数相关的另一个重要概念是所谓的分式线性变换。

分式线性变换

分式线性变换(fractional linear transformation)是指函数 ,使得

其中,

关于条件

容易验证,当 时,函数可能没有定义或者是常函数。

分式线性变换有如下性质:

分式线性变换的性质

是分式线性变换,并记 的系数形成的矩阵为

则它们有如下性质:7

  1. 分式线性变换的复合 和逆变换 仍然是分式线性变换,即全体分式线性变换构成
  2. 分式线性变换在系数同乘以非零常数后保持不变,即对于任意 ,如果 ,则
  3. 分式线性变换的复合的系数矩阵,对应着系数矩阵的乘积,即如果 ,则
  4. 分式线性变换的逆变换的系数矩阵,对应着系数矩阵的逆矩阵,即如果 ,则

有限连分数 可以看做是一系列分式线性变换复合的结果。设

那么,有限连分数

其中,分式线性变换 变换在 处的取值是 ,这是函数在 时的极限值。

对于一般的连分数,设实数 的余项为 ,即 ,则有

这同时也给出了分式线性变换 的形式。

当然也可以直接验证这个表达式。最开始的时候是

随后,如果 具有

的形式,那么根据分式线性变换的复合公式,有

这就可以归纳地得到了上述形式。分式线性变换也提供了递推公式和初值条件的另一个角度的理解。

给定正整数数组 组查询,每次查询给定 ,并要求计算 的值。

连分数的四则运算

利用分式线性变换,可以完成连分数的四则运算。这个算法最早由 Gosper 提出。

算法的基石是计算连分数的分式线性变换。本节以有限连分数为例,但是因为算法每输出一位,只需要读入有限多个连分数的项,所以对于无限连分数也是适用的,而且可以算到任意精度。结合前文的连分数比较算法,可以精确地比较任意精度的实数差异。

连分数的分式线性变换

给定分式线性变换 和连分数 ,求 的连分数表示

连分数的分式线性变换已经可以用于计算分数和连分数的四则运算问题:

对于一般的连分数之间的四则运算,需要用到双分式线性变换:

连分数的双分式线性变换

给定双分式线性变换 和连分数 ,求 的连分数表示

循环连分数

类似于循环小数的概念,如果连分数的系数形成了循环,就称为循环连分数。

循环连分数

设连分数 ,且存在自然数 和正整数 使得对于任何 ,都有 ,就称连分数 循环连分数(periodic continued fraction)。满足这个条件的最小的 称为它的最小正周期,而在连分数中重复出现的 序列就称为它的循环节。利用循环节,循环连分数可以写作 。如果 可以取作 ,即 ,就称它为 纯循环连分数(purely periodic continued fraction),否则称它为 混循环连分数(eventually periodic continued fraction)。

二次无理数

与循环连分数密切相关的概念是 (实)二次无理数(quadratic irrational),即整系数二次方程的无理数解。所有的二次无理数都可以表示成

的形式,其中, 是有理数且 是无平方因子的正整数。本文提到的二次无理数都默认是实数。而且, 的共轭是指

Euler 的结果说明,所有循环连分数都是二次无理数。

定理(Euler)

循环连分数表示的都是二次无理数。

Lagrange 的结果说明反过来也成立,因而二次无理数和循环连分数是等价的。

定理(Lagrange)

二次无理数可以表示成循环连分数。

定理的证明也提供了一个计算二次无理数的余项的递推公式:

二次无理数的余项递推公式

二次无理数总可以表示成

的形式,且 。它的余项

中, 都是整数,且满足递推关系

这个递推公式可以直接用于二次无理数的连分数的计算,而且根据定理的证明,。该算法的复杂度取决于循环节的长度,而后者可以证明是 8

二次无理数

给定二次无理数 ,求出其连分数的表示。其中, 不是完全平方。

纯循环连分数

二次无理数是有循环连分数表示的充分必要条件,本节的讨论则给出了实数具有纯循环连分数表示的充分必要条件。

首先,因为纯循环连分数具有类似有限连分数的形式,所以可以做「反序」操作。类似于反序定理,这样得到的连分数表示和原来的连分数表示之间有确定的关系。

定理(Galois)

对于纯循环连分数

互为「倒数负共轭」,即 的共轭的倒数的相反数是

Galois 利用这个观察,进一步地给出了二次无理数有纯循环连分数表示的充分必要条件。

定理(Galois)

二次无理数 可以表示为纯循环连分数,当且仅当 且它的共轭

Galois 定理揭示了纯二次不尽根(pure quadratic surd)——即形如 的二次无理数——的连分数表示的规律。

推论

对于有理数 ,如果 是无理数,那么

且对于任意 ,都有

二次无理数 的连分数展开主要应用在 Pell 方程 的求解中。

例题

在掌握了基础概念后,需要研究一些具体的例题来理解如何在算法竞赛中应用连分数的方法。

线下凸包

给定 ,求出满足 的整点 的集合的凸包。

习题

参考文献与拓展阅读

本页面主要内容译自博文 Continued fractions,版权协议为 CC-BY-SA 4.0,内容有改动。

Footnotes

  1. 自然数 只有非标准表示:

  2. 译名来自张明尧、张凡翻译的《具体数学》第 6.7 节。

  3. 此时不能默认既约分数 一定是渐近分数,虽然 Legendre 定理表明 确实只能是某个渐近分数。对于渐近分数的情形,可以通过渐近分数逼近实数的误差入手加以证明。

  4. 不同文献可能对此处的 的取值范围是否包括端点有不同的处理。

  5. 时,应理解为形式连分数,相当于截断到连分数的倒数第二项。

  6. 此说法并非专业术语。可能转译自俄文文献 ЦЕПНЫЕ ДРОБИ,在 Алгоритм «вытягивания носов» 一节。

  7. 这些性质表明,全体分式线性变换的群同构于 射影线性群

  8. 证明见 维基百科页面 的参考文献。

  9. 关于自然对数的底 的连分数展开的证明可以参考 此处