Perché Quicksort è meglio di altri algoritmi di ordinamento in pratica?


308

In un corso di algoritmi standard ci viene insegnato che quicksort è in media e nel peggiore dei casi. Allo stesso tempo, vengono studiati altri algoritmi di ordinamento che sono nel peggiore dei casi (come mergesort e heapsort ), e persino il tempo lineare nel migliore dei casi (come bubblesort ) ma con qualche necessità aggiuntiva di memoria.O ( n 2 ) O ( n registro n )O(nlogn)O(n2)O(nlogn)

Dopo una rapida occhiata ad alcuni altri tempi di esecuzione , è naturale dire che quicksort non dovrebbe essere efficiente come altri.

Inoltre, considera che gli studenti imparano nei corsi di programmazione di base che la ricorsione non è davvero buona in generale perché potrebbe usare troppa memoria, ecc. Pertanto (e anche se questo non è un vero argomento), questo dà l'idea che Quicksort potrebbe non essere davvero buono perché è un algoritmo ricorsivo.

Perché, quindi, quicksort supera in pratica altri algoritmi di ordinamento? Ha a che fare con la struttura dei dati del mondo reale ? Ha a che fare con il modo in cui la memoria funziona nei computer? So che alcuni ricordi sono molto più veloci di altri, ma non so se questo è il vero motivo di questa performance contro-intuitiva (rispetto alle stime teoriche).


Aggiornamento 1: una risposta canonica sta dicendo che le costanti coinvolte nella del caso medio sono più piccole delle costanti coinvolte in altri algoritmi . Tuttavia, devo ancora vedere una giustificazione adeguata di ciò, con calcoli precisi anziché solo idee intuitive.O ( n registro n )O(nlogn)O(nlogn)

In ogni caso, sembra che la vera differenza si verifichi, come suggeriscono alcune risposte, a livello di memoria, in cui le implementazioni sfruttano la struttura interna dei computer, utilizzando, ad esempio, che la memoria cache è più veloce della RAM. La discussione è già interessante, ma mi piacerebbe comunque vedere maggiori dettagli riguardo alla gestione della memoria, poiché sembra che la risposta abbia a che fare con essa.


Aggiornamento 2: Esistono diverse pagine Web che offrono un confronto tra algoritmi di ordinamento, alcuni più elaborati di altri (in particolare sorting-algorithms.com ). Oltre a presentare un valido aiuto visivo, questo approccio non risponde alla mia domanda.


2
Unisci ordinamento è nel peggiore dei casi, e l'ordinamento di un array di numeri interi in cui esiste un limite noto sulla dimensione degli interi può essere eseguito in tempo con un ordinamento di conteggio. O ( n )O(nlogn)O(n)
Carl Mummert,

13
sorting-algorithms.com ha un confronto abbastanza approfondito degli algoritmi di ordinamento.
Joe

2
Aggiornamento annuncio 1: suppongo che tu possa avere analisi rigorose o ipotesi realistiche. Non ho visto entrambi. Ad esempio, la maggior parte delle analisi formali conta solo i confronti.
Raffaello

9
Questa domanda ha vinto un recente concorso su programmers.SE !
Raffaello

3
Domanda interessante. Qualche tempo fa ho eseguito alcuni test con dati casuali e un'implementazione ingenua di ordinamento rapido e di tipo merge. Entrambi gli algoritmi hanno funzionato abbastanza bene per piccoli set di dati (fino a 100000 articoli) ma dopo quell'ordinamento si è rivelato molto meglio. Ciò sembra contraddire l'assunto generale secondo cui l'ordinamento rapido è così buono e non ho ancora trovato una spiegazione per questo. L'unica idea che mi è venuta in mente è che normalmente il termine ordinamento rapido viene utilizzato per algoritmi più complessi come l'intro ordinamento e che l'implementazione ingenua dell'ordinamento rapido con pivot casuale non è così buona.
Giorgio,

Risposte:


