连分数和 Pell 方程 - 数值问题
Continued fractions and Pell's equation - numerical issues
数学背景
Continued fractions are a way to represent numbers (rational or not), with a basic recursion formula 计算一下。给定一个数字 r,我们定义 r[0]=r
并且有:
for n in range(0..N):
a[n] = floor(r[n])
if r[n] == [an]: break
r[n+1] = 1 / (r[n]-a[n])
其中 a
是最终表示。我们还可以通过
定义一系列convergents
h[-2,-1] = [0, 1]
k[-2, -1] = [1, 0]
h[n] = a[n]*h[n-1]+h[n-2]
k[n] = a[n]*k[n-1]+k[n-2]
其中 h[n]/k[n]
收敛于 r.
Pell's equation is a problem of the form x^2-D*y^2=1
where all numbers are integers and D
is not a perfect square in our case. A solution for a given D
that minimizes x
is given by continued fractions。基本上,对于上面的等式,保证这个(基本)解是 x=h[n]
和 y=k[n]
找到的最低 n
解决了 [=57 的连分数扩展中的方程=]sqrt(D
).
问题
我无法让这个简单的算法适用于 D=61
。我首先注意到它没有解决 100 个系数的 Pell 方程,所以我将它与 Wolfram Alpha 的 convergents and continued fraction representation 进行了比较,并注意到第 20 个元素失败了 - 与我得到的 4
相比,表示形式是 3
,产生不同的收敛性 - h[20]=335159612
在 Wolfram 上与 425680601
相比对我而言。
我在两个系统上测试了下面的代码,两种语言(虽然公平地说,Python 是我猜的幕后 C),并得到相同的结果 - 循环 20 上的差异。我'你会注意到收敛仍然是准确和收敛的! 为什么我得到的结果与 Wolfram Alpha 不同,是否可以修复?
为了测试,这里有一个 Python 程序来求解 D=61
的 Pell 方程,打印前 20 个收敛点和连分数表示 cf
(以及一些额外的不需要的绒毛):
from math import floor, sqrt # Can use mpmath here as well.
def continued_fraction(D, count=100, thresh=1E-12, verbose=False):
cf = []
h = (0, 1)
k = (1, 0)
r = start = sqrt(D)
initial_count = count
x = (1+thresh+start)*start
y = start
while abs(x/y - start) > thresh and count:
i = int(floor(r))
cf.append(i)
f = r - i
x, y = i*h[-1] + h[-2], i*k[-1] + k[-2]
if verbose is True or verbose == initial_count-count:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
if x**2 - D*y**2 == 1:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
print(cf)
return
count -= 1
r = 1/f
h = (h[1], x)
k = (k[1], y)
print(cf)
raise OverflowError(f"Converged on {x} {y} with count {count} and diff {abs(start-x/y)}!")
continued_fraction(61, count=20, verbose=True, thresh=-1) # We don't want to stop on account of thresh in this example
一个c
程序做同样的事情:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int main() {
long D = 61;
double start = sqrt(D);
long h[] = {0, 1};
long k[] = {1, 0};
int count = 20;
float thresh = 1E-12;
double r = start;
long x = (1+thresh+start)*start;
long y = start;
while(abs(x/(double)y-start) > -1 && count) {
long i = floor(r);
double f = r - i;
x = i * h[1] + h[0];
y = i * k[1] + k[0];
printf("%ld\u00B2-%ldx%ld\u00B2 = %lf\n", x, D, y, x*x-D*y*y);
r = 1/f;
--count;
h[0] = h[1];
h[1] = x;
k[0] = k[1];
k[1] = y;
}
return 0;
}
mpmath
,python的多精度库可以使用。请注意所有重要的数字都是 mp 格式。
在下面的代码中,x, y and i
是标准的多精度整数。 r
和 f
是多精度实数。请注意,初始计数设置为高于 20。
from mpmath import mp, mpf
mp.dps = 50 # precision in number of decimal digits
def continued_fraction(D, count=22, thresh=mpf(1E-12), verbose=False):
cf = []
h = (0, 1)
k = (1, 0)
r = start = mp.sqrt(D)
initial_count = count
x = 0 # some dummy starting values, they will be overwritten early in the while loop
y = 1
while abs(x/y - start) > thresh and count > 0:
i = int(mp.floor(r))
cf.append(i)
x, y = i*h[-1] + h[-2], i*k[-1] + k[-2]
if verbose or initial_count == count:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
if x**2 - D*y**2 == 1:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
print(cf)
return
count -= 1
f = r - i
r = 1/f
h = (h[1], x)
k = (k[1], y)
print(cf)
raise OverflowError(f"Converged on {x} {y} with count {count} and diff {abs(start-x/y)}!")
continued_fraction(61, count=22, verbose=True, thresh=mpf(1e-100))
输出类似于 wolfram 的:
...
335159612²-61x42912791² = 3
1431159437²-61x183241189² = -12
1766319049²-61x226153980² = 1
[7, 1, 4, 3, 1, 2, 2, 1, 3, 4, 1, 14, 1, 4, 3, 1, 2, 2, 1, 3, 4, 1]
数学背景
Continued fractions are a way to represent numbers (rational or not), with a basic recursion formula 计算一下。给定一个数字 r,我们定义 r[0]=r
并且有:
for n in range(0..N):
a[n] = floor(r[n])
if r[n] == [an]: break
r[n+1] = 1 / (r[n]-a[n])
其中 a
是最终表示。我们还可以通过
h[-2,-1] = [0, 1]
k[-2, -1] = [1, 0]
h[n] = a[n]*h[n-1]+h[n-2]
k[n] = a[n]*k[n-1]+k[n-2]
其中 h[n]/k[n]
收敛于 r.
Pell's equation is a problem of the form x^2-D*y^2=1
where all numbers are integers and D
is not a perfect square in our case. A solution for a given D
that minimizes x
is given by continued fractions。基本上,对于上面的等式,保证这个(基本)解是 x=h[n]
和 y=k[n]
找到的最低 n
解决了 [=57 的连分数扩展中的方程=]sqrt(D
).
问题
我无法让这个简单的算法适用于 D=61
。我首先注意到它没有解决 100 个系数的 Pell 方程,所以我将它与 Wolfram Alpha 的 convergents and continued fraction representation 进行了比较,并注意到第 20 个元素失败了 - 与我得到的 4
相比,表示形式是 3
,产生不同的收敛性 - h[20]=335159612
在 Wolfram 上与 425680601
相比对我而言。
我在两个系统上测试了下面的代码,两种语言(虽然公平地说,Python 是我猜的幕后 C),并得到相同的结果 - 循环 20 上的差异。我'你会注意到收敛仍然是准确和收敛的! 为什么我得到的结果与 Wolfram Alpha 不同,是否可以修复?
为了测试,这里有一个 Python 程序来求解 D=61
的 Pell 方程,打印前 20 个收敛点和连分数表示 cf
(以及一些额外的不需要的绒毛):
from math import floor, sqrt # Can use mpmath here as well.
def continued_fraction(D, count=100, thresh=1E-12, verbose=False):
cf = []
h = (0, 1)
k = (1, 0)
r = start = sqrt(D)
initial_count = count
x = (1+thresh+start)*start
y = start
while abs(x/y - start) > thresh and count:
i = int(floor(r))
cf.append(i)
f = r - i
x, y = i*h[-1] + h[-2], i*k[-1] + k[-2]
if verbose is True or verbose == initial_count-count:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
if x**2 - D*y**2 == 1:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
print(cf)
return
count -= 1
r = 1/f
h = (h[1], x)
k = (k[1], y)
print(cf)
raise OverflowError(f"Converged on {x} {y} with count {count} and diff {abs(start-x/y)}!")
continued_fraction(61, count=20, verbose=True, thresh=-1) # We don't want to stop on account of thresh in this example
一个c
程序做同样的事情:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int main() {
long D = 61;
double start = sqrt(D);
long h[] = {0, 1};
long k[] = {1, 0};
int count = 20;
float thresh = 1E-12;
double r = start;
long x = (1+thresh+start)*start;
long y = start;
while(abs(x/(double)y-start) > -1 && count) {
long i = floor(r);
double f = r - i;
x = i * h[1] + h[0];
y = i * k[1] + k[0];
printf("%ld\u00B2-%ldx%ld\u00B2 = %lf\n", x, D, y, x*x-D*y*y);
r = 1/f;
--count;
h[0] = h[1];
h[1] = x;
k[0] = k[1];
k[1] = y;
}
return 0;
}
mpmath
,python的多精度库可以使用。请注意所有重要的数字都是 mp 格式。
在下面的代码中,x, y and i
是标准的多精度整数。 r
和 f
是多精度实数。请注意,初始计数设置为高于 20。
from mpmath import mp, mpf
mp.dps = 50 # precision in number of decimal digits
def continued_fraction(D, count=22, thresh=mpf(1E-12), verbose=False):
cf = []
h = (0, 1)
k = (1, 0)
r = start = mp.sqrt(D)
initial_count = count
x = 0 # some dummy starting values, they will be overwritten early in the while loop
y = 1
while abs(x/y - start) > thresh and count > 0:
i = int(mp.floor(r))
cf.append(i)
x, y = i*h[-1] + h[-2], i*k[-1] + k[-2]
if verbose or initial_count == count:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
if x**2 - D*y**2 == 1:
print(f'{x}\u00B2-{D}x{y}\u00B2 = {x**2-D*y**2}')
print(cf)
return
count -= 1
f = r - i
r = 1/f
h = (h[1], x)
k = (k[1], y)
print(cf)
raise OverflowError(f"Converged on {x} {y} with count {count} and diff {abs(start-x/y)}!")
continued_fraction(61, count=22, verbose=True, thresh=mpf(1e-100))
输出类似于 wolfram 的:
...
335159612²-61x42912791² = 3
1431159437²-61x183241189² = -12
1766319049²-61x226153980² = 1
[7, 1, 4, 3, 1, 2, 2, 1, 3, 4, 1, 14, 1, 4, 3, 1, 2, 2, 1, 3, 4, 1]