算圆周率的奇怪方式增加了——Python利用物理定律模拟计算圆周率

用动量和能量守恒计算圆周率

实现原理

灵感来自G. Galperin的论文PLAYING POOL WITH π (THE NUMBER π FROM A BILLIARD POINT OF VIEW),DOI: 10.1070/RD2003v008n04ABEH000252。我在这里只是简单介绍下原理,感兴趣的同学们可以自己仔细阅读。论文是全英文,有需要可以私信我,有问题我帮你解答。Youtube上3Blue1Brown的视频有介绍这片论文,有很好的图片和讲解,更好理解。

简单概括下,Galperin的想法很神奇。不同于普通的算π的方法(如无限级数法),作者用动量守恒和动能守恒算 π π π

首先,我们来理解一下动量守恒和能量守恒。
在完全弹性碰撞中,两个物体在碰撞后除了动能的转换没有其它能量的消耗。这是完全弹性碰撞的动量守恒公式:
m 1 v 1 + m 2 v 2 = m 1 v 1 ′ + m 2 v 2 ′ m_{1}v_{1}+m_{2}v_{2}=m_{1}v_{1}^{'}+m_{2}v_{2}^{'} m1v1+m2v2=m1v1+m2v2

基于此,二者能量的关系可用动能守恒表示:
1 2 m 1 v 1 2 + 1 2 m 2 v 2 2 = 1 2 m 1 v 1 ′ 2 + 1 2 m 2 v 2 ′ 2 \frac{1}{2}m_{1}v_{1}^{2}+\frac{1}{2}m_{2}v_{2}^{2}=\frac{1}{2}m_{1}v_{1}^{'2}+\frac{1}{2}m_{2}v_{2}^{'2} 21m1v12+21m2v22=21m1v12+21m2v22
连立上面两条公式,我们得到:
{ m 1 v 1 + m 2 v 2 = m 1 v 1 ′ + m 2 v 2 ′ 1 2 m 1 v 1 2 + 1 2 m 2 v 2 2 = 1 2 m 1 v 1 ′ 2 + 1 2 m 2 v 2 ′ 2 \left\{ \begin{array}{lr} m_{1}v_{1}+m_{2}v_{2}=m_{1}v_{1}^{'}+m_{2}v_{2}^{'} & \\ \\ \frac{1}{2}m_{1}v_{1}^{2}+\frac{1}{2}m_{2}v_{2}^{2}=\frac{1} {2}m_{1}v_{1}^{'2}+\frac{1}{2}m_{2}v_{2}^{'2} & \end{array} \right. m1v1+m2v2=m1v1+m2v221m1v12+21m2v22=21m1v12+21m2v22
化简该公式,我们得到:
{ v 1 ′ = ( m 1 − m 2 ) v 1 + 2 m 2 v 2 m 1 + m 2 v 2 ′ = ( m 2 − m 1 ) v 2 + 2 m 1 v 1 m 1 + m 2 \left\{ \begin{array}{lr} v_{1}^{'}=\frac{(m_{1}-m_{2})v_{1}+2m_{2}v_{2}}{m_{1}+m_{2}} & \\ \\ v_{2}^{'}=\frac{(m_{2}-m_{1})v_{2}+2m_{1}v_{1}}{m_{1}+m_{2}} & \end{array} \right. v1=m1+m2(m1m2)v1+2m2v2v2=m1+m2(m2m1)v2+2m1v1
这样,我们就有了程序模拟的核心算法。

在下图所示的理想的物理系统(无摩擦力,墙和地面质量无限,球体无角动量等)中,这两条关系成立:(图源:Galperin)
Source: Galperin
具体的数学证明,Galperin在论文中有提到。我们要做的,就是用暴力模拟验证他的结论。设物体朝墙运动时速度为正,远离墙时速度为负。二者的碰撞在以下情况时: 0 > v 2 ≥ v 1 0>v_{2}\geq v_{1} 0>v2v1,两个物体不会再次相遇。

Galperin的数学论证非常详尽。总而言之,他证明了以下关系:
Π ( N ) = 314159265358979... Π(Ν)=314159265358979... Π(N)=314159265358979...
其中 Π ( N ) Π(Ν) Π(N)是当大物体重量是小物体的 10 0 N 100^Ν 100N倍时的碰撞次数, N N N是计算 π π π的位数-1。
我们要做的,就是根据他的论文写个程序实现。

代码

#!/usr/local/bin/python3
import time
from decimal import *
from decimal import Decimal as dcm #这个库提高下浮点运算精度

getcontext().prec = 50 #小数位数设为50位

N = int(input("Enter N:"))
m = dcm(1)        #初始化m1
M = dcm(m*100**N) #初始化m2

vm0 = dcm(0)      #初始化v1
vM0 = dcm(10000)  #初始化v2

vm = vm0 	#初始化v1'
vM = vM0	#初始化v2'

cnt = -3 #初始化计数器 经过测试软件总会多算几次 所以先-3

start = time.time() #设置一个计时器,算所需时间
while not (vm < vM and vM < dcm(0)): #也就是当两个物体不会同时向右且不再相遇时
    vm = dcm((m-M)*vm0+2*M*vM0)/dcm(M+m)
    vM = dcm((M-m)*vM0+2*m*vm0)/dcm(M+m) #上面提到的两个核心模拟算法
    cnt += 1
    vm0 = dcm(-vm) #弹性碰撞,小物体的动量变成反方向
    vM0 = dcm(vM)  #大物体动量更新,方向不变
    cnt += 1 #又撞了一次
end = time.time() #结束计时器
print("Number of collisions:", cnt)
print("Time elapse:", end-start, "seconds.") #输出

运行结果

运行结果见下表:(本人自制)
最左第一列是N的值
第二列是输出的碰撞次数
第三列是运行时间
第四列是根据输出计算的 π π π
第五列是和真实 π π π值的差距
运行结果
下图是N的变化与计算偏差的指数回归:(作者自制)
计算准确度
由上图可见,随着N的增大,计算的圆周率指数级地无限趋于准确。

下图是N的变化与计算所需时间的指数回归:(作者自制)
时间变化
可见,随着准确度的指数级提高,计算的时间复杂度也指数级地提高。因此,一般的笔记本真算不过来太大的N。

总结与拓展

我们的程序确实非常有效。然而,有很多其它计算 π π π的方法更加高效。我们的程序也有一些疏漏。比如,输出的值永远是奇数。这是因为在while循环里默认最小增加值是2。有兴趣的小伙伴可以思考下如何解决!也许可以多加几个判定?
对于Galperin论文感兴趣的同学请私信我或留评论,我会很乐意帮助你们解答相关问题!

ps:本人在用这些东西写小论文,所以转载请引用!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EricFrenzy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值