题解:P1727 计算π
以下部分内容来自 https://www.numberworld.org,有兴趣的读者可以去了解更多内容。
截止至 2025 年 7 月 15 日,本做法为非打表做法最优解,总用时仅 98ms,记录,甚至
我们都知道, Chudnovsky 公式是一个非常快计算
这个公式很复杂,我们显然不能一项一项进行计算,这会使时间复杂度达到
假设我们要计算以下形式的式子:
注意上面的是
边界条件(由于端点处的特殊性,若在初始时有
这种算法叫做 binary splitting。
对于所有
我们把 Chudnovsky 公式改写成上面式子的形式(把阶乘和指数运算写到乘积里面),得:
我们采用上面的方法分治计算,由于
接下来我们分析这个算法的时间复杂度,我们要精确到
如果我们把
但是如果我们在计算中使用高精度浮点数,只保留
参考实现:
void _pi_impl(int l,int r,BigFloat &P,BigFloat &Q,BigFloat &R){
if (l+1==r){
P=(BigFloat(13591409)+BigFloat(545140134)*r)*(R=BigFloat(2*r-1)*(6*r-5)*(6*r-1));
if (r&1) P.s.sign^=1;
Q=BigFloat(10939058860032000ll)*r*r*r;
return;
}
int mid=(l+r)>>1;
_pi_impl(l,mid,P,Q,R);
BigFloat P2,Q2,R2;
_pi_impl(mid,r,P2,Q2,R2);
P*=Q2;
P+=P2*R;
Q*=Q2,R*=R2;
}
// Chudnovsky
BigFloat pi(){ // eps=O(151931373056000^-n)
BigFloat p,q,r;
// auto st=clock();
_pi_impl(0,ceil(BigFloat_prec*digit/std::log10(151931373056000)),p,q,r);
// std::cout<<"main:"<<double(clock()-st)/CLOCKS_PER_SEC*1000<<"ms\n";
// st=clock();
auto tmp=sqrt(BigFloat(10005));
// std::cout<<"sqrt:"<<double(clock()-st)/CLOCKS_PER_SEC*1000<<"ms\n";
return BigFloat(4270934400ll)*q/(tmp*(p+q*13591409));
}
高精度模板可以去这里自取,同时也欢迎大家来查错。