GitHub地址:戳我看源码>>>>蒙特卡罗方法
概述
- 蒙特卡罗方法又称统计模拟法、随机抽样技术,是一种随机模拟方法
- 用事件发生的“频率”来决定事件的“概率”。
- 用数学方法在计算机上大量、快速地模拟试验,以获得问题的近似解。
- 为象征性地表明这一方法的概率统计特征,故借用赌城蒙特卡罗命名。
应用
求PI值
思路
PI
- 用蒙特卡罗算法的思想就是随机往正方形里打点,圆内的点个数之和近似于圆的面积,打点总数近似于正方形的面积
- PI = 4 * cirleArea / squareArea
- PI = 4 * 圆内的点 / 打点总数
代码
const _inCircle = Symbol('inCircle')
const _calcPI = Symbol('calcPI')
const _addPoint = Symbol('addPoint')
class PI {
constructor(N = 1000000000, consoleInterval = 100000000) {
this.N = N // 预计打点次数
this.r = 100 // 半径
this.cirleArea = 0 // 圆内点个数
this.squareArea = 0 // 已打点个数
this.consoleInterval = consoleInterval // 每打多少个点打印一次PI
}
// 判断是否落在圆内 包含圆边上的点
[_inCircle](x, y) {
return Math.pow(this.r - x, 2) + Math.pow(this.r - y, 2) <= Math.pow(this.r, 2)
}
// 计算目前的PI值
[_calcPI]() {
// SquareArea不包含4条边上的点 所以加上4条边的长度
return 4 * this.cirleArea / (this.squareArea + 8 * this.r)
}
// 随机打点
[_addPoint]() {
// 正方形内随机打点的坐标 结果没有包含四天边上的点
let x = Math.random() * 2 * this.r
let y = Math.random() * 2 * this.r
this.squareArea++
if (this[_inCircle](x, y)) {
this.cirleArea++
}
}
// 获取并打印PI
getPI() {
for (let i = 1; i < this.N + 1; i++) {
this[_addPoint]()
if (i % this.consoleInterval === 0) {
console.log(this.cirleArea, this.squareArea, 'PI', this[_calcPI]())
}
}
}
}
// 实例化PI 获取PI值
new PI().getPI()
打印结果
consolePI
从图中可以看到 PI值接近于理论值 而且数据量越大越接近理论值
三门问题
描述
三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let's Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机率?如果严格按照上述的条件,即主持人清楚地知道,自己打开的那扇门后是羊,那么答案是会。不换门的话,赢得汽车的几率是1/3。换门的话,赢得汽车的几率是2/3。
思路
三扇门分别用1、2、3表示,随机一扇门为有奖品的,随机一扇门为参赛者所选择的,如果两者相等则参赛者不换就赢了反之失败,如果不相等则参赛者换就一定会赢反之失败
代码
const _run = Symbol('_run')
const _calcRate = Symbol('_calcRate')
class MontyHall {
constructor(change = true, N = 10000000, consoleInterval = N / 10) {
this.win = 0 // 获胜次数
this.loose = 0 // 失败次数
this.count = 0 // 已进行次数
this.change = change // 是否改变当前选择
this.N = N // 预计实验次数
this.consoleInterval = consoleInterval // 每打多少个点打印一次PI
}
// 开启一场比赛
[_run]() {
let lottery = Math.ceil(Math.random() * 3)
let player = Math.ceil(Math.random() * 3)
if (player === lottery) {
this.change ? this.loose++ : this.win++
} else {
this.change ? this.win++ : this.loose++
}
this.count++
}
// 计算概率
[_calcRate]() {
return (this.win / this.count * 100).toFixed(2) + '%'
}
// 获取并打印rate
getRate() {
for (let i = 1; i < this.N + 1; i++) {
this[_run]()
if (i % this.consoleInterval === 0) {
console.log(this.win, i, 'rate', this[_calcRate]())
}
}
}
}
// 实例化MontyHall 获取Rate值
new MontyHall().getRate()
打印结果
consoleRate
从打印结果可以看出如果换门 胜率基本稳定在2/3
总结
当遇到一些没有固定规律可循的问题时,个人觉得这种暴力模拟的方法不失为一种好方法