奇数と偶数の整数をチェックするには、モジュロを使用するよりも最下位ビットのチェックの方が効率的ですか?
>>> def isodd(num):
return num & 1 and True or False
>>> isodd(10)
False
>>> isodd(9)
True
奇数と偶数の整数をチェックするには、モジュロを使用するよりも最下位ビットのチェックの方が効率的ですか?
>>> def isodd(num):
return num & 1 and True or False
>>> isodd(10)
False
>>> isodd(9)
True
うん。標準ライブラリの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
ご覧のとおり、私の (初日==古い==遅い;-) Macbook Air では、&
解は解よりも 7 ~ 18 ナノ秒速くなりました%
。
timeit
どれが速いかを示すだけでなく、どれだけ (テストを数回実行するだけで) もわかります。これは通常、それがどれほど重要ではないかを示しています (関数呼び出しのオーバーヘッドが約 400 の場合、10 ナノ秒の違いを本当に気にしますか? ?!-)...
マイクロ最適化は本質的に無関係であるとプログラマーに納得させることは、不可能な作業であることが証明されています.Knuthが書いてから 35 年 (コンピューターは桁違いに高速になっています!) が経ちますが、
約 97% の確率で、わずかな効率性を忘れる必要があります。時期尚早の最適化は諸悪の根源です。
彼が説明したように、これはホーアのさらに古い声明からの引用です. 彼らのケースが残りの 3% に収まると誰もが完全に確信していると思います!
したがって、「それは問題ではない」を際限なく繰り返す代わりに、私たち (特にティム・ピーターズはそこでの栄誉に値する) を標準の Python ライブラリ モジュールに入れましtimeit
た。うーん、このケースは 97% のグループに分類されます!-)
正直に言うと、それは問題ではないと思います。
最初の問題は読みやすさです。他の開発者にとってより意味のあることは何ですか? 個人的には、数値の偶数/奇数をチェックするときにモジュロを期待します。他のほとんどの開発者も同じことを期待していると思います。予想外の別の方法を導入すると、コードの読み取りが難しくなり、保守が難しくなる可能性があります。
2 つ目は、どちらの操作を行ってもおそらくボトルネックになることはないという事実です。私は最適化に賛成ですが、初期の最適化はどの言語や環境でもできる最悪のことです。何らかの理由で、数値が偶数か奇数かを判断することがボトルネックである場合は、問題を解決する最速の方法を見つけてください。しかし、ここで最初のポイントに戻ります。初めてルーチンを作成するときは、できるだけ読みやすい方法で作成する必要があります。
ジョンは良い点を指摘します。実際のオーバーヘッドは関数呼び出しにあります。
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
興味深いことに、関数呼び出しなしで両方のメソッドが同時に remore します。
"num & 1 および True または False を返す" ? うわー!あなたがスピード狂の場合 (1) "return num & 1" (2) インライン: Python 関数呼び出しを回避するため、if somenumber % 2 == 1
判読可能であり、ビートです。isodd(somenumber)
悪意のある最適化とは別に、すべてのコーダーが 2 回見なくても理解できる非常に慣用的な「var % 2 == 0」が取り除かれます。したがって、これはpythons zenにも違反しており、利益はほとんどありません。
さらに、 a = b および True または False は、読みやすくするために置き換えられました。
num & 1 の場合は True を返し、それ以外の場合は False を返します
上記の回答のいずれも、変数のセットアップ (タイミング リテラルは別の話です) と関数の呼び出し (明らかに「下位用語」を隠しています) の両方を行っていないことに本当に驚きました。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