2009-07-07 82 views
26

檢查奇數和偶數的整數是檢查比使用模數更高效的最低位?檢查奇數時是否快於%?

>>> def isodd(num): 
     return num & 1 and True or False 

>>> isodd(10) 
False 
>>> isodd(9) 
True 
+1

什麼是「和真或假」? – FogleBird 2009-07-07 01:33:39

+14

如果你想獲得一個布爾結果,只要做bool(num&1) – FogleBird 2009-07-07 01:34:36

+0

我正在使用Python 3.1 – riza 2009-07-07 01:41:44

回答

57

是的。標準庫中的timeit模塊是如何檢查這些事情的。 E.g:

AmAir:stko aleax$ python -mtimeit -s'def isodd(x): x & 1' 'isodd(9)' 
1000000 loops, best of 3: 0.446 usec per loop 
AmAir:stko aleax$ python -mtimeit -s'def isodd(x): x & 1' 'isodd(10)' 
1000000 loops, best of 3: 0.443 usec per loop 
AmAir:stko aleax$ python -mtimeit -s'def isodd(x): x % 2' 'isodd(10)' 
1000000 loops, best of 3: 0.453 usec per loop 
AmAir:stko aleax$ python -mtimeit -s'def isodd(x): x % 2' 'isodd(9)' 
1000000 loops, best of 3: 0.461 usec per loop 

正如你看到的,我(首日== ==老慢;-)的Macbook Air,該&解決方案是重複7至18納秒比%解決方案快。

timeit不僅告訴你什麼是快,但多少(只運行測試幾次),這通常表明它是多麼超級不重要。(你真的護理約10納秒差異,開銷時)... - )...

令人信服的程序員認爲微優化本質上是不相關的已被證明是一項不可能完成的任務 - 儘管已經過了35年(在哪些計算機上獲得了命令幅度更快!)因爲Knuth wrote

我們應該忘記小的效率,比如約97%的時間:不成熟的優化是所有邪惡的根源。

正如他解釋的,是來自Hoare的一個更古老的陳述。我想每個人都完全相信他們的案件會落在剩下的3%!因此,我們(Tim Peters尤其值得榮譽)放在標準的Python庫模塊timeit中,而不是無休止地重複「無關緊要」,這使得測量這樣的微基準變得輕而易舉讓至少一些程序員說服自己,嗯,這箱子落在97%組 - !)

23

要完全誠實的,我不認爲它很重要。

第一個問題是可讀性。對其他開發人員更有意義的是什麼?我個人認爲,在檢查數字的均勻性/奇特性時會有模數。我期望大多數其他開發人員會期望同樣的事情。通過引入一種不同的,意想不到的方法,您可能會讀取代碼,因此維護更加困難。

第二個只是一個事實,你可能永遠不會有任何一個操作瓶頸。我是爲了優化,但是早期優化是您在任何語言或環境中可以做的最糟糕的事情。如果出於某種原因,確定數字是偶數還是奇數是瓶頸,那麼找出解決問題的最快方法。但是,這又回到了我的第一點 - 第一次寫例程時,應該儘可能以可讀性最強的方式編寫。

4

「return num & 1 and True or False」?華!如果你瘋狂(1)「return num & 1」(2)內聯:if somenumber % 2 == 1清晰並且跳動isodd(somenumber),因爲它避免了Python函數調用。

4

約翰提出了一個很好的觀點。真正的開銷是在函數調用中:

[email protected] ~> python -mtimeit -s'9 % 2' 
10000000 loops, best of 3: 0.0271 usec per loop 
[email protected] ~> python -mtimeit -s'10 % 2' 
10000000 loops, best of 3: 0.0271 usec per loop 

[email protected] ~> python -mtimeit -s'9 & 1' 
10000000 loops, best of 3: 0.0271 usec per loop 
[email protected] ~> python -mtimeit -s'9 & 1' 
10000000 loops, best of 3: 0.0271 usec per loop 

