C'è un modo per misurare quanto è ordinato un elenco?


161

C'è un modo per misurare quanto è ordinato un elenco?

Voglio dire, non si tratta di sapere se un elenco è ordinato o meno (booleano), ma qualcosa di simile a un rapporto di "ordinamento", qualcosa di simile al coefficiente di correlazione nelle statistiche.

Per esempio,

  • Se gli elementi di un elenco sono in ordine crescente, la sua velocità sarebbe 1.0

  • Se l'elenco è ordinato in ordine decrescente, la sua velocità sarebbe -1.0

  • Se la lista è quasi ordinata in ordine crescente, la sua velocità sarebbe 0,9 o un valore vicino a 1.

  • Se l'elenco non è ordinato (casuale), il suo tasso sarebbe vicino a 0

Sto scrivendo una piccola biblioteca alla Scala per fare pratica. Penso che un tasso di smistamento sarebbe utile, ma non trovo alcuna informazione su qualcosa del genere. Forse non conosco termini adeguati per il concetto.



4
Questo sarebbe usato per determinare l'algoritmo ideale per ordinare l'elenco? Ad esempio per valori vicini a 0, QuickSort sarebbe l'ideale, ma valori su entrambe le estremità della scala (quasi ordinati o quasi in ordine inverso), MergeSort sarebbe molto più veloce, poiché in questi casi QC si trasforma in O (N ^ 2).
Darrel Hoffman,

8
+1 per "ratio of sortess"
0x499602D2

1
@Fuhrmanator La versione stocastica dell'algoritmo non deve eseguire un ordinamento per arrivare a una stima probabilistica dell'ordinamento. È solo se si desidera ottenere una misura esatta che è necessario eseguire un ordinamento.
Timothy Shields,