215

Risposta breve

L'argomento sull'efficienza della cache è già stato spiegato in dettaglio. Inoltre, c'è un argomento intrinseco, perché Quicksort è veloce. Se implementato come con due "puntatori a incrocio", ad esempio qui , i circuiti interni hanno un corpo molto piccolo. Poiché questo è il codice eseguito più spesso, questo paga.

Risposta lunga

Prima di tutto,

Il caso medio non esiste!

Poiché il caso migliore e il caso peggiore spesso si verificano raramente nella pratica, viene eseguita un'analisi media del caso. Ma qualsiasi analisi del caso medio presuppone una certa distribuzione degli input ! Per l'ordinamento, la scelta tipica è il modello di permutazione casuale (assunto tacitamente su Wikipedia).

Perché -Notation?O

L'eliminazione delle costanti nell'analisi degli algoritmi viene eseguita per un motivo principale: se sono interessato a tempi di esecuzione esatti , ho bisogno di costi (relativi) di tutte le operazioni di base coinvolte (anche ignorando ancora i problemi di cache, pipeline nei processori moderni ...). L'analisi matematica può contare la frequenza con cui viene eseguita ciascuna istruzione, ma i tempi di esecuzione di singole istruzioni dipendono dai dettagli del processore, ad esempio se una moltiplicazione di numeri interi a 32 bit impiega tanto tempo quanto l'addizione.

Ci sono due modi per uscire:

  1. Risolvi un modello di macchina.

    Questo viene fatto nella serie di libri di Don Knuth "The Art of Computer Programming" per un computer "tipico" artificiale inventato dall'autore. Nel volume 3 trovi risultati esatti di casi medi per molti algoritmi di ordinamento, ad es

    • Quicksort:11.667(n+1)ln(n)1.74n18.74
    • Mergesort:12.5nln(n)
    • Heapsort: 16nln(n)+0.01n
    • Insertionsort: [ fonte ]2.25n2+7.75n3ln(n) Autonomia di diversi algoritmi di ordinamento

    Questi risultati indicano che Quicksort è il più veloce. Ma è dimostrato solo sulla macchina artificiale di Knuth, non implica necessariamente nulla per dire il tuo PC x86. Si noti inoltre che gli algoritmi si riferiscono in modo diverso per input piccoli:
    Autonomia di diversi algoritmi di ordinamento per piccoli input
    [ sorgente ]

  2. Analizzare le operazioni di base astratte .

    Per l'ordinamento basato sul confronto, in genere si tratta di scambi e confronti chiave . Nei libri di Robert Sedgewick, ad esempio "Algorithms" , questo approccio è perseguito. Lo trovi lì

    • Quicksort: confronti e swap in media12nln(n)13nln(n)
    • Mergesort: confronti, ma fino a accessi array (mergesort non è basato su swap, quindi non possiamo ).8,66 n ln ( n )1.44nln(n)8.66nln(n)
    • Insertionsort: confronti e scambi in media.114n214n2

    Come vedi, questo non consente prontamente confronti di algoritmi come l'esatta analisi di runtime, ma i risultati sono indipendenti dai dettagli della macchina.

Altre distribuzioni di input

Come notato sopra, i casi medi sono sempre rispetto ad alcune distribuzioni di input, quindi si potrebbero considerare altri diversi dalle permutazioni casuali. Ad esempio è stata fatta una ricerca per Quicksort con elementi uguali e c'è un bell'articolo sulla funzione di ordinamento standard in Java


8
I risultati di tipo 2. possono essere trasformati in risultati di tipo 1. inserendo costanti dipendenti dalla macchina. Pertanto, direi 2. è un approccio superiore.
Raffaello

2
@Raphael +1. Suppongo che tu stia supponendo che il dipendente dalla macchina sia anche dipendente dall'implementazione, giusto? Voglio dire, macchina veloce + scarsa implementazione probabilmente non è molto efficiente.
Janoma

