【发布时间】:2014-11-28 23:57:27
【问题描述】:
假设我想使用基于球体体积的蒙特卡罗模拟来估计 π(代码如下)。
有没有办法优化对random.random()的调用?
$ cat pi_estimate.py
#!/usr/bin/env python
"""
The task:
Consider the part of the sphere x^2 + y^2 + z^2 <= 1 for which x,y,z > 0
The calculation:
We generate n (e.g., 10,000) points (x,y,z) with 0 <= x,y,z <= 1 and using
the formula for sphere volume: V = (4/3) * PI * r^3, we'll estimate PI.
The cube in which the sphare resides has a volume: 8 * r^3
Now, if we only consider the quardant where 0 <= x,y,z <= 1, then it's
only 1/8th of the total volumes, namely, Vs = (1/6) * PI * r^3, and
Vc = r^3
As r == 1, Vs = (1/6) * PI, and Vc = 1
Vs/Vc = (1/6) * PI
Thus PI = 6 * Vs/Vc
So, every point (x,y,z) with 0 <= x,y,z <= 1 which is x^2 + y^2 + z^2 <= 1
is added to Vs (and Vc), and if not than it is only in Vc.
"""
import random
N = 1000000
Vs_counter = 0
for i in xrange(N):
x = random.random()
y = random.random()
z = random.random()
if (x**2 + y**2 + z**2) <= 1:
Vs_counter += 1
pi = 6 * (1.0 * Vs_counter / N)
print "PI is estimated",pi
以下分析确实表明该脚本大部分时间都在random.random():
$ python -m cProfile pi_estimate.py
PI is estimated 3.142194
3000049 function calls in 3.856 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 __future__.py:48(<module>)
1 0.000 0.000 0.000 0.000 __future__.py:74(_Feature)
7 0.000 0.000 0.000 0.000 __future__.py:75(__init__)
1 0.007 0.007 0.007 0.007 hashlib.py:55(<module>)
6 0.000 0.000 0.000 0.000 hashlib.py:94(__get_openssl_constructor)
1 2.822 2.822 3.856 3.856 pi_estimate.py:22(<module>)
1 0.000 0.000 0.003 0.003 random.py:100(seed)
1 0.030 0.030 0.040 0.040 random.py:40(<module>)
1 0.000 0.000 0.000 0.000 random.py:655(WichmannHill)
1 0.000 0.000 0.000 0.000 random.py:72(Random)
1 0.000 0.000 0.000 0.000 random.py:805(SystemRandom)
1 0.000 0.000 0.003 0.003 random.py:91(__init__)
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_md5}
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha1}
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha224}
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha256}
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha384}
1 0.000 0.000 0.000 0.000 {_hashlib.openssl_sha512}
1 0.000 0.000 0.000 0.000 {binascii.hexlify}
1 0.001 0.001 0.001 0.001 {function seed at 0xffe31e2c}
6 0.000 0.000 0.000 0.000 {getattr}
6 0.000 0.000 0.000 0.000 {globals}
1 0.000 0.000 0.000 0.000 {math.exp}
2 0.000 0.000 0.000 0.000 {math.log}
1 0.000 0.000 0.000 0.000 {math.sqrt}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
3000000 0.994 0.000 0.994 0.000 {method 'random' of '_random.Random' objects}
1 0.002 0.002 0.002 0.002 {posix.urandom}
编辑(2014 年 11 月 27 日星期四 20:45:26)
由于 @ch3ka 检查了他的代码并在使用库函数的本地名称时展示了改进,我决定使用 my 代码进行检查。
在我的代码中,我无法确定任何改进:
$ for i in {1..9} ; do for script in pi_estimate.py pi_estimate_local.py ; do echo $script; python -m cProfile $script | grep 3000000; done; done
pi_estimate.py
3000000 0.360 0.000 0.360 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.341 0.000 0.341 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.326 0.000 0.326 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.337 0.000 0.337 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.331 0.000 0.331 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.317 0.000 0.317 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.327 0.000 0.327 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.316 0.000 0.316 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.354 0.000 0.354 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.325 0.000 0.325 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.326 0.000 0.326 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.341 0.000 0.341 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.349 0.000 0.349 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.324 0.000 0.324 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.326 0.000 0.326 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.315 0.000 0.315 0.000 {method 'random' of '_random.Random' objects}
pi_estimate.py
3000000 0.358 0.000 0.358 0.000 {method 'random' of '_random.Random' objects}
pi_estimate_local.py
3000000 0.324 0.000 0.324 0.000 {method 'random' of '_random.Random' objects}
这是两个脚本(我编辑了文档字符串):
$ cat pi_estimate.py | tail -14
import random
N = 1000000
Vs_counter = 0
for i in xrange(N):
x = random.random()
y = random.random()
z = random.random()
if (x**2 + y**2 + z**2) <= 1:
Vs_counter += 1
pi = 6 * (1.0 * Vs_counter / N)
print "PI is estimated",pi
$ cat pi_estimate_local.py | tail -14
from random import random as rnd
N = 1000000
Vs_counter = 0
for i in xrange(N):
x = rnd()
y = rnd()
z = rnd()
if (x**2 + y**2 + z**2) <= 1:
Vs_counter += 1
pi = 6 * (1.0 * Vs_counter / N)
print "PI is estimated",pi
编辑(2014 年 11 月 28 日星期五 17:50:09)
在@ch3ka 最后三个 cmets 之后,我计算了对 random() 的 300k 次调用,正如@ch3ka 指出的那样,Python 分析器确实呈现出误导性的画面。使用本地引用调用 random 确实可以节省时间,即:
$ for i in {1..9}; do python pi_estimate.py ; done
PI is estimated 3.143076 -> execution time: 2.62900018692
PI is estimated 3.143346 -> execution time: 2.58100008965
PI is estimated 3.140286 -> execution time: 2.52200007439
PI is estimated 3.145734 -> execution time: 2.5110001564
PI is estimated 3.140898 -> execution time: 2.51300001144
PI is estimated 3.143058 -> execution time: 2.59200000763
PI is estimated 3.139332 -> execution time: 2.60400009155
PI is estimated 3.142956 -> execution time: 2.47399997711
PI is estimated 3.144552 -> execution time: 2.50100016594
$ for i in {1..9}; do python pi_estimate_local.py ; done
PI is estimated 3.146772 -> execution time: 2.22300004959
PI is estimated 3.142038 -> execution time: 2.18499994278
PI is estimated 3.139032 -> execution time: 2.14800000191
PI is estimated 3.14052 -> execution time: 2.20199990273
PI is estimated 3.141384 -> execution time: 2.25199985504
PI is estimated 3.142086 -> execution time: 2.25200009346
PI is estimated 3.137748 -> execution time: 2.18099999428
PI is estimated 3.141906 -> execution time: 2.40199995041
PI is estimated 3.138126 -> execution time: 2.16100001335
【问题讨论】:
标签: python python-2.7 optimization random cygwin