趣味 SQL:使用蒙特卡洛方法计算圆周率 Pi 的值

pi
圆周率(Pi)是圆的周长与直径的比值,一般用希腊字母 π 表示,是一个在数学及物理学中普遍存在的数学常数。π 也等于圆形之面积与半径平方之比,是精确计算圆周长、圆面积、球体积等几何形状的关键值。

蒙特卡洛方法(Monte Carlo method)也称统计模拟法、统计试验法。蒙特卡洛方法的基本思想是:首先建立一个描述问题的概率模型或随机过程,使它的参数或数字特征等于问题的解;然后通过对模型或过程的观察或抽样试验来计算这些参数或数字特征;最后给出所求解的近似值。

使用蒙特卡洛法求解 π 值基本步骤如下:构造一个单位正方形和一个单位圆的 1/4;往正方形内随机投点,根据点到原点的距离是否大于 1 判断点是落在 1/4 的圆内还是在圆外。

pi

根据落在两个不同区域的点的数目,例如总的点数为 N,1/4 圆内的点数为 n。如果次数够多,根据两者的面积公式:

n N ≈ 1 4 π 1. 0 2 1. 0 2 = π 4 \frac n N \approx \frac {{\frac1 4}{\pi 1.0^2}} {1.0^2} = \frac \pi 4 Nn1.0241π1.02=4π

也就是说,

π ≈ 4 n N \pi \approx 4 \frac n N π4Nn

我们只要知道 n 和 N 的值,就可以获得 π 的近似值。

接下来我们介绍如何在不同的数据库中实现以上过程,首先是 MySQL:

with recursive pi(x, y, n) as (
  select rand() x,rand() y, 1 n
  union all
  select rand(), rand(), n+1 from pi where n<100
)
select 4*sum(1-floor(x*x+y*y))/count(*) val from pi;
val |
----|
3.16|

其中,WITH 表示通用表表达式。关于通用表表达式(Common Table Expression)的详细介绍可以参考这篇文章

rand() 函数随机返回 0 到 1 之间的数字;floor() 函数用于返回小于等于参数的整数,这里表示点落在 1/4 圆内(0)或者圆外(1)。

试验次数10010001000010000010000000100000000
近似值3.163.1563.10763.13623.141143.1415404

随着次数的增加,模拟计算出的值越接近 π 的真实值。

其他的数据库也可以使用类似的方式进行模拟,语法略微有些差异:

-- Oracle 使用 dbms_random.value() 函数获取随机值
with pi(x, y, n) as (
  select dbms_random.value() x,dbms_random.value() y, 1 n from dual
  union all
  select dbms_random.value(), dbms_random.value(), n+1 from pi where n<10000
)
select 4*sum(1-floor(x*x+y*y))/count(*) val from pi;
VAL  |
-----|
3.166|

-- SQL Server rand() 函数在一个 SELECT 中只返回一个相同的值,需要设置不同的 seed
with pi(x, y, n) as (
select rand(CHECKSUM(NEWID())) x,rand(CHECKSUM(NEWID())) y, 1 n
union all
select rand(CHECKSUM(NEWID())), rand(CHECKSUM(NEWID())), n+1 from pi where n<10000
)
select 4*sum(1-floor(x*x+y*y))/count(*) val from pi OPTION( MAXRECURSION 32767);
val  |
-----|
3.134|

-- PostgreSQL 使用 random() 函数获取随机数
with recursive pi(x, y, n) as (
select random() x,random() y, 1 n
union all
select random(), random(), n+1 from pi where n<10000
)
select 4*sum(1-floor(x*x+y*y))/count(*) val from pi;
val   |
------|
3.1476|

-- SQLite random() 函数返回 -9223372036854775808 到 +9223372036854775807 之间的数字
with recursive pi(x, y, n) as (
select abs((random()/9223372036854775808.0)) x,abs((random()/9223372036854775808.0)) y, 1 n
union all
select abs((random()/9223372036854775808.0)), abs((random()/9223372036854775808.0)), n+1 from pi where n<10000
)
select 4*sum(1.0-floor(x*x+y*y))/count(*) val from pi;
val   |
------|
3.1584|

除了 Oracle 之外,以上其他数据库都提供了 pi() 函数。

定期更新数据库领域相关文章,欢迎关注❤️、点赞👍、转发📣!

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不剪发的Tony老师

为 5 个 C 币而折腰。

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

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

打赏作者

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

抵扣说明:

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

余额充值