Files
SolutionEuler/solutions/0057.SquareRoot/readme.md
Sidney Zhang d156541f31 feat(solutions/0057): 添加欧拉计划第57题连分数平方根解法
包含正算递推实现、约分优化及详细数学推导文档
2026-09-16 11:30:19 +08:00

337 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

方法1:倒算
- N = a_n ; D = 1
- k:(n-1 → 0) => N,D = a_k*N+D ,N
- 最后: gcd(N,D) 约分
方法2:正算
正算如何成立呢?这个我还没有很好的想法。
```
p2, p1 = 0, 1 # p_{-2}, p_{-1}
q2, q1 = 1, 0 # q_{-2}, q_{-1}
for a in cf:
p = a * p1 + p2
q = a * q1 + q2
p2, p1 = p1, p
q2, q1 = q1, q
# 结果就是 p1 / q1
```
-----
广义连分数
形式为:
$b_0+\frac{a_1}{b_1+\frac{a_2}{b_2+\cdots+\frac{a_n}{b_n}}}$
用递推:
$A_{-1}=1,\quad A_0=b_0$
$B_{-1}=0,\quad B_0=1$
对 $k=1,\dots,n$ :
$A_k=b_kA_{k-1}+a_kA_{k-2}$
$B_k=b_kB_{k-1}+a_kB_{k-2}$
最后:
$\text{值}=\frac{A_n}{B_n}$
简单连分数只是 $b_0=a_0,\ b_k=a_k,\ a_k=1$ 的特殊情况。
-----
## 1. 从最直观的“从后往前”看
简单连分数:
$$
[a_0;a_1,\dots,a_n]
=a_0+\frac1{a_1+\frac1{a_2+\cdots+\frac1{a_n}}}
$$
如果从最里面往外算,设当前尾部的值为:
$$
T_k=[a_k;a_{k+1},\dots,a_n]=\frac{N_k}{D_k}
$$
那么往外加一层 $a_{k-1}$,就变成:
$$
T_{k-1}=a_{k-1}+\frac1{T_k}
=a_{k-1}+\frac1{N_k/D_k}
=a_{k-1}+\frac{D_k}{N_k}
$$
通分:
$$
T_{k-1}=\frac{a_{k-1}N_k+D_k}{N_k}
$$
所以:
$$
N_{k-1}=a_{k-1}N_k+D_k,\qquad D_{k-1}=N_k
$$
这就是从后往前的递推:
$$
(N,D)\leftarrow (aN+D,\;N)
$$
它的道理非常直接:
**连分数每往外加一层,就是做一次 $x\mapsto a+1/x$。**
如果 $x=N/D$,那么:
$$
a+\frac1x=a+\frac DN=\frac{aN+D}{N}
$$
所以分子变成 $aN+D$,分母变成原来的分子 $N$。
---
## 2. 从前往后的 $p,q$ 递推为什么也对?
从前往后定义收敛项:
$$
\frac{p_k}{q_k}=[a_0;a_1,\dots,a_k]
$$
递推公式是:
$$
p_k=a_kp_{k-1}+p_{k-2}
$$
$$
q_k=a_kq_{k-1}+q_{k-2}
$$
初始条件:
$$
p_{-2}=0,\quad p_{-1}=1
$$
$$
q_{-2}=1,\quad q_{-1}=0
$$
这个递推看起来像是突然出现的,但它其实来自矩阵乘法。
---
## 3. 关键:线性分式变换与矩阵表示
考虑变换:
$$
f_a(x)=a+\frac1x
$$
它可以写成:
$$
f_a(x)=\frac{ax+1}{x}
$$
这种形式叫线性分式变换。它可以对应一个 $2\times 2$ 矩阵:
$$
f_a \longleftrightarrow M(a)=
\begin{pmatrix}
a & 1\\
1 & 0
\end{pmatrix}
$$
为什么?因为线性分式变换:
$$
x\mapsto \frac{Ax+B}{Cx+D}
$$
对应矩阵:
$$
\begin{pmatrix}
A & B\\
C & D
\end{pmatrix}
$$
而 $a+1/x=(ax+1)/(1x+0)$,所以矩阵就是:
$$
\begin{pmatrix}
a & 1\\
1 & 0
\end{pmatrix}
$$
线性分式变换有个重要性质:
**复合两个线性分式变换,对应矩阵相乘。**
也就是说:
$$
f_{a_0}(f_{a_1}(x))
$$
对应矩阵:
$$
M(a_0)M(a_1)
$$
于是整个连分数:
$$
[a_0;a_1,\dots,a_n]
$$
对应矩阵乘积:
$$
M(a_0)M(a_1)\cdots M(a_n)
$$
---
## 4. 矩阵乘积展开就是递推
设:
$$
M(a_0)M(a_1)\cdots M(a_k)
=
\begin{pmatrix}
p_k & p_{k-1}\\
q_k & q_{k-1}
\end{pmatrix}
$$
当 $k=-1$ 时,空乘积是单位矩阵:
$$
\begin{pmatrix}
1 & 0\\
0 & 1
\end{pmatrix}
$$
可以对应:
$$
p_{-1}=1,\quad p_{-2}=0,\quad q_{-1}=0,\quad q_{-2}=1
$$
现在右乘一个 $M(a_k)$:
$$
\begin{pmatrix}
p_{k-1} & p_{k-2}\\
q_{k-1} & q_{k-2}
\end{pmatrix}
\begin{pmatrix}
a_k & 1\\
1 & 0
\end{pmatrix}
=
\begin{pmatrix}
a_kp_{k-1}+p_{k-2} & p_{k-1}\\
a_kq_{k-1}+q_{k-2} & q_{k-1}
\end{pmatrix}
$$
所以新的左上角就是:
$$
p_k=a_kp_{k-1}+p_{k-2}
$$
新的左下角就是:
$$
q_k=a_kq_{k-1}+q_{k-2}
$$
这正是从前往后的递推公式。
最后,矩阵乘积作用在无穷大上:
$$
\begin{pmatrix}
p_n & p_{n-1}\\
q_n & q_{n-1}
\end{pmatrix}
(\infty)
=
\frac{p_n}{q_n}
$$
所以:
$$
[a_0;a_1,\dots,a_n]=\frac{p_n}{q_n}
$$
---
## 5. 总结一句话
递推之所以成立,是因为:
$$
a+\frac1x=\frac{ax+1}{x}
$$
每一步都是同一个线性分式变换。
线性分式变换复合对应矩阵相乘,而矩阵相乘展开后,自然得到:
$$
p_k=a_kp_{k-1}+p_{k-2}
$$
$$
q_k=a_kq_{k-1}+q_{k-2}
$$
从后往前算,是直接反复做 $N,D\leftarrow aN+D,N$;
从前往后算,是用 $p,q$ 递推维护当前收敛项的分子分母。
两者本质上是同一件事,只是计算方向不同。
广义连分数也一样,如果每层是:
$$
b_k+\frac{a_k}{x}
$$
它对应矩阵:
$$
\begin{pmatrix}
b_k & a_k\\
1 & 0
\end{pmatrix}
$$
递推就变成:
$$
A_k=b_kA_{k-1}+a_kA_{k-2}
$$
$$
B_k=b_kB_{k-1}+a_kB_{k-2}
$$
简单连分数只是 $b_k=a_k,\ a_k=1$ 的特殊情况。