【发布时间】:2014-09-04 21:17:58
【问题描述】:
假设我想用蒙特卡罗模拟计算 Pi 作为练习。
我正在编写一个函数,它在正方形(0, 1), (1, 0) 中随机选取一个点并测试该点是否在圆内。
import scala.math._
import scala.util.Random
def circleTest() = {
val (x, y) = (Random.nextDouble, Random.nextDouble)
sqrt(x*x + y*y) <= 1
}
然后我正在编写一个函数,它将测试函数和试验次数作为参数,并返回发现测试为真的试验分数。
def monteCarlo(trials: Int, test: () => Boolean) =
(1 to trials).map(_ => if (test()) 1 else 0).sum * 1.0 / trials
...我可以计算 Pi
monteCarlo(100000, circleTest) * 4
现在我想知道monteCarlo功能是否可以改进。你如何写monteCarlo 高效和可读?
例如,由于试验次数很多,是否值得使用 view 或 iterator 代替 Range(1, trials) 和 reduce 代替 map 和 sum ?
【问题讨论】:
-
主要的加速应该来自删除 sqrt。
sqrt(x*x + y*y) <= 1与x*x + y*y <= 1相同(即两边平方) -
好点!谢谢。
-
我在这里放了一个包含各种方法的微基准测试页面:gist.github.com/willf/e1f2ce95f04442af53e5 我很高兴地说递归是最快的:)
-
@WillFitzgerald,很有趣,谢谢。我实际上很惊讶基于 Stream 的版本有多快。在这种事情上,递归可能总是最快的,因为开销较小,但是,正如他们所说,“递归是函数式编程的首选”——高效,但对于工作程序来说不一定是最清晰或最快的。跨度>
-
在优化时,想想它编译成机器码的样子。机器代码中最快的事情总是简单的 while 循环。方法调用、对象分配(对于 lambdas 通常是必需的)等在像这样的紧密循环中非常昂贵。所以最快的将是一个while循环。下面的递归方法只是因为尾调用优化而很快,这意味着它们不会编译为字节码中的递归方法调用,而是编译为 while 循环。
标签: scala montecarlo