2009-12-04 21 views
8

È un noto problema con Quicksort che quando il set di dati è inserito o quasi nell'ordine, le prestazioni si riducono in modo orribile. In questo caso, Insertion Sort, che è normalmente molto lento, è facilmente la scelta migliore. La domanda è sapere quando usare quale.Algoritmo di analisi pre-ordinamento?

Esiste un algoritmo disponibile per eseguire un set di dati, applicare un fattore di confronto e restituire un rapporto sul livello di chiusura del set di dati in ordine di classificazione? Preferisco Delphi/Pascal, ma posso leggere altre lingue se l'esempio non è eccessivamente complesso.

+1

Questa lentezza di quicksort con sequenze pre-ordinate è solo un problema, AFAIK, se l'implementazione è troppo semplice rispetto alla scelta di un elemento di pivot. Vedi http://www.cprogramming.com/tutorial/computersciencetheory/quicksort.html per esempio. – Dirk

risposta

9

Come ci si aspetterebbe un bel po 'di riflessione. La tecnica della mediana di tre significa che il comportamento peggiore di quicksort non si verifica per i dati ordinati, ma per i casi meno ovvi.

Introsort è piuttosto eccitante, poiché evita del tutto il quadratico di Quicksort. Invece della tua domanda naturale, "come faccio a rilevare che i dati sono quasi ordinati", in effetti si chiede come sta andando avanti, "ci vuole troppo tempo?". Se la risposta è sì, passa da quicksort a heapsort.

Timsort combina merge sort con insertion sort e si comporta molto bene su dati ordinati o invertiti e su dati che includono sottoinsiemi ordinati o in ordine inverso.

Quindi probabilmente la risposta alla tua domanda è "non hai bisogno di un'analisi preliminare, hai bisogno di un algoritmo di ordinamento adattivo".

+0

+1 per il link timsort –

+0

+1 wow, timsort sembra abbastanza pulito. – wowest

0

Non ho sentito di alcuna analisi di pre-selezione ma la mia opinione è che se si passerà attraverso il set di dati per analizzarlo, si sta già tagliando le prestazioni del tempo di ordinamento complessivo.

+2

Questo è un buon punto, ma se il passaggio di analisi è O (n), non domina il tempo di ordinamento asintotico. E se può aiutare a evitare un tempo di ordinamento nel caso peggiore O (n^2), potrebbe essere un vantaggio netto nel tempo di ordinamento per dataset di grandi dimensioni. – ddaa

+1

@ddaa: Questo sarebbe vero per gli ordinamenti di confronto, ma l'ordinamento O (n) è possibile con Ordinamento Radice o Ordinamento Bucket. Se includiamo questi algoritmi, il tempo di ordinamento potrebbe essere dominato dal tempo di analisi ... –

+1

@Jason: Non eseguirai questa analisi sui dati che stai per ordinare. La domanda è sulla scelta tra quicksort e insertion sort, e stai pensando di non fare neanche ... –

0

Una possibile soluzione consiste nel prendere il primo, l'ultimo e l'elemento centrale nell'intervallo di ordinamento corrente (durante l'operazione QuickSort) e scegliere quello centrale come elemento di pivot.

+0

Il tuo caso migliore è ancora O (N log N), dove Ordinamento inserzione è O (N) per dati quasi ordinati. – wowest

0

Per analizzare completamente al fine di decidere quale algoritmo utilizzare, si eseguirà quasi il lavoro di ordinamento. Si potrebbe fare qualcosa come controllare i valori con una piccola percentuale di indici casuali ma in aumento (cioè analizzare un piccolo campione degli articoli).

0

Dovresti comunque eseguire tutti i record per determinare se è ordinato o meno, quindi per migliorare le prestazioni, inizia con il tuo primo record ed esegui il resto finché non noti qualcosa non correttamente ordinato, o raggiungi la fine di la lista. Se trovi miss, quindi ordina solo gli elementi da quella posizione fino alla fine (poiché l'inizio dell'elenco è già ordinato).

A ciascuna voce nella seconda parte, vedere se l'articolo è < rispetto all'ultimo elemento nella prima parte e in tal caso utilizzare un ordinamento di inserimento in SOLO la prima parte. Altrimenti Quicksort contro tutti gli altri oggetti nella seconda parte. In questo modo l'ordinamento è ottimizzato per il caso specifico.

