2009-06-07 17 views
67

Quindi penso che sto per essere sepolto per aver fatto una domanda così banale ma sono un po 'confuso su qualcosa.O (N log N) Complessità - Simile al lineare?

Ho implementato quicksort in Java e C e stavo facendo alcuni confronti di base. Il grafico è venuto fuori come due linee rette, con il C che è 4ms più veloce della controparte Java su 100.000 numeri interi casuali.

Results

Il codice per i miei test può essere trovato qui;

android-benchmarks

non ero sicuro di quello che una (n log n) la linea sarebbe simile, ma non pensavo che sarebbe dritto. Volevo solo verificare che questo fosse il risultato previsto e che non dovrei provare a trovare un errore nel mio codice.

Ho bloccato la formula in Excel e per la base 10 sembra essere una linea dritta con un nodo all'inizio. Questo perché la differenza tra log (n) e log (n + 1) aumenta in modo lineare?

Grazie,

Gav

+1

Google * image * search sembra sorprendentemente buono come "n log n". –

+1

La linea Java in cima non mi sembra diretta. – Puppy

+0

Simile, infatti, che è il motivo per cui viene chiamato ["linearithmic time"] (http://en.wikipedia.org/wiki/Time_complexity#Linearithmic_time) – msanford

risposta

76

rendere il grafico più grande e vedrete che O (n log n) non è una linea abbastanza dritto. Ma sì, è abbastanza vicino al comportamento lineare. Per capire perché, prendi il logaritmo di alcuni numeri molto grandi.

Ad esempio (base 10):

log(1000000) = 6 
log(1000000000) = 9 
… 

Così, per ordinare 1.000.000 numeri, un O (n log n) Sorting aggiunge un fattore misero 6 (o poco più poiché la maggior parte degli algoritmi di ordinamento dipenderanno base 2 logaritmi). Non molto.

In realtà, questo fattore di registro è così straordinariamente piccola che per la maggior parte degli ordini di grandezza, con sede O (n log n) algoritmi di sovraperformare algoritmi di tempo lineare. Un esempio importante è la creazione di una struttura di dati dell'array suffisso.

Un caso semplice mi ha recentemente morso when I tried to improve a quicksort sorting of short strings by employing radix sort. Risulta, per le stringhe corte, questo ordinamento di tipo (tempo lineare) è stato più veloce di quicksort, ma c'era un punto di svolta per le stringhe ancora relativamente corte, dato che l'ordinamento radix dipende in modo cruciale dalla lunghezza delle stringhe che si ordinano.

+2

Risposta fantastica, grazie per averlo chiarito. Basta leggere il tuo post ora, roba davvero interessante. – gav

+1

I buoni tipi tendono ad andare per un algoritmo lineare una volta che hanno diviso e conquistato in pezzi sufficientemente piccoli. Esattamente quanto piccola è una questione di benchmarking (dati reali). –

+2

Tom: Non sono sicuro di cosa intendi esattamente per lineare. Spesso, gli algoritmi di ordinamento fanno il contrario, usando ordinamenti O (n^2) come l'ordinamento di inserimento su piccole porzioni, poiché il loro fattore costante è così piccolo che persino il runtime quadratico supera l'ordinamento nlogn. D'altro canto, l'introsort utilizza una strategia per uscire da ricorsioni troppo profonde - ma ancora una volta, questo non è lineare, ma scambia solo un caso quadradic per il comportamento di O (n logn). –

1

log (N) è (molto) più o meno il numero di cifre in N. Così, per la maggior parte, c'è poca differenza tra log (n) e log (n + 1)

+3

log-base- * 10 * è molto approssimativamente il numero di cifre in N (presupponendo che si stia utilizzando la rappresentazione decimale). La maggior parte degli algoritmi di ordinamento/ricerca utilizza log-base-2 che, pur essendo proporzionale a log-base-10 (quindi il big-O si applica ancora), non assomiglia a quello che descrivi :-) – paxdiablo

+0

Un altro modo per dirlo è che log-base-2 è approssimativamente il numero di cifre in N quando è scritto in binario, ovvero il numero di bit necessari per rappresentare N. –

0

Prova tracciare un la linea lineare attuale sopra e vedrai il piccolo aumento. Notare che il valore Y a 50.0000 è inferiore al valore 1/2 Y a 100.000.

È lì, ma è piccolo. Ecco perché O (nlog (n)) è così buono!

+0

È ancora dannatamente meglio di O (n^2). – paxdiablo

3
  1. Di solito gli algoritmi O (n * log (n)) hanno un'implementazione logaritmica a 2 basi.
  2. Per n = 1024, log (1024) = 10, quindi n * log (n) = 1024 * 10 = 10240 calcoli, un aumento di un ordine di grandezza.

Quindi, O (n * log (n)) è simile al lineare solo per una piccola quantità di dati.

Suggerimento: non dimenticare che quicksort si comporta molto bene su dati casuali e che non è un algoritmo O (n * log (n)).

+2

Tutti i logaritmi sono uguali, differiscono solo per la scala. Quindi non vedo il significato della tua prima affermazione. Inoltre, non sono d'accordo con la tua affermazione che O (n log n) è solo simile al lineare per una piccola quantità di dati. Ancora una volta, è una cosa in scala. Come contro-esempio, basta guardare i grafici nella domanda originale. – waxwing

