Quale algoritmo di ordinamento funziona meglio su dati per lo più ordinati?
Quale algoritmo di ordinamento funziona meglio su dati per lo più ordinati?
Risposte:
Basandomi sul metodo altamente scientifico di guardare gif animate direi che Insertion e Bubble sono buoni candidati.
Solo alcuni elementi => INSERTION SORT
Gli articoli sono per lo più ordinati già => INSERTION SORT
Preoccupato per gli scenari peggiori => HEAP SORT
Interessato ad un buon risultato nel caso medio => QUICKSORT
Gli oggetti vengono estratti da un universo denso => ORDINE DI SECCHIO
Desiderio di scrivere il minor codice possibile => INSERTION SORT
Timsort è "una fusione adattiva, stabile e naturale" con " prestazioni soprannaturali su molti tipi di array parzialmente ordinati (meno di lg (N!) Confronti necessari, e solo N-1)". Python è integratosort()
ha usato questo algoritmo per qualche tempo, apparentemente con buoni risultati. È specificamente progettato per rilevare e sfruttare le sottosequenze parzialmente ordinate nell'input, che spesso si verificano in set di dati reali. Nel mondo reale accade spesso che i confronti siano molto più costosi rispetto allo scambio di elementi in un elenco, poiché in genere si scambiano solo puntatori, il che rende molto spesso timsort una scelta eccellente. Tuttavia, se sai che i tuoi confronti sono sempre molto economici (scrivere un programma giocattolo per ordinare numeri interi a 32 bit, per esempio), esistono altri algoritmi che probabilmente avranno prestazioni migliori. Il modo più semplice per sfruttare timsort è ovviamente usare Python, ma poiché Python è open source potresti anche essere in grado di prendere in prestito il codice. In alternativa, la descrizione sopra contiene dettagli più che sufficienti per scrivere la propria implementazione.
lg(n!)
confronti su un array quasi ordinato, fino al O(n)
! | @behrooz: nessun tipo di confronto può avere un caso medio di meglio di O(n log n)
, ed lg(n!)
è O(n log n)
. Quindi il peggior caso di timsort non è asintoticamente peggiore di quello di qualsiasi altro tipo di confronto. Inoltre, il suo caso migliore è migliore o uguale a qualsiasi altro tipo di confronto.
Ordinamento di inserzione con il seguente comportamento:
k
negli slot 1..n
, verificare innanzitutto se el[k] >= el[k-1]
. In tal caso, vai all'elemento successivo. (Ovviamente salta il primo elemento.)1..k-1
per determinare la posizione di inserimento, quindi scorrere gli elementi sopra. (Si potrebbe fare questo solo se k>T
, dove T
è un certo valore di soglia, con il piccolo k
questo è eccessivo.)Questo metodo effettua il minor numero di confronti.
Prova l'ordinamento introspettivo. http://en.wikipedia.org/wiki/Introsort
È basato su quicksort, ma evita il comportamento peggiore che quicksort ha per elenchi quasi ordinati.
Il trucco è che questo algoritmo di ordinamento rileva i casi in cui quicksort entra nella modalità peggiore e passa all'heap o all'unione dell'ordinamento. Le partizioni quasi ordinate vengono rilevate da un metodo di partizione non naiive e le partizioni piccole vengono gestite utilizzando l'ordinamento per inserzione.
Ottieni il meglio da tutti i principali algoritmi di ordinamento per il costo di più codice e complessità. E puoi essere sicuro che non ti imbatterai mai nel comportamento peggiore, indipendentemente dall'aspetto dei tuoi dati.
Se sei un programmatore C ++ controlla il tuo algoritmo std :: sort. Potrebbe già utilizzare l'ordinamento introspettivo internamente.
Splaysort è un metodo di ordinamento oscuro basato su alberi di visualizzazione , un tipo di albero binario adattivo. Splaysort è valido non solo per i dati parzialmente ordinati, ma anche per quelli parzialmente invertiti, o addirittura per tutti i dati che hanno qualsiasi tipo di ordine preesistente. È O (nlogn) nel caso generale e O (n) nel caso in cui i dati siano ordinati in qualche modo (avanti, indietro, organo a canne, ecc.).
Il suo grande vantaggio rispetto all'ordinamento per inserzione è che non ripristina il comportamento O (n ^ 2) quando i dati non sono ordinati affatto, quindi non è necessario essere assolutamente sicuri che i dati siano parzialmente ordinati prima di utilizzarli .
Il suo svantaggio è lo spazio extra ambientale della struttura di splay di cui ha bisogno, nonché il tempo necessario per costruire e distruggere l'albero di splay. Ma a seconda della dimensione dei dati e della quantità di pre-ordinamento che ci si aspetta, il sovraccarico può valere la pena per l'aumento della velocità.
Un documento su splaysort è stato pubblicato in Software - Practice & Experience.
Il smoothsort di Dijkstra è un ottimo tipo di dati già ordinati. È una variante heapsort che funziona nel caso peggiore O (n lg n) e nel caso migliore O (n). Ho scritto un'analisi dell'algoritmo, nel caso tu sia curioso di sapere come funziona.
Il Mergesort naturale è un altro davvero ottimo per questo: è una variante di Mescesort dal basso che funziona trattando l'input come la concatenazione di più intervalli ordinati diversi, quindi usando l'algoritmo di unione per unirli. Ripetete questo processo fino a quando non viene ordinato tutto l'intervallo di input. Questo viene eseguito nel tempo O (n) se i dati sono già ordinati e nel caso peggiore O (n lg n). È molto elegante, anche se in pratica non è buono come altri tipi adattivi come Timsort o smoothsort.
L'ordinamento per inserzione richiede tempo O (n + il numero di inversioni).
Un'inversione è una coppia (i, j)
tale i < j && a[i] > a[j]
. Cioè, una coppia fuori servizio.
Una misura di essere "quasi ordinati" è il numero di inversioni --- si potrebbe prendere "dati quasi ordinati" per indicare i dati con poche inversioni. Se uno sa che il numero di inversioni è lineare (ad esempio, hai appena aggiunto elementi O (1) a un elenco ordinato), l'ordinamento per inserzione impiega O (n) tempo.
Come tutti gli altri hanno detto, fai attenzione all'ingenuo Quicksort, che può avere prestazioni O (N ^ 2) su dati ordinati o quasi ordinati. Tuttavia, con un algoritmo appropriato per la scelta del pivot (casuale o mediana di tre - vedi Scelta di un pivot per Quicksort ), Quicksort continuerà a funzionare in modo sano.
In generale, la difficoltà con la scelta di algoritmi come inserire l'ordinamento sta nel decidere quando i dati sono sufficientemente fuori servizio che Quicksort sarebbe davvero più veloce.
Non farò finta di avere tutte le risposte qui, perché penso che per ottenere le risposte effettive potrebbe essere necessario codificare gli algoritmi e profilarli su campioni di dati rappresentativi. Ma ho pensato a questa domanda tutta la sera, ed ecco cosa mi è successo finora, e alcune ipotesi su cosa funzioni meglio dove.
Sia N il numero totale di articoli, M il numero fuori servizio.
L'ordinamento delle bolle dovrà far passare qualcosa come 2 * M + 1 attraverso tutti gli N elementi. Se M è molto piccolo (0, 1, 2?), Penso che questo sarà molto difficile da battere.
Se M è piccolo (diciamo meno del log N), l'ordinamento per inserzione avrà prestazioni medie eccezionali. Tuttavia, a meno che non ci sia un trucco che non vedo, avrà prestazioni pessime nel caso peggiore. (Giusto? Se l'ultimo elemento nell'ordine viene prima, allora devi inserire ogni singolo elemento, per quanto posso vedere, che ucciderà le prestazioni.) Immagino che ci sia un algoritmo di ordinamento più affidabile là fuori per questo caso, ma non so cosa sia.
Se M è più grande (diciamo uguale o grande del log N), l'ordinamento introspettivo è quasi sicuramente il migliore.
Eccezione a tutto ciò: se in realtà sai in anticipo quali elementi non sono ordinati, la soluzione migliore sarà quella di estrarre quegli elementi, ordinarli usando l'ordinamento introspettivo e unire i due elenchi ordinati in un elenco ordinato. Se potessi capire rapidamente quali articoli sono fuori servizio, anche questa sarebbe una buona soluzione generale, ma non sono stato in grado di trovare un modo semplice per farlo.
Ulteriori pensieri (durante la notte): Se M + 1 <N / M, è possibile eseguire la scansione dell'elenco alla ricerca di una corsa di N / M in una riga che sono ordinate, quindi espandere quella corsa in entrambe le direzioni per trovare il oggetti ordinati. Ciò richiederà al massimo i confronti 2N. È quindi possibile ordinare gli elementi non ordinati ed eseguire un'unione ordinata nei due elenchi. I confronti totali dovrebbero essere inferiori a qualcosa come 4N + M log2 (M), che sta per battere qualsiasi routine di ordinamento non specializzata, credo. (Ancora di più: questo è più complicato di quanto pensassi, ma continuo a pensare che sia ragionevolmente possibile.)
Un'altra interpretazione della domanda è che potrebbero esserci molti articoli fuori servizio, ma sono molto vicini a dove dovrebbero essere nell'elenco. (Immagina di iniziare con un elenco ordinato e di scambiare ogni altro oggetto con quello che viene dopo di esso.) In quel caso penso che l'ordinamento a bolle funzioni molto bene - Penso che il numero di passaggi sarà proporzionale al più lontano fuori posto di un oggetto è. L'ordinamento per inserzione funzionerà male, perché ogni articolo fuori ordine attiverà un inserimento. Sospetto che l'ordinamento introspettivo o qualcosa del genere funzionerà bene.
Se hai bisogno di un'implementazione specifica per algoritmi di ordinamento, strutture di dati o qualsiasi cosa che abbia un collegamento a quanto sopra, potrei consigliarti l'eccellente progetto "Strutture di dati e algoritmi" su CodePlex?
Avrà tutto il necessario senza reinventare la ruota.
Solo il mio piccolo granello di sale.
Questa bella raccolta di algoritmi di ordinamento per questo scopo nelle risposte, sembra mancare Gnome Sort , che sarebbe anche adatto, e probabilmente richiede il minimo sforzo di implementazione.
medita Prova Heap. Credo che sia il più coerente tra i tipi O (n lg n).
L'ordinamento delle bolle (o, ancora più sicuro, dell'ordinamento delle bolle bidirezionale) è probabilmente l'ideale per gli elenchi per lo più ordinati, anche se scommetto che un ordinamento a pettine ottimizzato (con una dimensione del gap iniziale molto più bassa) sarebbe un po 'più veloce quando l'elenco non era' t altrettanto perfettamente ordinato. Comb ordinamento degrada in bolla-ordinamento.
bene dipende dal caso d'uso. Se sai quali elementi vengono modificati, rimuovere e inserire sarà il caso migliore per quanto mi riguarda.
L'ordinamento a bolle è sicuramente il vincitore Il prossimo sul radar sarebbe l'ordinamento per inserzione.
Tenere lontano da QuickSort - è molto inefficiente per i dati preordinati. L'ordinamento per inserzione gestisce bene i dati quasi ordinati spostando il minor numero possibile di valori.