1
Primo istinto sarcastico ma divertente: è possibile inserire l'ordinamento dell'elenco e vedere quanto tempo impiega, quindi confrontarlo con il tempo impiegato per ordinare (l'elenco ora ordinato) e viceversa.
kqr

Risposte:


142

Puoi semplicemente contare il numero di inversioni nell'elenco.

Inversione

Un'inversione in una sequenza di elementi di tipo Tè una coppia di elementi di sequenza che appaiono fuori ordine secondo alcuni ordinamenti <sull'insieme di T's.

Da Wikipedia :

Formalmente, A(1), A(2), ..., A(n)sia una sequenza di nnumeri.
Se i < je A(i) > A(j), la coppia (i,j)viene chiamata inversione di A.

Il numero di inversione di una sequenza è una misura comune della sua ordinamento.
Formalmente, il numero di inversione è definito come il numero di inversioni, ovvero

definizione

Per rendere più chiare queste definizioni, considerare la sequenza di esempio 9, 5, 7, 6. Questa sequenza ha le inversioni (0,1), (0,2), (0,3), (2,3) e il numero di inversione 4 .

Se si desidera un valore tra 0e 1, è possibile dividere il numero di inversione per N choose 2.

Per creare effettivamente un algoritmo per calcolare questo punteggio per quanto è ordinato un elenco, hai due approcci:

Approccio 1 (deterministico)

Modifica il tuo algoritmo di ordinamento preferito per tenere traccia di quante inversioni corregge durante l'esecuzione. Anche se questo non è banale e ha implementazioni variabili a seconda dell'algoritmo di ordinamento che scegli, finirai con un algoritmo che non è più costoso (in termini di complessità) rispetto all'algoritmo di ordinamento che hai iniziato.

Se segui questa strada, tieni presente che non è semplice come contare gli "swap". Mergesort, ad esempio, è il caso peggiore O(N log N), ma se viene eseguito in un elenco ordinato in ordine decrescente, correggerà tutte le N choose 2inversioni. Sono O(N^2)inversioni corrette nelle O(N log N)operazioni. Quindi alcune operazioni devono inevitabilmente correggere più di una inversione alla volta. Devi stare attento con la tua implementazione. Nota: puoi farlo con O(N log N)complessità, è solo complicato.

Correlato: calcolo del numero di "inversioni" in una permutazione

Approccio 2 (stocastico)

  • Campionare casualmente coppie (i,j), dovei != j
  • Per ogni coppia, determinare se list[min(i,j)] < list[max(i,j)](0 o 1)
  • Calcola la media di questi confronti e poi normalizza per N choose 2

Personalmente seguirei l'approccio stocastico a meno che tu non abbia un requisito di precisione, se non altro perché è così facile da implementare.


Se quello che vuoi veramente è un valore ( z') tra -1(in ordine decrescente) a 1(in ordine crescente), puoi semplicemente mappare il valore sopra ( z), che è tra 0(in ordine crescente) e 1(in ordine decrescente), a questo intervallo usando questa formula :

z' = -2 * z + 1

2
Per me è piuttosto affascinante che l'ordinamento di un elenco sia (tipicamente) O (n * logn) e che il metodo ingenuo / ovvio per calcolare le inversioni sia O (n ^ 2). Mi chiedo se ci sono algoritmi migliori là fuori per calcolare il numero di inversioni?
Mark Bessey,

5
Ci sono un paio di approcci interessanti in questa domanda SO: stackoverflow.com/questions/6523712/… Fondamentalmente, equivalgono a ordinare l'array per capire quante inversioni ci sono.
Mark Bessey l'

4
Pensavo ingenuamente che potevi contare solo coppie adiacenti fuori servizio. Ma ciò sarà gravemente sottostimato: 1 2 3 1 2 3 ha solo un'inversione adiacente, ma è invertita del 50% dalla misura più corretta.
Barmar,

2
@Barmar Penso che la lista 1 2 3 1 2 3 si qualificherebbe come ordinata ;-)
scunliffe,

2
@TimothyShields, beh, no, non lo è. Ma non voglio affermare il punto. Solo un suggerimento per aggiungere una definizione non formale che è più accessibile ai meno inclini simbolicamente.
Chris Calo,

24

La misura tradizionale di come è ordinata una lista (o altra struttura sequenziale) è il numero di inversioni.

Il numero di inversioni è il numero di coppie (a, b) st indice di a <b AND b <<a. Per questi scopi <<rappresenta qualsiasi relazione di ordinamento scelta per il proprio ordinamento.

Un elenco completamente ordinato non ha inversioni e un elenco completamente invertito ha il numero massimo di inversioni.


5
Tecnicamente, 5 4 3 2 1è completamente ordinato poiché l'ordine non è specificato, ma sono pedante :-)
paxdiablo

7
@paxdiablo Dipende dalla definizione di <.
Marcin

@paxdiablo, si potrebbe misurare l'ordinamento in base alla distanza dal numero di inversioni al più vicino di 0 o n choose 2.
huon,

17

È possibile utilizzare la correlazione effettiva.

Supponiamo che a ciascun elemento nell'elenco ordinato, assegni un valore intero a partire da zero. Si noti che un grafico dell'indice di posizione degli elementi rispetto al rango apparirà come punti in una linea retta (correlazione di 1,0 tra posizione e rango).

È possibile calcolare una correlazione su questi dati. Per un ordinamento inverso otterrai -1 e così via.


1
Mi dispiace, ma questo lascia troppo inspiegabile, come il modo in cui assegni gli interi.
Marcin

2
È necessario l'elenco ordinato per assegnare gli interi; quindi è solo un'enumerazione degli articoli.
Kaz,

1
Esattamente quello che stavo per suggerire. Determinare la correlazione tra la posizione dell'oggetto nell'elenco originale e la sua posizione nell'elenco ordinato. La cattiva notizia è che le routine di correlazione probabilmente funzionano in O (n ^ 2); la buona notizia è che sono probabilmente pronti all'uso per il tuo ambiente.
Peter Webb,

2
Sì, solo il rho di Spearman en.wikipedia.org/wiki/…
Lucas,

Sono curioso ... questo approccio equivale a ridimensionare il conteggio del numero di inversioni?
Clayton Stanley,

4

Ci sono state grandi risposte e vorrei aggiungere un aspetto matematico per completezza:

  • È possibile misurare quanto è ordinato un elenco misurando quanto è correlato a un elenco ordinato. Per fare ciò, puoi usare la correlazione di rango (la più nota è quella di Spearman ), che è esattamente la stessa della solita correlazione, ma usa il rango di elementi in un elenco invece dei valori analogici dei suoi elementi.

  • Esistono molte estensioni, come un coefficiente di correlazione (+1 per l'ordinamento esatto, -1 per l'inversione esatta)

  • Ciò consente di avere proprietà statistiche per questa misura, come il teorema del limite centrale permutazionale, che consente di conoscere la distribuzione di questa misura per elenchi casuali.


3

A parte il conteggio delle inversioni, per gli elenchi numerici è immaginabile una distanza quadrata media dallo stato ordinato:

#! ruby
d = -> a { a.zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case

Penso che sia il quadrato della funzione di correlazione standard, vedi en.wikipedia.org/wiki/Correlation_ratio . E si applica ugualmente agli elenchi non numerici; i due valori che vengono confrontati sono la posizione dell'oggetto nei due elenchi.
Peter Webb,

Sono un semplice. Non so nemmeno quale sia il rapporto di correlazione. Quando leggo l'articolo di Wikipedia, proprio in alto, mi viene chiesto di sapere cos'è la "dispersione statistica", quindi "deviazione standard", quindi "variazione", quindi "coefficiente di correlazione tra le classi". Ho imparato tutto questo, più volte e più volte, l'ho dimenticato di nuovo. In questa mia pragmatica risposta, misuro semplicemente la distanza tra i due vettori con il teorema di Pitagora, che ricordo dalla scuola elementare, tutto qui.
Boris Stitnicky,

1

Non sono sicuro del metodo "migliore", ma un semplice sarebbe quello di confrontare ogni elemento con quello successivo, incrementando un contatore se element2> element 1 (o qualunque cosa tu voglia testare) e poi dividere per il numero totale di elementi. Dovrebbe darti una percentuale.


1

Conterrei i confronti e li dividerei per il numero totale di confronti. Ecco un semplice esempio di Python .

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result

0

Che ne dici di questo?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()

2
Questo conta solo inversioni adiacenti. Se guardi le altre risposte vedrai che questo è insufficiente.
Konrad Rudolph,

1
@KonradRudolph: Penso che questa risposta soddisfi la domanda posta. Il fatto che altre risposte siano più complete non significa che questa sia insufficiente; dipende dai requisiti del PO.
LarsH,

0

Se prendi la tua lista, calcoli le classifiche dei valori in quella lista e chiami la lista delle classifiche Ye un'altra lista, Xche contiene gli interi da 1a length(Y), puoi ottenere esattamente la misura di ordinamento che stai cercando calcolando il coefficiente di correlazione , rtra le due liste.

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

Per un elenco completamente ordinato r = 1.0, per un elenco in ordine inverso r=-1.0, e le rvariazioni tra questi limiti per vari gradi di ordinamento.

Un possibile problema con questo approccio, a seconda dell'applicazione, è che il calcolo del rango di ciascun elemento nell'elenco equivale a ordinarlo, quindi è un'operazione O (n log n).


Ma questo non ignorerà la forma della curva. Se il suo array è ordinato, ma, diciamo, contiene valori che aumentano in modo esponenziale, la correlazione sarà piccola dove vuole che sia 1.0.
Lee Daniel Crocker,

@LeeDanielCrocker: Sì, è un buon punto. Ho modificato la mia risposta per risolvere questo problema prendendo le classifiche dei valori.
Simon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.