簡體   English   中英

為什么(在 Python 中)random.randint 比 random.random 慢得多?

[英]Why (in Python) is random.randint so much slower than random.random?

我對一些隨機 integer 生成代碼的相對速度感到好奇。 我寫了以下內容來檢查它:

from random import random
from random import choice
from random import randint
from math import floor
import time

def main():
    times = 1000000

    startTime = time.time()
    for i in range(times):
        randint(0,9)
    print(time.time()-startTime)

    startTime = time.time()
    for i in range(times):
        choice([0,1,2,3,4,5,6,7,8,9])
    print(time.time()-startTime)

    startTime = time.time()
    for i in range(times):
        floor(10*random())##generates random integers in the same range as randint(0,9)
    print(time.time()-startTime)

main()

該代碼的一次試驗的結果是

0.9340872764587402

0.6552846431732178

0.23188304901123047

即使在執行了乘法和 math.floor 之后,生成整數的最終方法也是迄今為止最快的。 弄亂生成數字的范圍的大小並沒有改變任何東西。

那么,為什么 random 方式比 randint 快? 有什么理由(除了易用性、可讀性和不引起錯誤)人們更喜歡 randint 而不是隨機的(例如,randint 產生更多的隨機偽隨機整數)? 如果floor(x*random())感覺可讀性不夠,但您想要更快的代碼,您是否應該使用 go 進行專門的例程?

def myrandint(low,high):   ###still about 1.6 longer than the above, but almost 2.5 times faster than random.randint
    return floor((high-low+1)*random())+low  ##returns a random integer between low and high, inclusive. Results may not be what you expect if int(low) != low, etc. But the numpty who writes 'randint(1.9,3.2)' gets what they deserve.

在我回答你的問題之前(別擔心,我確實做到了),請注意常見的程序員的習語:

過早的優化是萬惡之源。

雖然情況並非總是如此,但除非您需要,否則不要擔心微優化。

這對於 Python 來說是雙倍的:如果您正在編寫速度至關重要的東西,您通常會希望使用運行速度更快的語言編寫它,例如 C。 然后,如果您想將 Python 用於應用程序的非關鍵部分,則可以為該 C 代碼編寫 Python 綁定,例如,Num。

與其專注於使代碼中的單個表達式或函數盡可能快地運行,不如專注於您使用的算法和代碼的整體結構(並使其具有可讀性,但您已經意識到這一點)。 然后,當您的應用程序開始緩慢運行時,您可以對其進行分析以找出哪些部分花費的時間最多,並僅改進這些部分。

對結構良好、可讀性強的代碼進行更改將更容易,並且優化實際瓶頸通常會比大多數微優化提供更好的加速時間編碼比。 花在思考兩個表達式中哪個運行得更快的時間是你可以花時間完成其他事情的時間。

作為一個例外,我會說學習為什么一個選項比另一個更快是值得的,因為這樣你就可以將更多的通用知識融入你未來的編程中,讓你更快地調用而不用擔心細節。

但是關於為什么我們不應該浪費時間擔心速度已經夠多了,讓我們來談談速度。


查看random模塊的來源(對於 CPython 3.7.4),開頭評論末尾的這一行提供了一個簡短的答案:

* The random() method is implemented in C, executes in a single Python step,
  and is, therefore, threadsafe.

第一個陳述是對我們最重要的陳述。 random is a python binding for a C function, so the complexity of its operation runs at the blinding speed of machine code rather than the relatively slow speed of Python.

另一方面, randint在 Python 中實現的,因此會受到顯着的速度損失。 randint調用randrange ,它確保范圍的邊界(和步長)是整數,范圍不為空,步長不為零,然后調用getrandbits ,這在 C 中實現。

僅此一項就產生了randint的大部分緩慢。 然而,還有一個變量在起作用。

再深入一點,進入內部 function _randbelow ,事實證明,獲取 0 到n之間的隨機數的算法非常簡單:它獲取n中的位數,然后隨機生成那么多位,直到得到number 不大於n

平均而言(在n的所有可能值中),這幾乎沒有影響,但比較極端情況,這是顯而易見的。

我寫了一個 function來測試那個循環的影響。 結果如下:

bits   2 ** (n - 1)   (2 ** n) - 1   ratio
   1   1.122583558    1.06002008     1.059021031
   2   1.083326405    1.008945953    1.0737209479
   4   1.071182065    0.900332951    1.1897621472
   8   1.074771422    0.91913078     1.1693345989
  16   1.144971642    0.920407928    1.2439828115
  32   1.134300228    0.927834944    1.2225237208
  64   1.244957927    0.96199336     1.2941439918
 128   1.293113046    1.00158057     1.2910724157
 256   1.366579178    1.069473996    1.2778049612
 512   1.629956014    1.190126045    1.3695658715

第一列是位數,第二和第三列是平均時間(以微秒為單位),以找到具有這么多位的隨機 integer,以微秒為單位,超過 1 000 000 次運行。 最后一列是第二列和第三列的比率。

您會注意到具有給定位長度的最大數字的平均運行時間大於具有該位長度的最小數字的平均運行時間。 這是因為那個循環:

當尋找不大於最大n位數的n位數時,只需要一次嘗試。 但是要找到一個小於最小的數(2 n -1是單個 1 位,后跟n -1 個 0 位),一半的嘗試會失敗。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM