мультипликативное выражение python вычисляется быстрее, если находит ноль? - PullRequest
8 голосов
/ 16 июля 2010

предположим, что у меня есть мультипликативное выражение с множеством мультипликатов (маленьких выражений)

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 оценит все выражения независимо от порядка их записи?

Ответы [ 5 ]

7 голосов
/ 16 июля 2010

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        

Вкратце, если выражение включает переменные, порядок не имеет значения.Все будет оценено.

5 голосов
/ 16 июля 2010

Не пытайтесь оптимизировать, прежде чем тестировать.

Имея это в виду, верно, что все выражения будут оцениваться, даже если промежуточный член равен нулю.

Заказ все еще может иметь значение. Выражения оцениваются слева направо . Если 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 или cProfile. Единственный способ действительно определить, насколько это полезно в вашей ситуации, - это сравнительный анализ.

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
4 голосов
/ 16 июля 2010

Это просто быстрая проверка в 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

Почему на Земле?

2 голосов
/ 16 июля 2010

>>> 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
>>> 
0 голосов
/ 16 июля 2010

Наверное, нет. Умножение является одной из самых дешевых операций из всех. Если 0 должен быть быстрее, тогда нужно будет проверять нули раньше, и это, вероятно, медленнее, чем просто умножение.

Самое быстрое решение должно быть multiply.reduce()

...