我正在寻找一种算法(最好在Go或C中),以找出包含范围的可能分母中最接近的公共分数n / d(dmin,dmax,其中1 <= dmin&lt; = dmax&lt; ; = 1e15)。如果有多个与Pi相同距离的公共分数,我想找到分母最小的分数。
注意:强力方法效率不高,因此我正在寻找更智能/更高效的解决方案。
示例:对于dmin = 1且dmax = 10,最接近的公共分数为22/7,与Pi的距离约为0.001
首先想到:观察farey序列,我们可以找到所有分母最接近dmax的近似值。不幸的是,结果不符合dmin的约束。
答案 0 :(得分:1)
我没有时间获得完整答案,但这是一个部分答案。这种技术使用了连续分数的概念 - 在线有很多关于它们的概念。我会忽略你的值dmin,这在下面没有使用。
根据需要获取continued fraction expansion of pi到任意数量的地方。对于dmax <= 1e15的界限,您只需要前28个数字
[3, 7, 15, 1, 292, 1, 1, 1, 2, 1, 3, 1, 14, 2, 1, 1, 2, 2, 2, 2, 1, 84, 2, 1, 1, 15, 3, 13]
使用短循环查找具有刚刚低于dmax且高于dmax的分母的pi的会聚。在Python中将是
pi_cont_frac = [3, 7, 15, 1, 292, 1, 1, 1, 2, 1,
3, 1, 14, 2, 1, 1, 2, 2, 2, 2,
1, 84, 2, 1, 1, 15, 3, 13]
denomlo, denomhi = 1, 0
numlo, numhi = 0, 1
for q in pi_cont_frac:
denomlo, denomhi = denomhi, q * denomhi + denomlo
numlo, numhi = numhi, q * numhi + numlo
if denomhi > dmax:
break
某些软件(如Microsoft Excel)会使用分数numlo/denomlo
,但可能会有更好的近似值。现在找到使denomhi - r * denomlo
低于(或等于)dmax的自然数r的值。
然后,numlo/denomlo
或(denomhi - r * denomlo)/(denomhi - r * denomlo)
是您想要的最接近pi的分数。只需检查哪一个更近。
该算法具有阶数log(dmax),并且由于pi的特性,它通常要低得多。对于dmax <= 1e15,它需要28个循环,但需要更多清理声明。
你可以通过预先计算和存储收敛数(numhi和denomhi的值)并在dmax之上搜索denomhi的值来制作更快的算法。这也只需要28个数字,但分子和分母都需要这个数字。二进制搜索最多需要5个步骤才能找到它 - 几乎是瞬时的。使用更多存储和更少计算的另一种可能性是存储所有中间馏分。存储将达到数百个,至少三百个。如果你不喜欢pi的持续分数扩展的存储列表,你可以使用pi的值来动态计算,但是使用双精度(在C中)只能得到28个数字我告诉你。
如需更多研究,请查看连续分数和中间分数。
答案 1 :(得分:0)
更普遍的问题是如何将分数 p/q
表示为 n/d
,其中 d
在所需范围 drange = [dmin, dmax]
中,q
不在 {{ 1}}、drange
。我想象了以下问题的细化条件:
gcd(p,q) = 1
(因此,对于将 pi 近似为分母在 10 到 15 之间的分数的解决方案,44/14 是不可接受的;(蛮力)答案是 41/13)gcd(n, d) = 1
这不适用于 pi 的情况,因为分母是任意长的,但它可以适用于找到最接近 30/43 且分母在 160 和 170 之间的分数的情况;蛮力答案是 118/169。dmin > q
和 Daulton 的方法成功,因为它 a) 偶然地在所需范围内给出 q > dmax
或 b) 如果 d
无条件成功,因为答案 2*(dmin - 1) < dmax
在范围内.所以困难的情况是
a/b = m*a/m*b = n/d
但 Daulton 方法要么没有给出满足条件的答案,因为 q > dmax
小于 d
dmin
不适用 Daulton 方法的情况在任何一种情况下,我们都可以通过考虑分数的 Stern-Brocot table 来理解为什么这很难解决。 Daulton 的方法会将我们带到比预期更小的分母,但答案在表格的更深处,我们无法知道哪条路径会带我们到达我们正在寻找的 q < dmax
的最接近表示。>
然而,我们可以做得比检查所有可能性更好。
考虑分数是 p/q
。定义一个方法来告诉最接近分数的 n 的倍数:
0 < x < 1
如果我们计算分母为 dmin 和 dmax 的最近分数,我们将看到必须考虑的分子范围。这些将少于分母的数量,因此需要更少的工作。这是使用 SymPy 组合起来的
def nfrac(x, n, reduce=1):
"""Return the multiple of 1/n that is closest to x
which is not necessarily the fraction closest to x
with denom less than n;
"""
from math import gcd
scaled = int(round(x * n))
m, r = divmod(scaled, n)
if reduce:
g = gcd(r, n)
r, n = r//g, n//g
return m*n+r, n
因此,不是考虑与 1700 个不同分母最接近的分数,而是考虑与 [22655, 22896] 范围内的分子(其中 241 个)最接近的分数。这个结果用蛮力方法检查过,发现是一样的。它与从 Daulton 建议的方法中获得的不同:
def inrange(x, a, b):
w, r = divmod(a, x)
if not r:
return True
if a < (w + 1)*x <= b:
return True
return False
def dlimit(f, n, m):
if f > 1:
w = int(f)
a, b, c = dlimit(f - w, n, m)
return a + b*w, b, c
p, q = f.numerator, f.denominator
if q > m:
approx = f.limit_denominator(m)
p = approx.numerator
q = approx.denominator
if inrange(q, n, m):
w, r = divmod(n, q)
if r:
w += 1
return w*p, w*q, 'easy'
N,_ = nfrac(f, n, 0)
M,_ = nfrac(f, m, 0)
af = 1/f
p = [nfrac(af, i, 0) for i in range(N, M + 1)]
if p[0][0] < n:
p = p[1:]
if p[-1][0] > m:
p = p[:-1]
assert all(n <= i[0] <= m for i in p)
return tuple(reversed(sorted([i for i in p if i[0]],
key=lambda x: abs(f - x[1]/x[0]))[0])) +( (N, M),)
>>> import math
>>> from fractions import Fraction
>>> dlimit(Fraction(math.pi),160000,161700)
(507895, 161668, (22655, 22896))