0

QuickSort Beng un problema solo quando il set di dati è enorme e già lo più ordinato, vorrei utilizzare la seguente euristica (in attesa di una soluzione in piena regola):

  • Non preoccupatevi se i dati impostare la dimensione è sotto la soglia.

  • Se si dispone di un accesso rapido (indicizzato) ai record (elementi), prendere un campione con 1 record in ogni record N e vedere se sono già ordinati. Dovrebbe essere abbastanza veloce per un piccolo campione e quindi puoi decidere di usare l'ordinamento rapido o meno.

+0

ma l'esempio fallisce se viene ordinato 1 record in ogni N, ma il record +1 in ogni N non lo è. potrebbe essere comunque necessario leggere ogni record per vedere se UNO di essi non è stato ordinato. – skamradt

+0

D'accordo, ma ci sono statisticamente poche possibilità che il campione si discosti così tanto dalla popolazione complessiva, specialmente se si randomizza un po 'N. –

0

Per rendere un punto concettuale che le persone non hanno ancora fatto: Quicksort è un algoritmo di divisione e conquista di buon senso con un bug ovvio in rari casi. Supponiamo di voler ordinare una pila di carte per studenti. (Che devo fare con un po 'di regolarità.) Nell'algoritmo quicksort, scegli un po' di carta, il pivot. Quindi dividere gli altri documenti a seconda che siano prima o dopo il perno. Quindi ripeti quello con i due sotto-moduli. Qual è il bug? Il pivot potrebbe essere un nome che si trova vicino a un'estremità dell'elenco anziché al centro, in modo da non ottenere molto da dividere in due pile.

Unisci un altro algoritmo di divisione e conquista che funziona in un ordine diverso. È possibile unire due elenchi ordinati in tempo lineare. Dividete le carte in due pile uguali o quasi uguali, quindi ordinate ricorsivamente ciascuna, quindi unite. Unisci sort non ha bug. Uno dei motivi per cui quicksort è più popolare di merge sort è storico: Quicksort è veloce (di solito) e funziona senza memoria aggiuntiva. Ma in questi giorni, può essere più importante salvare i confronti che risparmiare memoria, e il riassetto attuale è spesso astratto dai puntatori di permutazione. Se le cose fossero sempre state così, sospetto che l'unire sort sarebbe stato semplicemente più popolare di quicksort. (E forse aggiungere "velocemente" al nome era un buon venditore.)

+0

Dal mio POV il vantaggio di un ordinamento sul posto non è tanto quello che salva * la memoria *, in quanto salva un'allocazione di memoria e quindi non può fallire. Quindi, quando si ordina un array, quicksort/heapsort/insertion sort/bubble sort hanno tutte interfacce utente migliori di quelle di mergesort. Se il mergesort fosse preferito a quicksort, ovviamente potevi tentare di allocare la memoria, e se fallisce esegui un quicksort. Se stai allineando comunque una serie secondaria di puntatori e ordinandoli, allora stai introducendo la possibilità di un fallimento in quel punto, e quindi potresti anche permettere il fallimento altrove. –

+0

@SteveJessop Questo è un punto giusto. Tuttavia, questa preoccupazione, sebbene ancora significativa in alcuni casi, è anche un po 'datata. Sono d'accordo che non è banale per l'ambiente esterno allocare abbastanza memoria a ogni programma o funzione client che lo desidera. Tuttavia, anche questo è migliorato nel tempo in molti ambienti. –

+0

Io non credo che sia davvero una questione di equità, tanto da ciò che accade quando si esaurisce, e se siete robusto per questo. Se l'allocazione può fallire, scrivi il tuo programma in un modo. Se invece l'SO soffia qualcosa fuori dall'acqua finché non ha abbastanza memoria per soddisfare la richiesta o l'errore di pagina al primo accesso, allora scrivi il tuo programma in un altro modo. Alcuni linguaggi prendono una via di mezzo, dove in teoria si potrebbe * * catturare out-of-memory eccezioni e continuare, ma in pratica non lo fai, si lascia l'eccezione si uccide. Suppongo che possa essere considerato il modo "aggiornato" per farlo ;-) –