使用 64 位分子和分母的 pi 的最佳有理近似值是多少?

Posted

技术标签:

【中文标题】使用 64 位分子和分母的 pi 的最佳有理近似值是多少?【英文标题】:What is the best rational approximation to pi using a 64-bit numerator and denominator? 【发布时间】:2020-07-15 23:16:08 【问题描述】:

用两个 64 位整数表示的最准确的有理对是什么?如果您愿意,请随意添加其他 int 类型。

这是我想出的,但我确信它可以变得更准确,因为分母可以变得更大 - 我只是在以 10 为基础考虑。我很确定分子应该类似于uint64 max。

// c++
inline constexpr auto pi_num = 3141592653589793238ull;
inline constexpr auto pi_den = 1000000000000000000ull;
// c
const unsigned long long pi_num = 3141592653589793238ull;
const unsigned long long pi_den = 1000000000000000000ull;

【问题讨论】:

这个问题与C、C++甚至一般编程无关。 @Ayxan 当然可以。我需要一个准确的pi 并且不想使用浮点数。 C++ 不允许浮点数作为非类型模板参数,所以我只能使用有理整数。 然后更新您的问题以反映整数要求(以及原因) 尽可能获得最准确的四精度表示,转储四边形的字节,提取尾数和指数,使用尾数作为分子,使用 2^exponent 作为分母。切掉位(并降低指数),直到数字适合 64 位。这将为您提供 113 位的精度(减去您删除的那些)。关键是:您希望分母是 2 的幂,因此在执行除法时不会发生浮点损失 有趣的是,即使你得到存储到那个精度的数字,你在做任何类型的乘法或除法的第二秒都会失去很多,除非你有一个可以处理双倍精度大小的结果(此处为 256 位)。 【参考方案1】:

您可以使用continued fractions 获得无理数的极好近似值。如果您以前没有遇到过连分数,这是一种将数字写成嵌套分数形式的方法

将越来越多的项添加到连分数中,可以得到越来越好的有理数近似值。

continued fraction of π 是

[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, 1, 4, 2, 6, 6, 99, 1, 2, 2, 6, 3, 5, 1, 1, 6, 8, 1, 7, 1, 2, 3, 7, 1, 2, 1, 1, 12, 1, 1, 1, 3, 1, 1, 8, 1, 1, 2, 1, 6, 1, 1, 5, 2, 2, 3, 1, 2, 4, 4, 16, 1, 161, ...]

因此我们可以编写一个小 Python 脚本来计算基于连分数表示的近似值,如下所示:

from fractions import *

digits = [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, 1, 4, 2, 6, 6, 99, 1, 2, 2, 6, 3, 5, 1, 1, 6, 8, 1, 7, 1, 2, 3, 7, 1, 2, 1, 1, 12, 1, 1, 1, 3, 1, 1, 8, 1, 1, 2, 1, 6, 1, 1, 5, 2, 2, 3, 1, 2, 4, 4, 16, 1, 161]

for i in range(len(digits)):
    # Start with the last digit
    f = Fraction(digits[i]);

    # Keep rewriting it as term + 1 / prev
    for j in range(i-1, -1, -1):
        f = digits[j] + 1 / f
    
    # Stop if we overshoot
    if f.numerator >= 2**64 or f.denominator >= 2**64: break
    
    # Print the approximation we found
    print(f)

这会打印出具有越来越好的近似值的连分数,直到我们超出适合 64 位整数的部分为止。这是输出:

3
22/7
333/106
355/113
103993/33102
104348/33215
208341/66317
312689/99532
833719/265381
1146408/364913
4272943/1360120
5419351/1725033
80143857/25510582
165707065/52746197
245850922/78256779
411557987/131002976
1068966896/340262731
2549491779/811528438
6167950454/1963319607
14885392687/4738167652
21053343141/6701487259
1783366216531/567663097408
3587785776203/1142027682075
5371151992734/1709690779483
8958937768937/2851718461558
139755218526789/44485467702853
428224593349304/136308121570117
5706674932067741/1816491048114374
6134899525417045/1952799169684491
30246273033735921/9627687726852338
66627445592888887/21208174623389167
430010946591069243/136876735467187340
2646693125139304345/842468587426513207

我相信,最后一个近似值是 π 的最佳近似值,它适合 64 位整数。 (有可能在这个分母和下一个分母之间出现一个更好的分母,溢出一个 64 位整数,但这仍然非常接近!)因此,你会想要

const uint64_t pi_num   = 2646693125139304345u;
const uint64_t pi_denom = 842468587426513207u;

This source 报告此近似值精确到小数点后 37 位 (!):

3.14159265358979323846264338327950288418 (approximation)
3.14159265358979323846264338327950288419 (actual)

对于您的目标,这应该绰绰有余。 (当然,除非您试图设置查找 π 或类似数字的记录。^_^)

【讨论】:

观察到使用了 128 位(两个 64 位整数),并且 log10(2^128) ≈ 38.5,所以 37 位小数(加上初始数字)是我们所期望的。 如果没有使用生成高精度十进制表示的库显式测试范围内的所有剩余数字,我不确定是否有一种简单的方法可以测试其余分母以查看是否有他们中的一个会给出更好的近似值。很抱歉! 如the Wikipedia article 中所述,最好的算法比截断连分数稍微复杂一些。 @anatolyg:是的 - 除了收敛之外,还需要考虑半收敛。在这种特殊情况下,有六个分子严格在26466931251393043452^64 之间的半收敛;这些半收敛是 π 的最佳可能界,但不是最佳可能界,因此2646693125139304345/842468587426513207 确实是 π 的最佳可能近似,分子和分母均以2^64 为界。 Johannis Wallis,“De Algebra Tractatus;Historicus et Practicus”,牛津 1685 年,p。 54,有这个数据(我用绝对错误注释):496638392183958130 / 158084910090576507 (3.75e-35); 926649338775027373 / 294961645557763847 (1.61e-35); 1356660285366096616 / 431838381024951187 (8.26e-36); 1786671231957165859 / 568715116492138527 (4.19e-36); 2216682178548235102 / 705591851959325867 (1.70e-36); 2646693125139304345 / 842468587426513207 (1.41e-38); 5723397196869677933 / 1821813910320213754 (6.37e-37)。最后一个数字对似乎是错误的。

以上是关于使用 64 位分子和分母的 pi 的最佳有理近似值是多少?的主要内容,如果未能解决你的问题,请参考以下文章

Week01-作业

week01-绪论

01-抽象数据类型

博客作业01-抽象数据类型

博客作业01-抽象数据类型

博客作业1--抽象数据类型