蒙特卡罗方法求非概率分布的随机数

问题

假如有四个选项ABCD,每个选项的概率不一样,比如A-0.01 B-0.45 C-0.35 D-0.19 。
如果要从四个选项中任选一个的话,选出的选项尽可能的符合对应的概率。如选100次,才会选出一个A出来。怎么实现?
概率图

算法

请参考 蒙特卡罗方法

思路

从ABCD中,随机选择一个作为候选选项,然后在从0,1中随机产生一个随机数,如果此随机数大于候选选项对应的概率,那么重新选择。若随机数小于等于候选选项的概率,那么此候选选项就是我们要的随机数。

    val array = Array("A", "B", "C", "D")
    val map = Map("A" -> 0.01, "B" -> 0.45, "C" -> 0.35, "D" -> 0.19)
  /**
    * 返回0-$(length-1)的随机整数
    *
    * @param length
    * @return
    */
  private def nextInt(length: Int): Int = {
    (random * length).toInt
  }
  /**
    * 随机从ABCD中选择一个选项,作为X,然后在从0-1中选择一个数值作为Y,那么如果Y大于X对应的概率,递归重头开始选,否则,X就是我们要选择的值。
    *
    * @param array 选项数组
    * @param map   选项对应的概率
    * @return 本次选出的选项
    */
  private def nextValue(array: Array[String], map: Map[String, Double]): String = {
    val x = array(nextInt(array.length))
    val y = random
    if (y > map(x)) {
      //继续nextValue
      nextValue(array, map)
    } else {
      x
    }
  }
  //此处用到了尾递归

测试代码

  def main(args: Array[String]): Unit = {
    //测试长度
    val length = 100000000
    val start = System.currentTimeMillis()
    val allResult = (0 to length).map(x => nextValue(array, map))
    val end = System.currentTimeMillis()
    println(s"计算${length}个随机数,共花费${(end - start) / 1000}秒")

    array.foreach(x => {
      val count = allResult.count(_.equals(x))
      println((s"${x}共随机获得${count}次,概率为${BigDecimal.valueOf(count) / 		BigDecimal.valueOf(length).toDouble}"))
    })
  }

输出结果

计算100000000个随机数,共花费29秒
A共随机获得998503次,概率为0.00998503
B共随机获得44999964次,概率为0.44999964
C共随机获得35003001次,概率为0.35003001
D共随机获得18998533次,概率为0.18998533

posted on 2016-07-26 00:44  luckuan1985  阅读(387)  评论(0编辑  收藏  举报