[email protected] ~> python -mtimeit -s'def isodd(x): x % 2' 'isodd(10)' 
1000000 loops, best of 3: 0.334 usec per loop 
[email protected] ~> python -mtimeit -s'def isodd(x): x % 2' 'isodd(9)' 
1000000 loops, best of 3: 0.358 usec per loop 

[email protected] ~> python -mtimeit -s'def isodd(x): x & 1' 'isodd(10)' 
1000000 loops, best of 3: 0.317 usec per loop 
[email protected] ~> python -mtimeit -s'def isodd(x): x & 1' 'isodd(9)' 
1000000 loops, best of 3: 0.319 usec per loop 

有趣的是,這兩種方法都是在沒有函數調用的情況下同時進行的。

10

您可以獲得的最佳優化是而不是將測試放入函數中。 'number % 2'和'& 1'是檢查奇/偶的非常常見的方式,有經驗的程序員會立即識別該模式,並且您總是可以拋出一個註釋,如'#如果數字是奇數,那麼等等等等'if你真的需要它是顯而易見的。

# state whether number is odd or even 
if number & 1: 
    print "Your number is odd" 
else: 
    print "Your number is even" 
1

除了邪惡的優化,它帶走了很地道的「VAR%2 == 0」,每一個編碼器理解不看兩次。所以這也違反了pythons禪宗,也很少有收穫。

而且A = B和真或假的

回報已被取代爲提高可讀性,真要是NUM & 1,否則假

0

,在前述的答案真的很驚訝,沒有做過這兩個變量的設置(定時字面是不同的故事),並沒有函數調用(這顯然隱藏了「較低期限」)。從ipython timeit陷入了這個時間,我得到了明確的贏家x & 1 - 使用python2.6更好〜〜18%(使用python3.1〜〜12%)。

在我的非常老的機器:

$ python -mtimeit -s 'x = 777' 'x&1' 
10000000 loops, best of 3: 0.18 usec per loop 
$ python -mtimeit -s 'x = 777' 'x%2' 
1000000 loops, best of 3: 0.219 usec per loop 

$ python3 -mtimeit -s 'x = 777' 'x&1' 
1000000 loops, best of 3: 0.282 usec per loop 
$ python3 -mtimeit -s 'x = 777' 'x%2' 
1000000 loops, best of 3: 0.323 usec per loop 
0

使用Python 3.6的答案是沒有。使用2017年MBP上的代碼顯示使用模數更快。

# odd.py 
from datetime import datetime 

iterations = 100_000_000 


def is_even_modulo(n): 
    return not n % 2 


def is_even_and(n): 
    return not n & 1 


def time(fn): 
    start = datetime.now() 
    for i in range(iterations, iterations * 2): 
     fn(i) 
    print(f'{fn.__name__}:', datetime.now() - start) 


time(is_even_modulo) 
time(is_even_and) 

給出了這樣的結果:

$ python3 -m odd 
is_even_modulo: 0:00:14.347631 
is_even_and: 0:00:17.476522 
$ python3 --version 
Python 3.6.1 

在其他的答案表明,該函數調用是一個大的開銷,但是,刪除它表明模仍快於位運算符和在Python 3.6.1 :

# odd.py 
from datetime import datetime 

iterations = 100_000_000 


def time_and(): 
    start = datetime.now() 
    for i in range(iterations): 
     i & 1 
    print('&:', datetime.now() - start) 


def time_modulo(): 
    start = datetime.now() 
    for i in range(iterations): 
     i % 2 
    print('%:', datetime.now() - start) 


time_modulo() 
time_and() 

結果:

$ python3 -m odd 
%: 0:00:05.134051 
&: 0:00:07.250571 

獎金:事實證明,這需要大約兩倍的時間在Python 2.7中運行。

$ time python2 -m odd 
('&:', '0:00:20.169402') 
('%:', '0:00:19.837755') 

real 0m41.198s 
user 0m39.091s 
sys 0m1.899s 
$ time python3 -m odd 
&: 0:00:11.375059 
%: 0:00:08.010738 

real 0m19.452s 
user 0m19.354s 
sys 0m0.042s