Sí, la solución de Venkatesh Kumar funcionará bastante bien, especialmente porque la función de clasificación de fusión solo necesita editarse en dos líneas.
Pero tal vez la próxima vez desee contar comparaciones en algún otro algoritmo de clasificación. Y tal vez sea un algoritmo tan complicado que no quiera profundizar en él y encontrar todos los lugares donde podría estar haciendo una comparación y editando. Quizás desee escribir algún código para contar las comparaciones de una vez por todas y nunca tener que pensarlo nuevamente o incluso mirar el código del algoritmo de clasificación que está probando.
Podemos hacer todas estas cosas cambiando de usar números / objetos regulares a nuestros propios objetos personalizados de conteo de comparación. Podemos envolver cualquier objeto que se pueda comparar. Aquí está el contenedor:
- Dada una lista enlazada circular, ¿cómo encuentro la secuencia más larga de nodos de valor no repetido?
- ¿Está sesgado el algoritmo de aleatorización del Reproductor de Windows Media?
- ¿Cuáles son las partes fáciles de la informática y cuáles son las partes difíciles?
- Después de que termina el programa de programación (estructuras de datos y algoritmo), ¿qué se les enseña a los estudiantes en CSE después de eso?
- ¿Cuál crees que es el algoritmo de optimización más inteligente?
clase CompCounter:
def __init __ (self, obj, counter):
self.obj = obj
self.counter = counter
def __le __ (self, otro):
contador [0] + = 1
if hasattr (self.obj, ‘__ le__’):
return self.obj .__ le __ (otro)
elif hasattr (otro, ‘__ ge__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ ge __ (self.obj)
más:
return self.obj .__ cmp __ (otro) <= 0
def __lt __ (self, otro):
contador [0] + = 1
print self.obj, otro
if hasattr (self.obj, ‘__ lt__’):
return self.obj .__ lt __ (otro)
elif hasattr (otro, ‘__ gt__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ gt __ (self.obj)
más:
return self.obj .__ cmp __ (otro) <0
def __ge __ (self, otro):
contador [0] + = 1
if hasattr (self.obj, ‘__ ge__’):
return self.obj .__ ge __ (otro)
elif hasattr (otro, ‘__ le__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ le __ (self.obj)
más:
return self.obj .__ cmp __ (otro)> = 0
def __gt __ (self, otro):
contador [0] + = 1
if hasattr (self.obj, ‘__ gt__’):
return self.obj .__ gt __ (otro)
elif hasattr (otro, ‘__ lt__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ lt __ (self.obj)
más:
return self.obj .__ cmp __ (otro)> 0
def __eq __ (self, otro):
contador [0] + = 1
if hasattr (self.obj, ‘__ eq__’):
return self.obj .__ eq __ (otro)
elif hasattr (otro, ‘__ eq__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ eq __ (self.obj)
más:
return self.obj .__ cmp __ (otro) == 0
def __ne __ (self, otro):
contador [0] + = 1
if hasattr (self.obj, ‘__ ne__’):
return self.obj .__ ne __ (otro)
elif hasattr (otro, ‘__ ne__’):
si el tipo (otro) es CompCounter:
contador [0] – = 1
devolver otro .__ ne __ (self.obj)
más:
return self.obj .__ cmp __ (otro)! = 0
def __str __ (self):
return self.obj .__ str __ ()
def __repr __ (self):
return self.obj .__ repr __ ()
Como puede ver, este contenedor no sabe nada sobre cómo comparar objetos. Simplemente delega la comparación real al objeto que envuelve *. Pero antes de eso incrementa un contador compartido.
Para usarlo, primero tendremos que envolver todas las cosas que estamos clasificando y luego, antes de terminar, desenvolverlas nuevamente . Podemos hacer esto fácilmente con un simple decorador:
decorador de importación
def countSort (contador):
@ decorator.decorator
def actualDecorator (sortfunc, * args, ** kwargs):
origList = args [0]
para i en rango (len (origList)):
origList [i] = CompCounter (origList [i], contador)
sortfunc (* args, ** kwargs)
para i en rango (len (origList)):
origList [i] = origList [i] .obj
volver actualDecorator
Tenga en cuenta que esto supone que la función de clasificación ajustada desea su lista en el primer argumento posicional y supone que modifica la lista en el lugar. Aquí debería usarse un algoritmo diferente (más simple) para envolver funciones que, como “ordenadas” de Python, no mutan sus argumentos.
Ahora, contar las comparaciones en el algoritmo de clasificación considerado es tan fácil como …
contador = [0]
@countedSort (contador)
def mergeSort (aList):
…y así…
O si el algoritmo está en un módulo diferente y se ha importado …
importar clasificaciones
contador = [0]
mergeSort = countedSort (contador) (sortingalgs.mergeSort)
¡Ni siquiera tenemos que tocar el código de la biblioteca!
Tenga en cuenta que el contador que estamos usando no es un int, sino una lista que contiene solo un int. Esto garantiza que cada objeto envuelto obtenga una copia del mismo contador, ya que las listas se pasan por referencia y mutables.
Ejecutar este código exacto en el ejemplo provisto con la implementación de clasificación de fusión produce un recuento de 82 comparaciones.
Nota:
* O lo delega al otro objeto … hay mucha lógica adicional aquí para garantizar que esto funcione en Python 2, donde el tipo int incorporado no tiene __le __ () y amigos definidos, y para ordenar cosas que no son solo ints. En este momento no puedo probarlo en Python 3 debido a algunas rarezas con mi entorno que me impiden instalar el módulo decorador. Si alguien quiere que funcione también en 3, con gusto lo actualizaré. Idealmente, este sería un código independiente de la versión. El código único de Python 3 o Python 2 podría usar un objeto contenedor mucho más pequeño.