问题
假如有四个选项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