python中对随机数进行四舍五入的最快方法

2024-04-26 09:52:19 发布

您现在位置:Python中文网/ 问答频道 /正文

我想生成高达一定精度的随机数,一次一个(所以我不寻找矢量化的解决方案)

我在stackoverflow上的this QnA中找到了一个方法,它按照承诺给了我这些基准测试。这种方法的速度几乎是原来的两倍。 现在,让我困惑的是

%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?

当我使用变量时,为什么上面的方法比较慢?当我直接传递随机生成的数字时,它会恢复失去的速度。 我错过了什么

在我的代码中,因为我需要在多个位置进行舍入,所以我希望它包装在一个函数中,如下所示。我知道函数调用有一个小的成本,但我不认为这是花费大量增加的时间的原因This answer说,它仍然应该比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的增益。或者有没有更快的方法


Tags: 方法函数loopitrandomprvariable速度
1条回答
网友
1楼 · 发布于 2024-04-26 09:52:19

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)

每个回路约160纳秒,同时:

%timeit int(fl*(10**pr)+0.5)/(10**pr)
%timeit int(fl*(10.**pr)+0.5)/(10.**pr)

以~500和~250纳秒的速度运行。我没意识到int和float的求幂是如此的不同

相关问题 更多 >