多数の被乗数 (小さな式) を持つ乗法式があるとします。
expression = a*b*c*d*....*w
たとえば、c は (x-1)、d は (y**2-16)、k は (x y-60)..... x、y は数字
で、c、d、k、
jゼロかもしれない
c d k j....*w と書く方が良いですか、それとも python は私が書いた順序に関係なくすべての式を評価しますか?
多数の被乗数 (小さな式) を持つ乗法式があるとします。
expression = a*b*c*d*....*w
たとえば、c は (x-1)、d は (y**2-16)、k は (x y-60)..... x、y は数字
で、c、d、k、
jゼロかもしれない
c d k j....*w と書く方が良いですか、それとも python は私が書いた順序に関係なくすべての式を評価しますか?
Python v2.6.5 はゼロ値をチェックしません。
def foo():
a = 1
b = 2
c = 0
return a * b * c
>>> import dis
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (a)
3 6 LOAD_CONST 2 (2)
9 STORE_FAST 1 (b)
4 12 LOAD_CONST 3 (3)
15 STORE_FAST 2 (c)
5 18 LOAD_FAST 0 (a)
21 LOAD_FAST 1 (b)
24 BINARY_MULTIPLY
25 LOAD_FAST 2 (c)
28 BINARY_MULTIPLY
29 RETURN_VALUE
更新: Baldurの式をテストしました。Pythonは、定数式を含むコードを最適化できます。奇妙なのは、test6
最適化されていないことです。
def test1():
return 0 * 1
def test2():
a = 1
return 0 * a * 1
def test3():
return 243*(5539**35)*0
def test4():
return 0*243*(5539**35)
def test5():
return (256**256)*0
def test6():
return 0*(256**256)
>>> dis.dis(test1) # 0 * 1
2 0 LOAD_CONST 3 (0)
3 RETURN_VALUE
>>> dis.dis(test2) # 0 * a * 1
5 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (a)
6 6 LOAD_CONST 2 (0)
9 LOAD_FAST 0 (a)
12 BINARY_MULTIPLY
13 LOAD_CONST 1 (1)
16 BINARY_MULTIPLY
17 RETURN_VALUE
>>> dis.dis(test3) # 243*(5539**35)*0
9 0 LOAD_CONST 1 (243)
3 LOAD_CONST 5 (104736434394484...681759461305771899L)
6 BINARY_MULTIPLY
7 LOAD_CONST 4 (0)
10 BINARY_MULTIPLY
11 RETURN_VALUE
>>> dis.dis(test4) # 0*243*(5539**35)
12 0 LOAD_CONST 5 (0)
3 LOAD_CONST 6 (104736433252667...001759461305771899L)
6 BINARY_MULTIPLY
7 RETURN_VALUE
>>> dis.dis(test5) # (256**256)*0
15 0 LOAD_CONST 4 (0L)
3 RETURN_VALUE
>>> dis.dis(test6) # 0*(256**256)
18 0 LOAD_CONST 1 (0)
3 LOAD_CONST 3 (323170060713110...853611059596230656L)
6 BINARY_MULTIPLY
7 RETURN_VALUE
つまり、式に変数が含まれる場合、順序は関係ありません。すべてが評価されます。
ベンチマークする前に最適化しようとしないでください。
それを念頭に置いて、中間項がゼロであっても、すべての式が評価されるのは事実です。
順序はまだ重要かもしれません。式は左から右に評価されます。が非常に大きい場合a,b,c,...
、Python に大量のメモリを割り当てさせ、 になる前に計算を遅くする可能性がありますj=0
。(j=0
式の前に来た場合、積はそれほど大きくならず、追加のメモリ割り当ては必要ありません)。
timeitまたはcProfileでコードのタイミングを計った後、これが自分の状況である可能性があると思われる場合は、事前評価c,d,k,j
とテストを試すことができます
if not all (c,d,k,j):
expression = 0
else:
expression = a*b*c*d*....*w
次に、これをtimeit
orcProfile
と合わせて時間を計ります。これがあなたの状況で役立つかどうかを実際に判断する唯一の方法は、ベンチマークすることです。
In [333]: import timeit
In [334]: timeit.timeit('10**100*10**100*0')
Out[334]: 1.2021231651306152
In [335]: timeit.timeit('0*10**100*10**100')
Out[335]: 0.13552498817443848
PyPy ははるかに高速ですが、これも最適化されていないようです。
% pypy-c
Python 2.7.3 (d994777be5ab, Oct 12 2013, 14:13:59)
[PyPy 2.2.0-alpha0 with GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
And now for something completely different: ``http://twitpic.com/52ae8f''
>>>> import timeit
>>>> timeit.timeit('10**100*10**100*0')
0.020643949508666992
>>>> timeit.timeit('0*10**100*10**100')
0.003732919692993164
これは、Python 3.1 での簡単なチェックです。
>>> import timeit
>>> timeit.timeit('243*325*(5539**35)*0')
0.5147271156311035
>>> timeit.timeit('0*243*325*(5539**35)')
0.153839111328125
そしてこれはPython 2.6で:
>>> timeit.timeit('243*325*(5539**35)*0')
0.72972488403320312
>>> timeit.timeit('0*243*325*(5539**35)')
0.26213502883911133
したがって、注文はそれに入ります。
また、Python 3.1 で次の結果を得ました。
>>> timeit.timeit('(256**256)*0')
0.048995018005371094
>>> timeit.timeit('0*(256**256)')
0.1501758098602295
なぜ地球上で?
>>> import timeit
>>> timeit.timeit('1*2*3*4*5*6*7*8*9*9'*6)
0.13404703140258789
>>> timeit.timeit('1*2*3*4*5*6*7*8*9*0'*6)
0.13294696807861328
>>>
おそらくそうではありません。乗算は、すべての中で最も安価な操作の1つです。0の方が速い場合は、前にゼロをチェックする必要があります。これは、乗算を行うよりもおそらく遅いでしょう。
最速の解決策はmultiply.reduce()