2
@Janoma Ho ipotizzato che l'algoritmo analizzato fosse dato in forma molto dettagliata (poiché l'analisi è dettagliata) e che l'implementazione doveva essere il più possibile per lettera. Ma sì, anche l'implementazione dovrebbe tener conto.
Raffaello

3
In realtà, l'analisi di tipo 2 è inferiore nella pratica. Le macchine del mondo reale sono così complicate che i risultati del tipo 2 non possono essere tradotti in modo fattibile nel tipo 1. Confrontalo con il tipo 1: tracciare i tempi di esecuzione sperimentali richiede 5 minuti di lavoro.
Jules,

4
@Jules: "tracciare il tempo di esecuzione sperimentale" non è di tipo 1; non è un tipo di analisi formale e non è trasferibile ad altre macchine. Ecco perché facciamo un'analisi formale, dopo tutto.
Raffaello

78

Ci sono più punti che possono essere fatti riguardo a questa domanda.

Quicksort è in genere veloce

O(n2)

n1O(nlogn)

Quicksort è in genere più veloce della maggior parte dei tipi

O(nlogn)O(n2)n

O(nlogn)O(nBlog(nB))B

Il motivo di questa efficienza della cache è che analizza linearmente l'input e partiziona linearmente l'input. Ciò significa che possiamo sfruttare al massimo ogni carico della cache che facciamo mentre leggiamo tutti i numeri che cariciamo nella cache prima di scambiare quella cache con un'altra. In particolare, l'algoritmo è ignaro della cache, il che fornisce buone prestazioni della cache per ogni livello di cache, che è un'altra vittoria.

O(nBlogMB(nB))Mk

Quicksort è in genere più veloce di Mergesort

Questo confronto riguarda completamente i fattori costanti (se consideriamo il caso tipico). In particolare, la scelta è tra una scelta non ottimale del pivot per Quicksort rispetto alla copia dell'intero input per Mergesort (o la complessità dell'algoritmo necessaria per evitare questa copia). Si scopre che il primo è più efficiente: non c'è alcuna teoria dietro questo, sembra solo essere più veloce.

nO(logn)O(n)

Infine, nota che Quicksort è leggermente sensibile all'input che sembra essere nel giusto ordine, nel qual caso può saltare alcuni swap. Mergesort non ha tali ottimizzazioni, il che rende Quicksort un po 'più veloce rispetto a Mergesort.

Usa il tipo adatto alle tue esigenze

In conclusione: nessun algoritmo di ordinamento è sempre ottimale. Scegli quello che più si adatta alle tue esigenze. Se hai bisogno di un algoritmo che è il più veloce per la maggior parte dei casi e non ti dispiace che potrebbe finire per essere un po 'lento in rari casi e non hai bisogno di un ordinamento stabile, usa Quicksort. Altrimenti, usa l'algoritmo più adatto alle tue esigenze.


3
La tua ultima osservazione è particolarmente preziosa. Un mio collega attualmente analizza le implementazioni di Quicksort in diverse distribuzioni di input. Alcuni di questi si rompono per molti duplicati, per esempio.
Raffaello

4
O(n2)

8
"[T] non c'è nessuna teoria dietro questo, sembra solo essere più veloce." Tale affermazione è altamente insoddisfacente dal punto di vista scientifico. Immagina Newton che dice: "Le farfalle volano in alto, le mele cadono: non c'è nessuna teoria dietro questo, le mele cadono e basta."
David Richerby,

2
@Alex ten Brink, cosa intendi con "In particolare, l'algoritmo è ignaro della cache "?
Hibou57,

4
@ David Richerby, "Questa affermazione è altamente insoddisfacente dal punto di vista scientifico": potrebbe essere solo testimone di un fatto senza far finta che dovremmo esserne contenti. Alcune famiglie di algoritmi soffrono di una mancanza di piena formalizzazione; le funzioni di hashing sono un esempio.
Hibou57,

45

In uno dei tutorial di programmazione presso la mia università, abbiamo chiesto agli studenti di confrontare le prestazioni di quicksort, mergesort, insertion sort e list.sort integrato di Python (chiamato Timsort ). I risultati sperimentali mi hanno sorpreso profondamente dal fatto che list.sort integrato ha funzionato molto meglio di altri algoritmi di ordinamento, anche con istanze che hanno reso facilmente quicksort, crash di fusione. Quindi è prematuro concludere che la solita implementazione di quicksort è la migliore in pratica. Ma sono sicuro che ci sia un'implementazione molto migliore di quicksort o una versione ibrida di esso.

Questo è un bell'articolo sul blog di David R. MacIver che spiega Timsort come una forma di fusione adattiva.


17
@Raphael Per dirla in modo succinto, Timsort è unire l'ordinamento per gli asintotici più l'ordinamento di inserzione per input brevi più alcune euristiche per far fronte in modo efficiente ai dati che hanno occasionalmente lo scoppio già ordinato (che accade spesso nella pratica). Dai: oltre all'algoritmo, list.sorttrae vantaggio dall'essere una funzione integrata ottimizzata dai professionisti. Un confronto più equo avrebbe tutte le funzioni scritte nella stessa lingua allo stesso livello di sforzo.
Gilles,

1
@Dai: potresti almeno descrivere con quale tipo di input (risp. La loro distribuzione) in quali circostanze (bassa RAM, una implementazione parallela, ...) hai ottenuto i tuoi risultati.
Raffaello

7
Abbiamo testato su un elenco di numeri casuali, e parzialmente ordinati, completamente ordinati e in ordine inverso. Era un corso introduttivo del 1 ° anno, quindi non era un profondo studio empirico. Ma il fatto che ora sia ufficialmente utilizzato per ordinare gli array in Java SE 7 e sulla piattaforma Android significa qualcosa.
Dai

3
Questo è stato discusso anche qui: cstheory.stackexchange.com/a/927/74
Jukka Suomela,

34

Penso che uno dei motivi principali per cui QuickSort è così veloce rispetto ad altri algoritmi di ordinamento sia perché è compatibile con la cache. Quando QS elabora un segmento di un array, accede agli elementi all'inizio e alla fine del segmento e si sposta verso il centro del segmento.

Quindi, quando si avvia, si accede al primo elemento dell'array e un pezzo di memoria ("posizione") viene caricato nella cache. E quando provi ad accedere al secondo elemento, è (molto probabilmente) già nella cache, quindi è molto veloce.

Altri algoritmi come heapsort non funzionano in questo modo, saltano molto nella matrice, il che li rende più lenti.


5
Questa è una spiegazione discutibile: mergesort è anche cache friendly.
Dmytro Korduban,

2
Penso che questa risposta sia sostanzialmente giusta, ma ecco alcuni dettagli youtube.com/watch?v=aMnn0Jq0J-E
rgrig

3
probabilmente anche la costante moltiplicativa per la complessità del caso medio di ordinamento rapido è migliore (indipendentemente dal fattore cache che hai citato).
Kaveh

1
Il punto che hai citato non è così importante, rispetto ad altre buone proprietà di ordinamento rapido.
MMS

1
@Kaveh: "anche la costante moltiplicativa per la complessità del caso medio di ordinamento rapido è migliore" Hai qualche dato al riguardo?
Giorgio,

29

Altri hanno già detto che l' autonomia media asintotica di Quicksort è migliore (nella costante) di quella di altri algoritmi di ordinamento (in determinate impostazioni).

O(nlogn)

Nota che ci sono molte varianti di Quicksort (vedi ad esempio la tesi di Sedgewick). Si comportano diversamente su diverse distribuzioni di input (uniformi, quasi ordinate, quasi inversamente ordinate, molti duplicati, ...) e altri algoritmi potrebbero essere migliori per alcuni.

k10


20

