2010-07-16 8 views
8

ia supongamos que tienen una expresión multiplicativa con un montón de multiplicandos (expresiones pequeños)¿la expresión multiplicativa de python evalúa más rápido si encuentra un cero?

expression = a*b*c*d*....*w 

donde, por ejemplo c es (x-1), d es (y ** 2-16), K es (x y-60) ..... x, y son números
y sé que c, d, k, j tal vez cero
¿Importa el orden en que escribo la expresión para una evaluación más rápida?
¿Es mejor escribir c
d k j .... * w python evaluará todas las expresiones sin importar el orden en que escriba?

+0

¿Cómo explicar la última sincronización de Baldur? –

Respuesta

7

Python v2.6.5 no comprueba los valores cero.

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   

Actualización: He probadoBaldur 's expresiones, y Python pueden y optimizar el código que involucran expresiones constantes. El extraño es que test6 no está optimizado.

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   

En resumen, si la expresión incluye variables, el orden no importa. Todo será evaluado.

+1

Esto es para constantes, mientras que la pregunta implica multiplicar expresiones. – ShreevatsaR

+0

Esto todavía implica expresiones constantes, por cierto. :-) Creo que las cosas correctas para comparar serían algo así como (digamos) x = 5; y = 12; (x * y-58) * (x * y-59) * (x * y-60) para diferentes ordenamientos de la expresión final (pero con constantes más grandes). – ShreevatsaR

+0

@ShreevatsaR, vamos, ¿'a * b * c' ya no lo demuestra? De todos modos, hice más combinaciones. Realmente, a menos que me esté perdiendo algo, no creo que el compilador incorpore código que compruebe variables para valores cero. El código generado es siempre una serie de instrucciones de carga, multiplicación, adición y sustracción. –

-1

Probablemente no. La multiplicación es una de las operaciones más baratas de todas. Si un 0 debe ser más rápido, entonces sería necesario verificar con ceros antes y probablemente sea más lento que simplemente hacer la multiplicación.

La solución más rápida debe ser multiply.reduce()

+2

Olvidas que los enteros (Python 3.X) y los largos (Python 2.X) son de longitud variable y el tiempo de multiplicación aumenta con la longitud. Entonces '0 * enorme * enorme' correrá mucho más rápido que' enorme * enorme * 0' –

+0

Buen punto, lo olvidé. – Mene

2

>>> 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 
>>> 
5

No trate de optimizar antes de referencia.

Teniendo esto en cuenta, es cierto que todas las expresiones se evaluarán incluso si un término intermedio es cero.

El pedido puede seguir siendo importante. Las expresiones son evaluated from left to right. Si a,b,c,... son números muy grandes, podrían forzar a Python a asignar mucha memoria, ralentizando el cálculo antes de llegar al j=0. (Si j=0 apareció antes en la expresión, entonces el producto nunca sería tan grande y no se necesitaría ninguna asignación de memoria adicional).

Si, después de tiempo de su código con timeit o cProfile, cree que esto puede ser su situación, entonces usted podría tratar de pre-evaluación c,d,k,j, y prueba de

if not all (c,d,k,j): 
    expression = 0 
else: 
    expression = a*b*c*d*....*w 

Luego el tiempo esto con timeit o cProfile y . La única forma de saber realmente si esto es útil en su situación es realizar una evaluación comparativa.

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 

Aunque PyPy es mucho más rápido, que no parece para optimizar este sea:

% 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 
+0

Esto hace que Python parezca realmente estúpido. ¿No debería atrapar cosas como la multiplicación por cero durante la compilación para codificar y optimizarlas? ¿PyPy es mejor en esas cosas? – endolith

+1

@endolith: No, PyPy tampoco optimiza esto. Véase más arriba. – unutbu

+0

Pero en ambos ejemplos, el tiempo se reduce en un orden de magnitud cuando coloca el 0 al principio, ¿no? Supongo que me he perdido algo ... ¿Pueden darme más detalles? – George

4

Esto es solo un control rápido en Python 3.1:

>>> import timeit 
>>> timeit.timeit('243*325*(5539**35)*0') 
0.5147271156311035 
>>> timeit.timeit('0*243*325*(5539**35)') 
0.153839111328125 

y esto en Python 2.6:

>>> timeit.timeit('243*325*(5539**35)*0') 
0.72972488403320312 
>>> timeit.timeit('0*243*325*(5539**35)') 
0.26213502883911133 

Así que la orden no entren en ella.

También me dio este resultado en Python 3.1:

>>> timeit.timeit('(256**256)*0') 
0.048995018005371094 
>>> timeit.timeit('0*(256**256)') 
0.1501758098602295 

¿Por qué en la Tierra?

+0

¿Cuántas veces se ejecuta 'timeit'? –

+0

nevermind ... http://docs.python.org/library/timeit.html?highlight=timeit#timeit.timeit –

+4

El último resultado de tiempo se debe a las capacidades limitadas del optimizador de mirilla de CPython. El bytecode para la expresión '(256 ** 256) * 0' se pliega hasta la constante '0', por lo que su tiempo no incluye ninguna operación aritmética. Para la segunda expresión, el '256 ** 256' se pliega a una constante, pero aún le queda la multiplicación por' 0'. Supongo que esto se debe a que el peepholer funciona esencialmente de izquierda a derecha. –

Cuestiones relacionadas