Complessità dell'algoritmo shuffle Fisher-Yates


15

Questa domanda riguarda l'algoritmo Fisher-Yates per restituire uno shuffle casuale di un dato array. La pagina di Wikipedia dice che la sua complessità è O (n), ma penso che sia O (n log n).

In ogni iterazione i, viene scelto un numero intero casuale tra 1 e i. Scrivere semplicemente il numero intero in memoria è O (log i), e poiché non ci sono iterazioni, il totale è

O (registro 1) + O (registro 2) + ... + O (registro n) = O (n registro n)

che non è meglio l'algoritmo ingenuo. Mi sto perdendo qualcosa qui?

Nota: l'algoritmo ingenuo consiste nell'assegnare a ciascun elemento un numero casuale nell'intervallo (0,1), quindi ordinare l'array in base ai numeri assegnati.

Risposte:


24

Sospetto che qui, come nella maggior parte degli algoritmi, il costo della lettura e della scrittura di numeri di bit sia considerato costante. È un peccato minore, purché non ti lasci trasportare e crolli per caso P e PSPACE .O(logn)


4
Mentre questo è davvero un "peccato minore", penso che sia un grave peccato della pedagogia del TCS che questo non sia mai menzionato esplicitamente! Ogni singolo studente CS lo scopre da solo e pensa che qualcosa di grave sia sbagliato fino a quando non gli viene detto che tutti lo sanno, ma nessuno ne parla. Inoltre, non c'era un brouhaha un paio d'anni fa quando qualcuno ha sfruttato il modello O (log n) per fornire un algoritmo del tempo subcubico per qualche famoso problema che era stato ipotizzato essere Omega (n ^ 3)? È mai stato risolto?
randomwalker

2
Non sono a conoscenza del brouhaha a cui ti riferisci. Per non menzionarlo, hai decisamente ragione. Dopo aver letto per la prima volta il post di Jeff Erickson, ora faccio un punto per dimostrare P = PSPACE nella mia classe di geometria solo per calci :)
Suresh Venkat,

1
Grazie per la risposta. Non ho mai saputo che fosse un grosso problema. Il link fornisce una buona lettura.
Tomer Vromen,

1
In conclusione: rendi sempre esplicito il tuo modello.
Jukka Suomela,

2
Penso che il motivo principale per cui lasciamo che bit op sia tempo costante è che (in tempo polinomiale) è possibile programmare una tabella di ricerca dell'accesso a tempo costante per tutte le coppie di operandi O ( log n ) -bit, per la maggior parte modelli computazionali "moderni". Non c'è nulla di "peccaminoso" in questo ... per me, vedo questa proprietà come una che può essere semplicemente assunta senza perdita di generalità. O(logn)O(logn)
Ryan Williams,

17

Il modello standard di calcolo presuppone che le operazioni aritmetiche su numeri interi O (log n) a bit possano essere eseguite in tempo costante, poiché tali operazioni sono tipicamente consegnate nell'hardware. Quindi, con l'algoritmo Fisher-Yates, "scrivere l'intero i in memoria" richiede solo O (1) tempo.

Naturalmente, è perfettamente significativo analizzare l'algoritmo in termini di operazioni a bit, ma il modello a costo in bit è meno predittivo del comportamento reale. Anche il semplice ciclo for i = 1 to n: print(i)richiede operazioni di bit O (n log n).


Bel punto con il ciclo. Mai notato che ...
Tomer Vromen il

8

Questa è una risposta a "[L'algoritmo Fisher-Yates] non è migliore dell'algoritmo ingenuo. Mi sto perdendo qualcosa qui?" che hai posto nella domanda.

Nel tuo algoritmo "ingenuo" che utilizza numeri reali: quanti bit di precisione usi? Se stai contando la complessità dei bit (come sembri fare per Fisher-Yates) e l'algoritmo utilizza k bit casuali per i numeri reali, il suo tempo di esecuzione sarebbe Ω (kn log n), dal momento che confrontando due k- bit numeri reali richiedono Ω (k) tempo. Ma k deve essere almeno Ω (log n) per impedire che due elementi vengano mappati sullo stesso numero reale, il che significa che l'algoritmo impiega Ω (n log 2 n) tempo, che è più lento del riordino Fisher-Yates di un fattore del registro n.