O(nlgn)

ps: per essere precisi, essere migliore di altri algoritmi dipende dal compito. Per alcune attività potrebbe essere meglio utilizzare altri algoritmi di ordinamento.

Guarda anche:


3
@Janoma è una questione di quale linguaggio e compilatore usi. Quasi tutti i linguaggi funzionali (ML, Lisp, Haskell) possono fare ottimizzazioni che impediscono la crescita dello stack e compilatori più intelligenti per i linguaggi imperativi possono fare lo stesso (GCC, G ++, e credo che MSVC lo faccia tutti). L'eccezione notevole è Java, che non farà mai questa ottimizzazione, quindi ha senso riscrivere la ricorsione come iterazione in Java.
Rafe Kettler,

4
@JD, non puoi usare l'ottimizzazione delle chiamate di coda con quicksort (almeno non completamente), perché si chiama due volte. È possibile ottimizzare la seconda chiamata, ma non la prima.
svick

1
@ Janoma, non hai davvero bisogno dell'implementazione ricorsiva. Ad esempio, se si considera l'implementazione della funzione qsort in C, questa non utilizza chiamate ricorsive e quindi l'implementazione diventa molto più veloce.
Kaveh,

1
Anche Heapsort è presente, perché il QS è spesso più veloce?
Kevin

6
23240

16

Θ(n2)Θ(nlogn)

Il secondo motivo è che esegue l' in-placeordinamento e funziona molto bene con ambienti di memoria virtuale.

AGGIORNAMENTO:: (Dopo i commenti di Janoma e Svick)

Per illustrare meglio ciò, lasciatemi fare un esempio usando Merge Sort (perché Merge sort è il prossimo algoritmo di ordinamento ampiamente adottato dopo l'ordinamento rapido, penso) e vi dico da dove provengono le costanti extra (al meglio delle mie conoscenze e perché penso L'ordinamento rapido è migliore):

Considera la seguente sequenza:

12,30,21,8,6,9,1,7. The merge sort algorithm works as follows:

(a) 12,30,21,8    6,9,1,7  //divide stage
(b) 12,30   21,8   6,9   1,7   //divide stage
(c) 12   30   21   8   6   9   1   7   //Final divide stage
(d) 12,30   8,21   6,9   1,7   //Merge Stage
(e) 8,12,21,30   .....     // Analyze this stage

Se ti interessa vedere come sta accadendo l'ultimo stadio, i primi 12 vengono confrontati con 8 e 8 è più piccolo, quindi inizia per primo. Ora 12 è ANCORA rispetto a 21 e 12 va avanti e così via e così via. Se prendi l'unione finale, cioè 4 elementi con altri 4 elementi, incorre in molti confronti EXTRA come costanti che NON sono sostenuti nell'ordinamento rapido. Questo è il motivo per cui si preferisce l'ordinamento rapido.


1
Ma cosa rende le costanti così piccole?
svick

1
@svick Poiché sono ordinati in-place, ad esempio, non è richiesta memoria aggiuntiva.
0x0

Θ(nlgn)

15

La mia esperienza di lavoro con i dati del mondo reale è che quicksort è una scelta sbagliata . Quicksort funziona bene con dati casuali, ma i dati del mondo reale spesso non sono casuali.

Nel 2008 ho rintracciato un bug del software sospeso fino all'uso di quicksort. Qualche tempo dopo ho scritto semplici impianti di inserimento tipo, quicksort, heap sort e merge sort e li ho testati. Il mio tipo di unione ha superato tutti gli altri mentre lavoravo su grandi set di dati.

Da allora, merge sort è il mio algoritmo di ordinamento preferito. È elegante. È semplice da implementare. È un tipo stabile. Non degenera in comportamenti quadratici come fa Quicksort. Passo all'ordinamento per inserzione per ordinare piccoli array.