+0

Non intendo graficamente simile (a una linea retta) ma con una complessità temporale simile. Il tempo O (n * logn) può essere facilmente un ordine di grandezza maggiore di O (n). Se i grafici confrontassero gli algoritmi O (n * logn) e O (n) vedresti cosa intendo. :) Man mano che N diventa sempre più grande, O (n * logn) * si sposta * sulle successive scale logaritmiche. –

+1

In media Quicksort è un algoritmo O (n log n). – Manu

5

Per un divertimento ancora più simile, provare a calcolare il tempo impiegato dalle operazioni n sullo standard disjoint set data structure. E 'stato dimostrato di essere asintoticamente n   α (n) dove α (n) è l'inverso della Ackermann function (anche se il tuo algoritmi soliti libri di testo sarà probabilmente solo mostrare un balzo di n Ceppo n o eventualmente n   log*   n). Per qualsiasi tipo di numero che si sarà probabile incontrare come la dimensione di input, α (n)   ≤   5 (e in effetti il ​​login *   n   ≤   5), anche se lo fa approccio all'infinito asintoticamente.

Ciò che suppongo tu possa imparare da questo è che mentre la complessità asintotica è uno strumento molto utile per pensare agli algoritmi, non è esattamente la stessa cosa dell'efficienza pratica.

10

proposito, quicksort è in realtà O (n^2), ma con un caso medio di O (nlogn)

proposito, c'è una bella differenza tra O (n) e O (nlogn). Ecco perché non è delimitata da O (n) per nessuna costante.

Per una dimostrazione grafica vedere:

O(n) vs O(nlogn)

+2

a) Senza specificare, O() viene solitamente utilizzato per indicare la complessità * prevista * (media). b) La notazione O() non include i fattori costanti, quindi O (n) e O (2n) sono uguali. Poiché log (n) è quasi costante (per grandi numeri, rispetto a n), si può affermare che O (n) e O (n log (n)) sono quasi gli stessi. Dovresti avere tracciato: http://www.wolframalpha.com/input/?i=plot+7+x%2C+x+log+x+from+1+to+1500 – Timmmm

+12

Questo di solito non è vero. La notazione Big O indica tipicamente la peggiore complessità asintotica e indica una funzione che si estende al di sopra della complessità degli algoritmi. O (n) non si avvicina a O (nlogn), sebbene a fini pratici O (nlogn) sia relativamente buono e non molto peggio. La peggiore delle prestazioni di un ordinamento rapido è certamente * non * una cosa insolita da incontrare. Prova a fare un quicksort sulle voci nel dizionario se non mi credi. – groundhog

+0

Non c'è una grande differenza. Soprattutto quando lo si confronta con il prossimo ordine $ O (n^2) $. https://i.sli.mg/9zXUQR.png –

2

Tutti i dati possono essere rappresentate graficamente su una linea se gli assi sono scelti correttamente :-)

Wikipedia dice Big-O è il caso peggiore (cioè f (x) è O (N) significa f (x) viene "limitata superiormente" di N) https://en.wikipedia.org/wiki/Big_O_notation

Ecco una bella serie di grafici che rappresentano le differenze tra le varie funzioni comuni: 01.239.706,27019 milioni

La derivata di log (x) è 1/x. Questo è quanto velocemente log (x) aumenta con l'aumentare di x. Non è lineare, anche se può sembrare una linea retta perché si piega lentamente. Quando penso a O (log (n)), lo considero come O (N^0 +), cioè il più piccolo potere di N che non è una costante, poiché ogni potere costante positivo di N lo supererà alla fine. Non è preciso al 100%, quindi i professori si arrabbieranno con te se lo spieghi in questo modo.

La differenza tra i registri di due basi diverse è un moltiplicatore costante. Cerca la formula per convertire i registri tra due basi: (sotto "cambio di base" qui: https://en.wikipedia.org/wiki/Logarithm) Il trucco è trattare k e b come costanti.

In pratica, di solito ci sarà qualche singhiozzo in qualsiasi dato che trama. Ci saranno delle differenze nelle cose al di fuori del tuo programma (qualcosa che si sta spostando nella CPU prima del tuo programma, errori di cache, ecc.). Ci vogliono molte esecuzioni per ottenere dati affidabili. Le costanti sono il più grande nemico nel tentativo di applicare la notazione Big O al runtime attuale. Un algoritmo O (N) con una costante elevata può essere più lento di un algoritmo O (N^2) per un numero abbastanza piccolo N.

+0

(suppongo che intendessi una linea retta, e non "linea" come termine generale per la curva.) Comprerei che qualsiasi funzione continuamente differenziabile, a valori reali di una variabile reale può essere tracciata su una linea retta se gli assi sono scelti correttamente con solo shenanigans moderati come valori di assi duplicati (necessari a meno che non sia una funzione uno-a-uno) ma "nessun dato"? Penso che sia un tratto. Che ne dici della funzione che è zero per tutti i numeri razionali, ma uno per tutti i numeri irrazionali. (È conosciuta come la funzione Dirichlet ed è una vera funzione matematica.) –

Problemi correlati