Se stai solo contando il numero di operazioni aritmetiche e di confronto e ignori la loro complessità in bit, allora Fisher-Yates è Θ (n) e il tuo algoritmo è Θ (n log n), ancora un fattore di log n a parte.


Sospettavo che l'algoritmo "ingenuo" avesse quell'implicito k ...
Tomer Vromen,

1
L'algoritmo "ingenuo" può essere implementato in modo pulito in tempo lineare come segue. Assegna a ogni elemento un numero intero casuale compreso tra 1 e n ^ 3, quindi ordina i numeri nel tempo O (n) tramite l'ordinamento radix. (Con alta probabilità, nessun elemento otterrà lo stesso numero casuale. Se ci sono duplicati, rimescolarli in modo ricorsivo.)
Jeffε

@JeffE: grazie! È molto pulito e ha la stessa complessità di Fisher-Yates. Dopo aver pubblicato questo, in realtà sentivo che l'algoritmo "ingenuo" non dovrebbe essere peggio ... Mi mancava il fatto che i numeri di n k-bit possono essere ordinati in O (nk), senza bisogno di O (nklog n). Ma immagino che Knuth-Fisher-Yates sia ancora migliore nelle costanti: richiede esattamente (log n!) Bit casuali — un numero intero casuale da 1 a n, quindi 1 a n-1, ecc. — Che è ottimale (anziché 3n log n), e può essere fatto sul posto con solo memoria aggiuntiva costante.
ShreevatsaR,

6

Non c'è niente di speciale negli interi per questo problema.

Ad esempio, le tabelle hash (che memorizzano qualsiasi tipo di valori) non sono il tempo O (1) per accedere se la funzione hash deve leggere l'intero valore per calcolare il suo hash. n elementi unici richiedono log n bit ciascuno in media per rappresentare, non importa quanto sia intelligente la tua rappresentazione, e qualsiasi funzione hash che legge il suo intero input richiederà quindi almeno tanto tempo per il calcolo. In pratica sono più veloci degli alberi rosso-neri, ma asintoticamente non sono migliori.

La brouhaha referenziata da randomwalker parlava di un documento POPL 2008 ( http://portal.acm.org/citation.cfm?doid=1328438.1328460 ), discusso qui: http://blog.computationalcomplexity.org/2009/05/shaving- registra-con-unit-cost.html

In quel post Lance Fortnow descrive come uno studente si sia lamentato del fatto che l'ordinamento richiede davvero n log ^ 2 n volta se dobbiamo leggere tutti i log n bit di due elementi per confrontarli, il che sembra una ragionevole obiezione.


Non ho l'autore del post sul blog. Si lamenta che l'ordinamento sia in realtà O (n log ^ 2 n), ma poi dice che la carta è solida?
Tomer Vromen il

Il documento è solido (cioè non falso) in quanto esiste un modello in cui le operazioni aritmetiche richiedono un tempo unitario, e in quel modello l'algoritmo del documento è il primo a realizzare o (n ^ 3) operazioni.
Dave Doty,

Non ottengo l'obiezione O (n log ^ 2 n) perché in termini di bit, l'input stesso ha dimensioni O (n log n). A proposito, come nota a margine, il livello di qualità dei commenti sul blog di complessità era molto più alto di allora ...
arnab

4

La pagina di Wikipedia dice che la sua complessità è O (n), ma penso che sia O (n log n).

In realtà, O (n log n) è un limite inferiore per questo problema nei modelli in cui l'ordinamento è O (n log n). Se tutte le permutazioni sono ugualmente probabili, l'algoritmo in funzione di flussi casuali a permutazioni deve essere suriettivo. Non ci sono! permutazioni quindi in qualcosa come un modello di albero decisionale ci sono rami di lunghezza almeno O (log n!) = O (n log n).

1-εO(ε)


3

In TCS, consideriamo - se non diversamente specificato esplicitamente - la complessità su una macchina di Turing. Mentre questo va bene per scopi teorici, i risultati non sono molto utili nella pratica poiché implementiamo diversi modelli di macchine (ovvero approssimazioni finite) nell'hardware. È quindi una domanda fattibile chiedere complessità su questi modelli. Ad esempio, supponiamo in genere che le macchine a registro (simili alle CPU reali) possano eseguire operazioni atomiche su due registri in tempo costante - questo è ciò che avrebbe potuto essere impiegato qui.

In breve: pensi in termini di TM, gli autori dell'articolo in termini di RM. Hai ragione entrambi.

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.