In molte occasioni mi sono trovato a pensare che una determinata implementazione funzioni sorprendentemente bene per quicksort solo per scoprire che in realtà non è quicksort. A volte l'implementazione passa da quicksort a un altro algoritmo e talvolta non utilizza affatto quicksort. Ad esempio, le funzioni qsort () di GLibc utilizzano effettivamente l'ordinamento di tipo merge. Solo se l'allocazione dello spazio di lavoro fallisce, si ricorre al quicksort sul posto che un commento di codice chiama "l'algoritmo più lento" .

Modifica: i linguaggi di programmazione come Java, Python e Perl utilizzano anche l'ordinamento di tipo merge o, più precisamente, una derivata, come Timsort o ordinamento di tipo merge per insiemi di grandi dimensioni e ordinamento di inserzione per insiemi di piccole dimensioni. (Java utilizza anche quicksort dual-pivot che è più veloce di quicksort semplice.)


Avevo visto qualcosa di simile a questo perché ci capitava di aggiungere / ricorrere costantemente per inserire in un batch di dati già ordinati. Puoi aggirare mediamente questo problema usando un quicksort randomizzato (e rimanere sorpreso da un ordinamento terribilmente lento raro e casuale), oppure puoi tollerare un ordinamento sempre più lento che non richiede mai un tempo sorprendente per finire. A volte è richiesta anche la stabilità dell'ordinamento. Java è passato dall'uso dell'ordinamento merge a una variante quicksort.
Rob

@Rob Questo non è accurato. Ancora oggi Java utilizza una variante di mergesort (Timsort). Utilizza anche una variante di quicksort (quicksort a doppio pivot).
Erwan Legrand,

14

1 - L' ordinamento rapido è attivo (non richiede memoria aggiuntiva, se non un importo costante).

2 - L' ordinamento rapido è più facile da implementare rispetto ad altri algoritmi di ordinamento efficienti.

3 - L' ordinamento rapido ha fattori costanti minori nel suo tempo di esecuzione rispetto ad altri algoritmi di ordinamento efficienti.

Aggiornamento: per unire l'ordinamento, è necessario eseguire alcune "unioni", che richiedono array aggiuntivi per archiviare i dati prima di unire; ma in rapido ordine, non lo fai. Ecco perché è attivo l'ordinamento rapido. Ci sono anche alcuni confronti extra fatti per l'unione che aumentano i fattori costanti nell'unione dell'ordinamento.


3
Hai visto implementazioni rapide sul posto e iterative di Quicksort? Sono molte cose ma non "facili".
Raffaello

2
Il numero 2 non risponde affatto alla mia domanda, e i numeri 1 e 3 hanno bisogno di una giustificazione adeguata, secondo me.
Janoma,

@Raphael: SONO facili. È molto più semplice implementare l'ordinamento rapido sul posto utilizzando un array, anziché i puntatori. E non deve essere iterativo per essere sul posto.
MMS

Gli array per la fusione non sono poi così male. Una volta spostato un oggetto da una pila sorgente alla pila di destinazione, non è più necessario che ci sia. Se si utilizzano array dinamici, si verifica un sovraccarico di memoria costante durante l'unione.
Oskar Skog,

@ 1 Anche Mergesort può essere installato. @ 2 Cosa definisce efficiente? Mi piace unire l'ordinamento perché è molto semplice e tuttavia efficiente secondo me. @ 3 Non rilevante quando si ordinano grandi quantità di dati e richiede che l'algoritmo sia implementato in modo efficiente.
Oskar Skog,

11

In quali condizioni uno specifico algoritmo di ordinamento è effettivamente il più veloce?

Θ(log(n)2)Θ(nlog(n)2)

Θ(nk)Θ(nm)k=2#number_of_Possible_valuesm=#maximum_length_of_keys

