Cosa rende un brutto caso l'ordinamento rapido?


10

Sto imparando a conoscere Quicksort e voglio illustrare diversi array su cui Quicksort avrebbe difficoltà. Il quicksort che ho in mente non ha un shuffle casuale iniziale, fa 2 partizioni e non calcola la mediana.

Finora ho pensato a tre esempi:

[1,2,3,4,5,6,7,8,9,10] - when the array is sorted
[10,9,8,7,6,5,4,3,2,1] - when the array is reversed
[1,1,1,1,1,1,1,1,1,1] - when the array is the same values
[1,1,1,2,2,2,3,3,3,3] - when there are few and unique keys

Ad esempio, non sono troppo sicuro di questo:

[1,3,5,7,9,10,8,6,4,2]

Quindi, cosa rende difficile un array con cui Quicksort ha difficoltà rispetto a quello in cui è (quasi) ideale?


2
Come viene selezionato il perno? Hai dichiarato due modi in cui non è stato selezionato, ma non come è stato selezionato.
Winston Ewert,

Si prega di dare il caso peggiore per QuickSort - quando può accadere? su StackOverflow una lettura. Trovo anche che sorting.at sia una bella visualizzazione degli algoritmi di ordinamento.

@WinstonEwert Pivot è selezionato dal primo elemento.
mQQERTY,

@ Renren29 Ho modificato un po 'la domanda cercando di spostarla per concentrarmi sul motivo per cui quicksort avrebbe avuto difficoltà con un determinato array piuttosto che cercare esempi di array (non credo che le persone ti diano le risposte [2,1,2,1,2,1,2,1]e che sia l'intero risposta). L'obiettivo della domanda sarebbe, idealmente, quello in cui altre persone possano venire e scoprire di più sul perché (che ha una risposta) piuttosto che sugli esempi (di cui ci sono innumerevoli).

Stai eseguendo QuickSort verso pezzi di 2 elementi? Perché le implementazioni del mondo reale tendono a usare tipi più semplici per piccoli pezzi. Ad esempio, compare-and-swap è molto più semplice di quicksort per N = 2.
Salterio,

Risposte:


9

Ogni algoritmo di ordinamento presenta il caso peggiore, e in molti casi il caso peggiore è davvero negativo, quindi vale la pena testarlo. Il problema è che non esiste un singolo caso peggiore solo perché conosci l'algoritmo di base.

I casi peggiori comuni includono: già ordinati; ordinati al contrario; quasi ordinati, uno fuori servizio; tutti i valori sono uguali; tutti uguali tranne il primo (o l'ultimo) è più alto (o più basso). Una volta abbiamo avuto una specie in cui il caso peggiore era un particolare schema a dente di sega, che era molto difficile da prevedere ma abbastanza comune nella pratica.

Il caso peggiore per quicksort è quello che riesce sempre a scegliere il peggior perno possibile, in modo che una delle partizioni abbia un solo elemento. Se il pivot è il primo elemento (scelta errata), i dati già ordinati o inversi sono il caso peggiore. Per una mediana di tre dati pivot che sono tutti uguali o solo il primo o l'ultimo è diverso fa il trucco.


Per quicksort la complessità media è nlogn e il caso peggiore è n ^ 2. Il motivo per cui vale la pena innescare comportamenti peggiori è perché questo è anche il caso che produce la massima profondità di ricorsione. Per un'implementazione ingenua la profondità di ricorsione potrebbe essere n, il che potrebbe innescare un overflow dello stack. Testare altre situazioni estreme (incluso il caso migliore) può essere utile per ragioni simili.


Vedo, quindi la deviazione standard dalla media determina davvero il risultato del partizionamento.
mQQERTY,

"... e in quasi tutti i casi il caso peggiore è davvero brutto, quindi vale la pena testarlo." . È discutibile. Quando guardo questa tabella: en.wikipedia.org/wiki/…, concludo che per la maggior parte degli algoritmi di ordinamento "buoni" (cioè con O(NlogN)prestazioni medie o migliori) i casi peggiori e medi hanno la stessa complessità. Ciò suggerisce che di solito NON vale la pena testare per i casi peggiori. (Dato che il test è probabilmente O(N)... o peggio.)
Stephen C,

@ Renren29: la mediana di 3 pivot sarà la prima o l'ultima solo se 2 o 3 dei valori sono uguali. SD non ci entra.
david.pfx,

@StephenC: molti algoritmi "buoni" tra cui quicksort hanno n ^ 2 complessità nel caso peggiore. Ma vedi modifica.
david.pfx,

@ david.pfx - "Some" ... SÌ. "Quasi ogni" ... NO.
Stephen C,

0

Un algoritmo sfugge alla maggior parte dei casi negativi usando un perno randomizzato, escludendo elementi continui equivale a un perno dal partizionamento e dalla ricerca asimmetrica. Cerca in avanti un elemento maggiore o uguale a un perno e cerca indietro un elemento meno di un perno.
Ringrazio MichaelT, la ricerca asimmetrica è stata ideata per risolvere [2,1,2,1,2,1,2,1].

Il seguente risultato è generato dalla mia funzione qsort_random (). N = 100.000

usec    call   compare   copy    pattern
80132   62946  1971278   877143  random
47326   57578  1606067   215155  sorted : 0,1,2,3,...,n-1
49927   63578  1628883   338715  sorted in reverse : n-1,n-2,...,2,1,0
55619   63781  1596934   377330  nearly reverse : n-2,n-1,n-4,n-3,...,2,3,0,1
54714   66667  1611454   290392  median-3-killer : n-1,0,1,2,...,n-2
1491    1      99999     4       all values the same : n,n,n,...
1577    1      99999     4       first is higher : n,1,1,1,...
2778    2      156159    10      last is lower : n,n,n,...,n,1
2994    3      199996    100009  a few data : n,...,n,1,...,1
3196    3      199996    50012   zigzag : n,1,n,1,...,n,1
917796  56284  67721985  673356  valley(sawtooth?) : n-1,n-3,...,0,...,n-4,n-2

La maggior parte dei casi è più veloce di un modello casuale. Il modello a valle è un brutto caso per la maggior parte della selezione del perno.

qsort(3)       usec = 14523   call = 0      compare = 884463    copy = 0
qsort_head()   usec = 138609  call = 99999  compare = 8120991   copy = 1214397
qsort_middle() usec = 664325  call = 99999  compare = 52928111  copy = 1036047
qsort_trad()   usec = 118122  call = 99999  compare = 6476025   copy = 1337523
qsort_random() usec = 295699  call = 58806  compare = 19439952  copy = 732962
qsort_log2()   usec = 66411   call = 63987  compare = 1597455   copy = 944821

qsort_log2 () sfugge a un brutto caso selezionando un perno negli elementi log2 (N).
qsort (3) usa la libreria GNU che è un tipo di unione di ordinamento degli indici.
qsort_trad () seleziona un perno nei primi, medi e ultimi elementi.
qsort_random () e qsort_log2 () non usano lo scambio.
I programmi e gli script della sorgente C sono pubblicati in github .

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.