32

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

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

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

9 回答 9

66

是的。标准库中的timeit模块是您检查这些内容的方式。例如:

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

如您所见,在我的 (first-day==old==slow;-) Macbook Air 上,该&解决方案可重复地比%解决方案快 7 到 18 纳秒。

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

让程序员相信微优化本质上是无关紧要的,这已被证明是一项不可能完成的任务——尽管自从 Knuth写到这已经 35 年了(计算机的速度已经快了几个数量级!)

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

正如他解释的那样,这是从 Hoare 更古老的声明中引用的。我想每个人都完全相信他们的案件属于剩下的 3%!

因此,与其无休止地重复“没关系”,我们(尤其是蒂姆·彼得斯(Tim Peters)应该得到那里的荣誉)放入标准的 Python 库模块timeit,这使得测量这些微基准变得非常容易,从而让至少一些程序员说服自己,嗯,这个案例确实属于 97% 的群体!-)

于 2009-07-07T01:28:58.613 回答
24

老实说,我认为这并不重要。

第一个问题是可读性。什么对其他开发人员更有意义?在检查数字的奇偶性时,我个人会期望模数。我希望大多数其他开发人员会期待同样的事情。通过引入一种不同的、意想不到的方法,您可能会使代码阅读和维护变得更加困难。

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

于 2009-07-07T01:30:52.853 回答
10

您可以获得的最佳优化是将测试放入函数中。' number % 2' 和 'number & 1' 是检查奇数/偶数的非常常用的方法,有经验的程序员会立即识别出这种模式,并且您可以随时输入诸如 '# if number is odd, then blah blah blah' 之类的注释,如果你真的需要它是显而易见的。

# state whether number is odd or even
if number & 1:
    print "Your number is odd"
else:
    print "Your number is even"
于 2009-07-07T02:00:48.343 回答
4

“返回 num & 1 和 True 或 False”?哇!如果你对速度很疯狂 (1) "return num & 1" (2) inline it:if somenumber % 2 == 1是清晰的并且节拍isodd(somenumber),因为它避免了 Python 函数调用。

于 2009-07-07T01:37:53.707 回答
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

有趣的是,这两种方法都是在没有函数调用的情况下同时进行的。

于 2009-07-07T01:56:27.413 回答
1

除了邪恶的优化之外,它还消除了每个编码人员无需看两次就可以理解的非常惯用的“var % 2 == 0”。所以这也违反了蟒蛇的禅宗,收获甚微。

此外,a = b 和 True 或 False 已被取代以获得更好的可读性

如果 num & 1 则返回 True,否则返回 False

于 2009-12-23T18:51:02.537 回答
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

奖励:事实证明,在 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
于 2018-01-15T16:23:26.920 回答
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
于 2011-03-01T10:33:20.353 回答
0

更新最新 python 版本的结果,% 比 & 快

➜ ✗ python -m timeit -s 'def isodd(x): x & 1' 'isodd(9)'
5000000 loops, best of 5: 78.4 nsec per loop
➜ ✗ python -m timeit -s 'def isodd(x): x & 1' 'isodd(10)'
5000000 loops, best of 5: 79.6 nsec per loop
➜ ✗ python -m timeit -s 'def isodd(x): x % 2' 'isodd(9)'
5000000 loops, best of 5: 65.8 nsec per loop
➜ ✗ python -m timeit -s 'def isodd(x): x % 2' 'isodd(10)'
5000000 loops, best of 5: 68.5 nsec per loop
于 2021-09-02T07:25:59.950 回答