ordinamento utilizzando una definizione personalizzata di "" e "" in python

5

Supponiamo di avere una funzione personalizzata come

def greater(a, b):
    if (a % b) % 2 == 0:
        return 1
    return 0

Definisce come confrontare due numeri e determinare quale è maggiore. In questo caso, se la funzione restituisce 1 a > b altro, un < b. Posso utilizzare <built-in function sorted> or incorporato per creare una matrice ordinata di numeri rispetto a questa definizione?

    
posta user5198 01.06.2012 - 00:40
fonte

2 risposte

6

Se stai usando Python 2.x, allora è facilmente ottenibile usando cmp , anche se devi modificare la tua funzione per restituire -1 invece di 0. Qualcosa del genere:

def greater(a, b):
    if (a % b) % 2 == 0:
        return 1
    return -1

x = [2,7,5,10,30,15]

print(sorted(x, cmp=greater))

Ma se usi Python 3.x, diventa un po 'più complicato da quando cmp è stato rimosso. La mia migliore idea è quella di implementare una classe per contenere il numero e sovrascrivere gli operatori di confronto __lt__ (minore di) e __gt__ (maggiore di). Qualcosa del genere:

class my_int(int):
    def __lt__(a,b):
        return (a % b) % 2 != 0
    def __gt__(a,b):        
        return (a % b) % 2 == 0

x = [my_int(2),my_int(7),my_int(5),my_int(10),my_int(30),my_int(15)]

print (sorted(x))
    
risposta data 01.06.2012 - 02:01
fonte
4

No.

La tua funzione non fornisce risposte coerenti su come ordinare i numeri, perché greater(x, y) != not greater(y, x) per alcuni valori di x e y. Ad esempio (3 % 4) % 2 == 1 e (4 % 3) % 2 == 1 anche! Ciò significa che 3 > 4 AND 3 < 4 , che è una sciocchezza. Se lo converti in una funzione in stile cmp come suggerisce @SystemDown, otterrai risultati incoerenti. Ad esempio:

>>> sorted(range(3, 6), cmp=greater)
[5, 4, 3]
>>> sorted(range(1, 6), cmp=greater)
[1, 4, 3, 5, 2]

Qui 5 è più piccolo di 4 in un caso ma più grande di 4 in un altro, usando la stessa funzione di comparazione!

Anche se la tua funzione cmp fosse coerente, ti consiglio di non utilizzarla in alcun modo o forma, ad esempio usando la funzione cmp_to_key suggerita da @zstewart, perché una funzione cmp è più lenta da leggere e eseguire il debug di più una funzione chiave equivalente.

Usando le funzioni chiave, la funzione viene chiamata una sola volta per elemento da ordinare, quindi se hai un elenco di 1 milione di elementi, viene chiamato un milione di volte. Quindi lo smistamento procede e confronta i valori di ritorno della funzione chiave n*log(n) volte, in media. Per un elenco di 1 milione di elementi, vale 13,8 milioni di volte.

Se invece usi una funzione cmp, viene chiamata una volta per ognuno di quei confronti n*log(n) ! Di nuovo, questo è 13,8 milioni di volte se la lista contiene 1 milione di elementi. Se utilizzi cmp_to_key , otterrai un ulteriore sovraccarico di creazione di istanze di 1 milione di istanze di una classe helper.

Ho provato con un elenco di 1 milione di numeri casuali usando queste funzioni, che hanno lo stesso risultato:

def cmp_func(a, b):
    if a > b:
        return -1
    elif a < b:
        return 1
    else:
        return 0

def key_func(x):
    return -x

L'ordinamento con key_func ha richiesto 1,35 s, mentre è stato utilizzato cmp_func richiesto 2,43 s.

Quindi per rispondere alla domanda implicita di @lzkata, è stata ritenuta una buona idea rimuovere l'argomento cmp da ordinare in 3.x perché

    Le funzioni
  • cmp sono inclini a produrre risultati incoerenti in modi sottili, risultanti in bug sottili e difficili da trovare
  • Le funzioni
  • cmp sono più lente
  • è quasi sempre possibile convertire una funzione cmp in una funzione chiave con prestazioni migliori
risposta data 26.04.2016 - 12:19
fonte

Leggi altre domande sui tag