È stabile e presenta una complessità temporale di O (n). Dovrebbe essere più veloce di algoritmi come Quicksort e Mergesort, ma non lo vedo quasi mai usato.
È stabile e presenta una complessità temporale di O (n). Dovrebbe essere più veloce di algoritmi come Quicksort e Mergesort, ma non lo vedo quasi mai usato.
Risposte:
A differenza dell'ordinamento radix, quicksort è universale, mentre l'ordinamento radix è utile solo per i numeri interi a lunghezza fissa.
Inoltre devi capire che O (f (n)) significa veramente in ordine di K * f (n), dove K è una costante arbitraria. Per l'ordinamento radix questo K sembra essere abbastanza grande (almeno l'ordine del numero di bit negli interi ordinati), d'altra parte quicksort ha uno dei K più bassi tra tutti gli algoritmi di ordinamento e la complessità media di n * log (n). Quindi, nello scenario di vita reale, quicksort sarà molto più veloce dell'ordinamento radix.
La maggior parte degli algoritmi di ordinamento sono di uso generale. Data una funzione di confronto, funzionano su qualsiasi cosa e algoritmi come Quicksort e Heapsort ordineranno con O (1) memoria aggiuntiva.
L'ordinamento Radix è più specializzato. È necessaria una chiave specifica in ordine lessicografico. È necessario un bucket per ogni possibile simbolo nella chiave e i bucket devono contenere molti record. (In alternativa, hai bisogno di una vasta gamma di bucket che conterrà ogni possibile valore chiave.) Probabilmente avrai bisogno di molta più memoria per eseguire l'ordinamento radix e la utilizzerai in modo casuale. Niente di tutto ciò è positivo per i computer moderni, poiché è probabile che si verifichino errori di pagina come Quicksort otterrà mancati riscontri nella cache.
Infine, in genere le persone non scrivono più i propri algoritmi di ordinamento. La maggior parte delle lingue dispone di strutture di libreria da ordinare e la cosa giusta da fare è normalmente usarle. Poiché l'ordinamento radix non è universalmente applicabile, in genere deve essere adattato all'uso effettivo e utilizza molta memoria aggiuntiva, è difficile inserirlo in una funzione di libreria o modello.
O(n^2)
memoria nel peggiore dei casi a causa di n
chiamate ricorsive sulle partizioni sinistra e destra. Se l'implementazione utilizza l'ottimizzazione della ricorsione della coda, è possibile ridurla solo O(n)
perché le chiamate alla partizione corretta non richiedono spazio aggiuntivo. ( en.wikipedia.org/wiki/Quicksort#Space_complexity )
S(n) \in O(n)
spazio per l'ordinamento con radix, vale a dire come per l'heap o l'ordinamento rapido.
n^2
di quicksort, ma O(log n)
...
È abbastanza raro che le chiavi ordinate siano in realtà numeri interi in un intervallo noto e scarso. Di solito hai campi alfabetici, che sembrano supportare un ordinamento non comparativo, ma poiché le stringhe del mondo reale non sono distribuite uniformemente in tutto l'alfabeto, questo non funziona come dovrebbe in teoria.
Altre volte, il criterio è definito solo a livello operativo (dati due record, è possibile decidere quale viene prima, ma non è possibile valutare quanto un record isolato sia "lontano" rispetto alla scala). Quindi il metodo spesso non è applicabile, meno applicabile di quanto si possa pensare o semplicemente non più veloce di O (n * log (n)).
Lo uso sempre, in realtà più di una sorta di confronto, ma devo ammettere che è una strana palla che funziona più con i numeri che con qualsiasi altra cosa (a malapena lavoro con le stringhe, e in genere sono internati se sì, a quel punto radix l'ordinamento può essere di nuovo utile per filtrare i duplicati e calcolare le intersezioni impostate; praticamente non faccio mai confronti lessicografici).
Un esempio di base sono i punti di ordinamento radix per una data dimensione come parte di una ricerca o divisione mediana o un modo rapido per rilevare punti coincidenti, frammenti di ordinamento di profondità o ordinamento radicale di una matrice di indici utilizzati in più loop per fornire un accesso più intuitivo alla cache pattern (non andare avanti e indietro in memoria solo per tornare indietro e ricaricare la stessa memoria in una riga della cache). Esiste un'applicazione molto ampia almeno nel mio dominio (computer grafica) solo per l'ordinamento su chiavi numeriche a 32 e 64 bit di dimensioni fisse.
Una cosa che volevo presentare e dire è che l'ordinamento radix può funzionare su numeri e negativi in virgola mobile, anche se è difficile scrivere una versione FP il più portatile possibile. Inoltre, mentre è O (n * K), K deve essere solo il numero di byte della dimensione della chiave (es: un milione di numeri interi a 32 bit richiederebbe generalmente 4 byte di dimensione se ci sono 2 ^ 8 voci nel bucket ). Il modello di accesso alla memoria tende inoltre ad essere molto più compatibile con la cache rispetto agli quicksorts anche se in genere ha bisogno di un array parallelo e di un piccolo array bucket (il secondo può in genere adattarsi perfettamente allo stack). QS potrebbe eseguire 50 milioni di swap per ordinare un array di un milione di numeri interi con schemi di accesso casuale sporadici. L'ordinamento radix può farlo in 4 passaggi lineari e compatibili con la cache sui dati.
Tuttavia, la mancanza di consapevolezza di poterlo fare con una piccola K, su numeri negativi insieme a virgola mobile, potrebbe benissimo contribuire significativamente alla mancanza di popolarità dei tipi di radix.
Per quanto riguarda la mia opinione sul perché le persone non lo usano più spesso, potrebbe avere a che fare con molti domini che generalmente non hanno la necessità di ordinare i numeri o usarli come chiavi di ricerca. Tuttavia, basandomi solo sulla mia esperienza personale, molti dei miei ex colleghi non l'hanno utilizzata nemmeno nei casi in cui era perfettamente adatto, e in parte perché non erano consapevoli che potesse essere fatto funzionare su FP e negativi. Quindi, a parte il fatto che funziona solo su tipi numerici, si pensa spesso che sia persino meno generalmente applicabile di quanto non sia in realtà. Non lo userei quasi nemmeno se pensassi che non funzionasse su numeri in virgola mobile e numeri interi negativi.
Alcuni parametri di riferimento:
Sorting 10000000 elements 3 times...
mt_sort_int: {0.135 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
mt_radix_sort: {0.228 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
std::sort: {1.697 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
qsort: {2.610 secs}
-- small result: [ 12 17 17 22 52 55 67 73 75 87 ]
E questo è solo con la mia ingenua implementazione ( mt_sort_int
è anche l'ordinamento radix ma con un ramo di codice più veloce dato che può assumere che la chiave sia un numero intero). Immagina quanto potrebbe essere veloce un'implementazione standard scritta da esperti.
L'unico caso in cui ho trovato che l'ordinamento radix è peggiore di quello basato sul confronto veramente veloce di C ++ è std::sort
stato per un numero molto piccolo di elementi, diciamo 32, a quel punto credo che std::sort
inizi a usare tipi più adatti al numero più piccolo di elementi come heapsorts o specie di inserzione, anche se a quel punto la mia implementazione usa solo std::sort
.
Un motivo in più: oggigiorno l'ordinamento è di solito implementato con una routine di ordinamento fornita dall'utente collegata alla logica di ordinamento fornita dal compilatore. Con un ordinamento radix questo sarebbe notevolmente più complesso e peggiorerebbe ulteriormente quando la routine di ordinamento agisce su più chiavi di lunghezza variabile. (Dire, nome e data di nascita.)
Nel mondo reale ho effettivamente implementato una sorta di radix una volta. Questo era ai vecchi tempi quando la memoria era limitata, non riuscivo a portare tutti i miei dati in memoria contemporaneamente. Ciò significava che il numero di accessi ai dati era molto più importante di O (n) vs O (n log n). Ho fatto un passaggio tra i dati assegnando ogni record a un cestino (da un elenco di quali record si trovavano in quali scomparti, in realtà non spostavano nulla). Per ogni cestino non vuoto (la mia chiave di ordinamento era il testo, ci sarebbero stati molti scomparti vuoti) Ho verificato se potevo effettivamente portare i dati in memoria - in caso affermativo, inseriscili e usa quicksort. In caso contrario, crea un file temporaneo contenente solo gli elementi nel cestino e chiama la routine in modo ricorsivo. (In pratica, pochi bin traboccerebbero.) Ciò causava due letture complete e una scrittura completa nella memoria di rete e qualcosa come il 10% di questa nella memoria locale.
In questi giorni problemi di big data sono molto più difficili da affrontare, probabilmente non scriverò mai più niente del genere. (Se dovessi affrontare gli stessi dati in questi giorni, vorrei semplicemente specificare un sistema operativo a 64 bit, aggiungere RAM se si verifica un thrashing in quell'editor.)
Se tutti i tuoi parametri sono tutti numeri interi e se hai oltre 1024 parametri di input, l'ordinamento radix è sempre più veloce.
Perché?
Complexity of radix sort = max number of digits x number of input parameters.
Complexity of quick sort = log(number of input parameters) x number of input parameters
Quindi l'ordinamento radix è più veloce quando
log(n)> max num of digits
Il numero intero massimo in Java è 2147483647. Che è lungo 10 cifre
Quindi il radix sort è sempre più veloce quando
log(n)> 10
Pertanto l' ordinamento radix è sempre più veloce quando
n>1024