Cómo agregar un contador de comparación para combinar la clasificación en Python

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:

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.

Probablemente hay muchas formas de hacerlo, pero esto es lo que consideraría la forma más fácil.

Primero, cree una variable de contador en alguna parte:

contador = 0

Definir una función comparar:

def compare (número1, número2):
#global significa que modifica el valor global
contador global
contador = contador + 1
return (número1

Muy bien, ¿ves lo que hace esto? Devuelve verdadero si número1

Entonces, ahora solo reemplace todas las instancias de (algo

P.ej:

mientras que i

se convierte

mientras compara (i, len (lefthalf)) y compare (j, len (righthalf)):

¡Y tu estas listo!

Advertencias:

  • No he probado el código, solo lo escribí aquí. Es posible que desee probarlo usted mismo, pero esto debería ayudarlo a obtener la idea clave (es decir, una función de envoltura alrededor del operador
  • La tercera línea del código vinculado tiene un signo>. Esto no funcionará muy bien con nuestra comparación. Sin embargo, tienes algunas opciones. Puede ajustar la convención correctamente y hacer que el retorno de comparación sea negativo si el primero es menor, 0 si son iguales y positivo si el segundo es menor. (es decir: return (número1 – número2) en lugar de (número1

mientras compara (i, len (izquierda)) <0 y compara (j, len (derecha)) <0:

  • Alternativamente, puede contar manualmente en ese caso en la línea 3 incrementando el contador.