| You are here: Inicio > Inmersión en Python > Ajustes de rendimiento > Optimización de operaciones con listas | << >> | ||||
Inmersión en PythonPython de novato a experto |
|||||
El tercer paso en el algoritmo Soundex es eliminar dígitos consecutivos duplicados. ¿Cual es la mejor manera de hacerlo?
Éste es el código que hemos usado hasta ahora, en soundex/stage2/soundex2c.py:
digits2 = digits[0]
for d in digits[1:]:
if digits2[-1] != d:
digits2 += d
Éstos son los resultados de rendimiento de soundex2c.py:
C:\samples\soundex\stage2>python soundex2c.py Woo W000 12.6070768771 Pilgrim P426 14.4033353401 Flingjingwaller F452 19.7774882003
Lo primero que debemos considerar es si es eficiente comprobar digits[-1] cada vez dentro del bucle. ¿Son costosos los índices de las listas? ¿Sería mejor mantener el último dígito en una variable aparte y mirar eso en su lugar?
La respuesta a esa pregunta está en soundex/stage3/soundex3a.py:
digits2 = ''
last_digit = ''
for d in digits:
if d != last_digit:
digits2 += d
last_digit = d
soundex3a.py no va nada más rápido que soundex2c.py, e incluso puede que vaya algo más lento (aunque no lo suficiente como para asegurarlo):
C:\samples\soundex\stage3>python soundex3a.py Woo W000 11.5346048171 Pilgrim P426 13.3950636184 Flingjingwaller F452 18.6108927252
¿Por qué no es más rápido soundex3a.py? Resulta que los índices de las listas en Python son extremadamente eficientes. Acceder varias veces a digits2[-1] no es problema. Por otro lado, mantener el último dígito visto en una variable aparte significa que tenemos dos asignaciones de variables por cada dígito que almacenamos, lo que elimina cualquier pequeña ganancia que hubiéramos obtenido por eliminar la búsqueda en la lista.
Probemos algo totalmente diferente. Si es posible tratar una cadena como una lista de caracteres, debería ser posible usar una lista por comprensión para iterar sobre la lista. El problema es que el código necesita acceder al carácter previo en la lista, y no es fácil hacerlo con una lista por comprensión sencilla.
Sin embargo, es posible crear una lista de números índice usando la función range(), y usar esos números de índice para buscar progresivamente en la lista y extraer cada carácter que sea diferente del anterior. Esto nos dará una lista de caracteres y podemos usar el método de cadena join() para reconstruir una cadena partiendo de ella.
Éste es soundex/stage3/soundex3b.py:
digits2 = "".join([digits[i] for i in range(len(digits))
if i == 0 or digits[i-1] != digits[i]])
¿Es más rápido? En una palabra, no.
C:\samples\soundex\stage3>python soundex3b.py Woo W000 14.2245271396 Pilgrim P426 17.8337165757 Flingjingwaller F452 25.9954005327
Es posible que las técnicas hasta ahora hayan sido “centradas en la cadena”. Python puede convertir una cadena en una lista de caracteres con una sola orden: list('abc') devuelve ['a', 'b', 'c']. Más aún, las listas se pueden modificar muy rápidamente. En lugar de crear incrementalmente una nueva lista (o cadena) partiendo de la original, ¿por qué no trabajar con elementos dentro de una única lista?
Éste es soundex/stage3/soundex3c.py, que modifica una lista para eliminar elementos consecutivos duplicados:
digits = list(source[0].upper() + source[1:].translate(charToSoundex))
i=0
for item in digits:
if item==digits[i]: continue
i+=1
digits[i]=item
del digits[i+1:]
digits2 = "".join(digits)
¿Es más rápido que soundex3a.py o soundex3b.py? No, de hecho es el método más lento:
C:\samples\soundex\stage3>python soundex3c.py Woo W000 14.1662554878 Pilgrim P426 16.0397885765 Flingjingwaller F452 22.1789341942
No hemos progresado nada aquí, excepto que hemos probado y eliminado varias técnicas “inteligentes”. El código más rápido que hemos visto hasta ahora fue el método original y más directo (soundex2c.py). Algunas veces ser inteligente no es una ventaja.
import string, re allChar = string.uppercase + string.lowercase charToSoundex = string.maketrans(allChar, "91239129922455912623919292" * 2) isOnlyChars = re.compile('^[A-Za-z]+$').search def soundex(source): if not isOnlyChars(source): return "0000" digits = source[0].upper() + source[1:].translate(charToSoundex) digits2 = digits[0] for d in digits[1:]: if digits2[-1] != d: digits2 += d digits3 = re.sub('9', '', digits2) while len(digits3) < 4: digits3 += "0" return digits3[:4] if __name__ == '__main__': from timeit import Timer names = ('Woo', 'Pilgrim', 'Flingjingwaller') for name in names: statement = "soundex('%s')" % name t = Timer(statement, "from __main__ import soundex") print name.ljust(15), soundex(name), min(t.repeat())
<< Optimización de búsquedas en diccionarios |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
Optimización de manipulación de cadenas >> |