是&检查奇数时,快于%?

时间:2009-07-07 01:26:43

标签: python performance bit-manipulation modulo

要检查奇数和偶数整数,最低位检查是否比使用模数更有效?

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

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

8 个答案:

答案 0 :(得分:59)

是的。标准库中的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

如您所见,在我的(第一天== old == slow ;-) Macbook Air上,&解决方案的重复速度比%解决方案快7到18纳秒。< / p>

timeit不仅告诉你什么更快,而且告诉你多少(只是运行几次),这通常表明它是多么重要(你真的非常关心吗?当调用函数的开销大约为400时,差异为10纳秒? - )......

令人信服的程序员认为微观优化本质上是不相关的已被证明是一项不可能完成的任务 - 即使已经过了35年(计算机的速度提高了几个数量级!),因为Knuth wrote

  

我们应该忘记小事   效率,约占97%   时间:过早优化是   万恶之源。

正如他所解释的那样是来自霍尔的一个更古老的陈述的引用。我想每个人都完全相信他们的案子会落在剩余的3%中!

因此,我们(Tim Peters特别值得获得荣誉)不是无休止地重复“无关紧要”,而是放入标准的Python库模块timeit中,这使得测量这样的微观模型变得非常容易基准,从而让至少一些程序员说服自己,嗯,这个案例属于97%组! - )

答案 1 :(得分:23)

说实话,我认为不重要。

第一个问题是可读性。对其他开发者更有意义的是什么?我个人会在检查数字的均匀度/奇数时期望模数。我希望大多数其他开发人员都期望同样的事情。通过引入一种不同的,意外的方法,您可能会使代码读取更难以进行维护。

第二个事实是,在进行任何操作时,您可能不会遇到瓶颈。我是为了优化,但早期优化是你可以在任何语言或环境中做的最糟糕的事情。如果由于某种原因,确定一个数字是偶数还是奇数是一个瓶颈,那么找到解决问题的最快方法。然而,这让我回到了我的第一点 - 第一次编写例程时,应该以最可读的方式编写。

答案 2 :(得分:10)

您可以获得的最佳优化是将测试放入函数中。 'number % 2'和'数字&amp; 1'是检查奇/偶的非常常见的方法,有经验的程序员会立即识别模式,你可以随时抛出一个评论,例如'#如果数字是奇数,那么等等等等等,如果你真的需要它是显而易见的

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

答案 3 :(得分:4)

“返回num&amp; 1和True或False”?华!如果你速度疯狂(1)“返回num&amp; 1”(2)内联它:if somenumber % 2 == 1清晰可见并击败isodd(somenumber)因为它避免了Python函数调用。

答案 4 :(得分:4)

约翰提出了一个很好的观点。真正的开销是在函数调用中:

me@localhost ~> python -mtimeit -s'9 % 2'
10000000 loops, best of 3: 0.0271 usec per loop
me@localhost ~> python -mtimeit -s'10 % 2'
10000000 loops, best of 3: 0.0271 usec per loop

me@localhost ~> python -mtimeit -s'9 & 1'
10000000 loops, best of 3: 0.0271 usec per loop
me@localhost ~> python -mtimeit -s'9 & 1'
10000000 loops, best of 3: 0.0271 usec per loop

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

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

有趣的是,两种方法在没有函数调用的情况下同时恢复。

答案 5 :(得分:1)

除了邪恶的优化之外,它还会消除每个编码员在不看两次的情况下理解的非常惯用的“var%2 == 0”。所以这也违反了蟒蛇禅,但收益甚微。

此外,为了更好的可读性,已经取代了a = b和True或False

如果num&amp; 1其他错误

答案 6 :(得分:1)

使用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

Bonus:事实证明,这需要花费大约两倍的时间在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

答案 7 :(得分:0)

真的很惊讶以上答案都没有做变量设置(时间字面是不同的故事)和没有函数调用(显然隐藏“较低的术语”)。从ipython的timeit坚持到那个时间,在那里我得到了明显的胜利者x&amp; 1 - 使用python2.6(使用python3.1约12%)更好的约18%。

在我很老的机器上:

$ 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