在 python 中舍入随机数的最快方法
Fastest way to round random numbers in python
我想生成达到一定精度的随机数,一次一个(所以我不是在寻找矢量化解决方案)。
我在 Whosebug 的 中找到了一个方法,它按照承诺给了我这些基准测试。该方法绝对快了将近两倍。
现在,这就是让我困惑的地方。
%timeit int(0.5192853551955484*(10**5)+0.5)/(10.**5) #> 149 ns ± 5.76 ns per loop
%timeit round(0.5192853551955484, 5) #> 432 ns ± 11.7 ns per loop
## Faster as expected
fl = random.random()
pr = 5
%timeit int(fl*(10**pr)+0.5)/(10.**pr) #> 613 ns ± 27.9 ns per loop
%timeit round(fl, pr) #> 444 ns ± 9.25 ns per loop
## Slower?!
%timeit int(random.random()*(10**5)+0.5)/(10.**5) #> 280 ns ± 29.3 ns per loop
%timeit round(random.random(), 5) #> 538 ns ± 17.5 ns per loop
## Faster than using a variable even though it has the overhead
## of creating a random number for each call?
为什么在使用变量时上述方法变慢了?当我直接传递随机生成的数字时,它会恢复丢失的速度。
我错过了什么?
在我的代码中,因为我需要在多个地方进行舍入,所以我希望将它包装在一个函数中,如下所示。我知道与函数调用相关的成本很小,但我不认为这是花费大部分时间的原因。 说,它仍然应该比 python 的标准 round()
函数更快。
def rounder(fl, pr):
p = float(10**pr)
return int(fl * p + 0.5)/p
%timeit rounder(random.random(), 5) #> 707 ns ± 14.2 ns per loop
%timeit round(random.random(), 5) #> 525 ns ± 22.1 ns per loop
## Having a global variable does make it faster by not having to do the 10**5 everytime
p = float(10**5)
def my_round_5(fl):
return int(fl* p + 0.5)/p
%timeit my_round_5(random.random()) #> 369 ns ± 18.9 ns per loop
我宁愿在我的代码中没有全局变量,但假设我承认这个要求,但性能增益比使用没有变量的公式要小。
所以,最后一个问题是,哪种方法对我使用最有益?
切换到需要全局变量的函数只有 100-150ns 的增益。或者有什么办法可以更快。
Python 字节编译器“知道”数字是如何工作的,并且它使用这些知识来尽可能地优化事物。您可以使用 dis
模块查看发生了什么。
例如,您的第一个“快速”示例:
from dis import dis
def fn():
return int(0.5192853551955484*(10**5)+0.5)/(10.**5)
dis(fn)
实际上是:
2 0 LOAD_GLOBAL 0 (int)
2 LOAD_CONST 1 (51929.03551955484)
4 CALL_FUNCTION 1
6 LOAD_CONST 2 (100000.0)
8 BINARY_TRUE_DIVIDE
10 RETURN_VALUE
即它知道 0.5192853551955484*(10**5)+0.5
的计算结果并在编译字节码时执行它。如果你有 pr
作为参数,它不能这样做,所以在 运行 代码时必须做更多的工作。
要回答“什么是最好的”问题,可能是这样的:
def fn(pr):
# cache to prevent global lookup
rng = random.random
# evaluate precision once
p = 10. ** pr
# generate infinite stream of random numbers
while True:
yield int(rng() * p + 0.5) / p
可以作为基准:
x = fn(5)
%timeit next(x)
每个循环给出 ~160ns,而:
%timeit int(fl*(10**pr)+0.5)/(10**pr)
%timeit int(fl*(10.**pr)+0.5)/(10.**pr)
运行 在 ~500 和 ~250ns 内。没有意识到 int 与 float 的幂运算如此不同!
我想生成达到一定精度的随机数,一次一个(所以我不是在寻找矢量化解决方案)。
我在 Whosebug 的
%timeit int(0.5192853551955484*(10**5)+0.5)/(10.**5) #> 149 ns ± 5.76 ns per loop
%timeit round(0.5192853551955484, 5) #> 432 ns ± 11.7 ns per loop
## Faster as expected
fl = random.random()
pr = 5
%timeit int(fl*(10**pr)+0.5)/(10.**pr) #> 613 ns ± 27.9 ns per loop
%timeit round(fl, pr) #> 444 ns ± 9.25 ns per loop
## Slower?!
%timeit int(random.random()*(10**5)+0.5)/(10.**5) #> 280 ns ± 29.3 ns per loop
%timeit round(random.random(), 5) #> 538 ns ± 17.5 ns per loop
## Faster than using a variable even though it has the overhead
## of creating a random number for each call?
为什么在使用变量时上述方法变慢了?当我直接传递随机生成的数字时,它会恢复丢失的速度。 我错过了什么?
在我的代码中,因为我需要在多个地方进行舍入,所以我希望将它包装在一个函数中,如下所示。我知道与函数调用相关的成本很小,但我不认为这是花费大部分时间的原因。 round()
函数更快。
def rounder(fl, pr):
p = float(10**pr)
return int(fl * p + 0.5)/p
%timeit rounder(random.random(), 5) #> 707 ns ± 14.2 ns per loop
%timeit round(random.random(), 5) #> 525 ns ± 22.1 ns per loop
## Having a global variable does make it faster by not having to do the 10**5 everytime
p = float(10**5)
def my_round_5(fl):
return int(fl* p + 0.5)/p
%timeit my_round_5(random.random()) #> 369 ns ± 18.9 ns per loop
我宁愿在我的代码中没有全局变量,但假设我承认这个要求,但性能增益比使用没有变量的公式要小。
所以,最后一个问题是,哪种方法对我使用最有益? 切换到需要全局变量的函数只有 100-150ns 的增益。或者有什么办法可以更快。
Python 字节编译器“知道”数字是如何工作的,并且它使用这些知识来尽可能地优化事物。您可以使用 dis
模块查看发生了什么。
例如,您的第一个“快速”示例:
from dis import dis
def fn():
return int(0.5192853551955484*(10**5)+0.5)/(10.**5)
dis(fn)
实际上是:
2 0 LOAD_GLOBAL 0 (int)
2 LOAD_CONST 1 (51929.03551955484)
4 CALL_FUNCTION 1
6 LOAD_CONST 2 (100000.0)
8 BINARY_TRUE_DIVIDE
10 RETURN_VALUE
即它知道 0.5192853551955484*(10**5)+0.5
的计算结果并在编译字节码时执行它。如果你有 pr
作为参数,它不能这样做,所以在 运行 代码时必须做更多的工作。
要回答“什么是最好的”问题,可能是这样的:
def fn(pr):
# cache to prevent global lookup
rng = random.random
# evaluate precision once
p = 10. ** pr
# generate infinite stream of random numbers
while True:
yield int(rng() * p + 0.5) / p
可以作为基准:
x = fn(5)
%timeit next(x)
每个循环给出 ~160ns,而:
%timeit int(fl*(10**pr)+0.5)/(10**pr)
%timeit int(fl*(10.**pr)+0.5)/(10.**pr)
运行 在 ~500 和 ~250ns 内。没有意识到 int 与 float 的幂运算如此不同!