NumPyのndarray.all()やany()は短絡評価などをしてなさそう。
NumPyのall()やany()には短絡評価が実装されているのだろうと勝手に思っていた。
しかしそれはなさそうだと、実験によって確かめた記事です。
NumpyExercises 100
numpy-100/100_Numpy_exercises.md at master · rougier/numpy-100 · GitHub
の60において、0が並んでいるか?を確かめるために、以下のようなコードを書いた。
import numpy as np import time as time N = 100000000 a = np.random.rand(N, 1) start_time = time.time() print(0 in np.count_nonzero(a, axis=1)) end_time = time.time() print(end_time-start_time) start_time = time.time() print(~a.all(axis=1).any()) end_time = time.time() print(end_time-start_time)
乱数関数が1億もの0を出す確率はほぼ0であるため、高確率でどちらもFalseを出力する。
そして、どの行も高確率で最初の要素が0ではなく、その時点でFalseであることが確定する。
この性質を用いた評価は短絡評価と呼ばれている。
N = 10においてそれぞれの時間は
0.1001 ms
0.0231 ms
N = 1000において
0.1121 ms
0.1159 ms
N = 100万において
5.095 ms
0.9420 ms
N = 1億において
617.5 ms
92.59 ms
であり、傾向としてはallやany関数を用いた方が高速ではあった。
しかし要素数が100万から1億へと変化しても、時間は100倍となり、おそらく短絡評価は行われていない。
本題ではないが、「count_nonzeroと~aで反転する操作のどちらが高速か?」は自明ではないが、countが遅いようだ。
ちなみに
np.random.rand(100,1000000)では
244.2 ms
80.15 ms
np.random.rand(1000000, 100)では
216.1 ms
84.20 ms
となり余りメモリのアクセスに影響されていなかった。
更に
np.random.rand(1000000000, 1)において
z = (a==0) start_time = time.time() print(z.all(axis=0).any()) end_time = time.time() print(end_time-start_time)
としてみた。
z = (a==0)には961.1 msかかっているが、
先ほどの2種類が
1894 ms(count_nonzero)
747.1 ms(~a.all(axis=1).any())
であるのに対し
25.58 ms
で実行した。
しかし、
なぜなら
N=10000で
0.0001693 ms
N=1000000で
0.5000 ms
となったため、単にboolの演算が高速なだけや、notを使っていないからだと思われる。
つまり、おそらく短絡評価は行われていない。