蒙特卡罗方法(Monte Carlo method)

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

总结

当遇到一些没有固定规律可循的问题时,个人觉得这种暴力模拟的方法不失为一种好方法

你可能感兴趣的:(蒙特卡罗方法(Monte Carlo method))