3) La struttura dati sottostante è composta da elementi collegati? Sì -> usa sempre l'ordinamento unione in posizione. Esistono sia misure facili da implementare fisse sia adattive (ovvero naturali) dal basso verso l'alto che uniscono tipi di arità diverse per strutture di dati collegate e poiché non richiedono mai la copia di tutti i dati in ogni passaggio e non richiedono nemmeno ricorsioni, sono più veloce di qualsiasi altro tipo basato sul confronto generale, anche più veloce di un ordinamento rapido.

Θ(n)

5) Le dimensioni dei dati sottostanti possono essere associate a dimensioni medio-piccole? ad es. n <10.000 ... 100.000.000 (a seconda dell'architettura e della struttura dati sottostanti)? Sì -> usa l'ordinamento bitonico o l'unione pari-dispari di Batcher. Vai a 1)

Θ(n)Θ(n2)Θ(nlog(n)2)il tempo di esecuzione peggiore è noto, o forse provare a ordinare i pettini. Non sono sicuro che sia l'ordinamento della shell che quello del pettine funzionino ragionevolmente bene in pratica.

Θ(log(n))Θ(n)Θ(n)Θ(log(n))Θ(n2)Θ(n)Θ(n)Θ(log(n))Θ(nlog(n))

Θ(nlog(n))

Suggerimenti per l'implementazione di quicksort:

Θ(n)Θ(log(n))Θ(nlogk(k1))

2) Esistono varianti iterative bottom-up e rapide di quicksort, ma AFAIK, hanno lo stesso spazio asintotico e limiti di tempo di quelli top-down, con i lati negativi aggiuntivi che sono difficili da implementare (ad esempio la gestione esplicita di una coda). La mia esperienza è che, per scopi pratici, non vale la pena considerarli.

Suggerimenti per l'implementazione di mergesort:

1) Il fusesort bottom-up è sempre più veloce del mergesort top-down, poiché non richiede chiamate di ricorsione.

2) l'unione molto ingenua può essere velocizzata usando un doppio buffer e commuta il buffer invece di copiare i dati dall'array temporale dopo ogni passaggio.

3) Per molti dati del mondo reale, il mergesort adattivo è molto più veloce di un mergesort di dimensioni fisse.

Θ(k)Θ(log(k))Θ(1)Θ(n)

Da quello che ho scritto, è chiaro che quicksort spesso non è l'algoritmo più veloce, tranne quando si applicano tutte le seguenti condizioni:

1) ci sono più di "pochi" possibili valori

2) la struttura dati sottostante non è collegata

3) non abbiamo bisogno di un ordine stabile

4) i dati sono abbastanza grandi da far entrare il leggero runtime asintotico subottimale di uno smistatore bitonico o della fusione pari-dispari di Batcher

5) i dati non sono quasi ordinati e non sono costituiti da parti più grandi già ordinate

6) possiamo accedere simultaneamente alla sequenza di dati da più punti

Θ(log(n))Θ(n)

ps: qualcuno deve aiutarmi con la formattazione del testo.


(5): l'implementazione dell'ordinamento di Apple controlla una corsa in ordine crescente o decrescente all'inizio e alla fine dell'array. Questo è molto veloce se non ci sono molti di questi elementi e può gestirli in modo molto efficace se ce ne sono più di n / nn di essi. Concatena due matrici ordinate e ordina il risultato e otterrai un'unione
gnasher729

8

La maggior parte dei metodi di ordinamento deve spostare i dati in pochi passaggi (ad esempio, unisci ordinamento apporta modifiche localmente, quindi unisce questo piccolo pezzo di dati, quindi unisce uno più grande ...). Di conseguenza, sono necessari molti movimenti di dati se i dati sono lontani dalla sua destinazione.

ab


5
La tua discussione su quicksort vs merge sort non trattiene l'acqua. Quicksort inizia con una mossa grande, quindi fa mosse sempre più piccole (circa la metà di ogni passo). Unisci ordinamento inizia con una piccola mossa, quindi effettua mosse sempre più grandi (circa il doppio di ogni passo). Ciò non significa che uno sia più efficiente dell'altro.
Gilles,
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.