Il seguente algoritmo deterministico (senza il comparatore) funziona per una tupla di input :( a1, ... , unn)
- Do la Fisher-Yates Shuffle usare il comparatore con una certa coppia statica (ad esempio ) (campionamento per accettazione-rifiuto facendo) come un coin flip. Se il comparatore emette 1 la prima volta, utilizzarlo invertito per evitare un loop di rifiuto infinito nel caso deterministico.un'1< a21
- (speedup opzionale: prova una singola coppia volte, dove n è la lunghezza o il tuo input. Se una qualsiasi delle due uscite differisce, restituisci la permutazione ottenuta in (1))nn
- Ordina il tuo array usando unisci ordinamento.
Data una relazione di ordine deterministico come comparatore, questo algoritmo ordina un array nel tempo poiché il shuffle Fisher-Yates viene eseguito in O ( n ) usando i "bit casuali" non casuali di O ( log n ) (ad es. Chiamate al comparatore ) in ogni passaggio e unisci l'ordinamento ha la stessa complessità asintotica. Il risultato di (1) è totalmente inutile in questo caso, ma poiché è seguito da un tipo reale, questo non fa male.O (nlogn )O (n)O (logn )
Dato un vero lancio della moneta come comparatore (1) consente l'array con uguale probabilità per ogni permutazione e se davvero devi fare (3) (hai lasciato fuori (2) o (2) non è riuscito a determinare la casualità), questo non è danno perché la distribuzione del suo risultato dipende solo dall'ordine del suo input che è uniformemente distribuito tra tutte le permutazioni a causa di (1), quindi anche il risultato dell'intero algoritmo è distribuito uniformemente. Il numero di volte in cui ogni campionamento di accettazione-rifiuto deve essere ripetuto viene distribuito geometricamente (respingi con probabilità ) e quindi ha un valore atteso<2. Ogni ripetizione utilizza al massimolognbit, quindi l'analisi di runtime è quasi la stessa del caso deterministico, ma otteniamo solo unruntime previstodiO(nlogn), con possibilità di non termine (termina soloquasi sicuramente).< 12< 2lognO (nlogn )
Come ha sottolineato Joe: se non ti piace il test per il primo bit in (1), fai (3) quindi (1) e usa che è sempre 0 , poiché l'array è già ordinato nel caso deterministico. Inoltre, devi sottrarre il tuo numero casuale dal limite superiore dell'intervallo nel loop, poiché il limite superiore per il numero casuale produce la permutazione identica. Ma tieni presente che (2) è proibito allora, perché devi sempre fare la riproduzione casuale nel caso del riscatto.un'n< a10
Puoi anche usare le stesse chiamate al tuo comparatore per (1) e (3), ma poi dimostrare che il risultato è distribuito uniformemente è almeno molto più difficile, se possibile affatto.
Il seguente algoritmo non ha fasi distinte per la riproduzione casuale e l'ordinamento, ma è asintoticamente più lento. È essenzialmente un
ordinamento per inserzione con
ricerca binaria . Userò
per indicare l'input e
b k = ( b k , 1 , … , b k , k ) per indicare il risultato dopo il
k -esimo giro:
a = ( a1, ... , unn)BK= ( bk , 1, ... , bk , k)K
- Impostare B1 , 1= a1
- Se allora b 2 = ( a 2 , a 1 ) e ( c , d ) : = ( 2 , 1 ) altrimenti b 2 = ( a 1 , a 2 ) e ( c , d ) : = ( 1 , 2 ) . In entrambi i casi a d <un'2< a1B2= ( a2, a1)( c , d) : = ( 2 , 1 )b2=(a1,a2)(c,d):=(1,2) sarà sempre 0 (ovvero falso) per un comparatore non casuale.ad<ac0
- Per ottenere per k ≥ 3, ottenere prima b k - 1 .bkk≥3bk−1
- Sia e k ′ = 2 l , ovvero k ′ è la potenza minima di 2 non inferiore a k .l=⌈log2k⌉k′=2lk′2k
- Lasciate . Per ogni j ∈ { 1 , … , l } let
i j = { i j - 1 + 2 l - j i j - 1 + 2 l - j > k - 1 ∧ a d < a c i j - 1 i j - 1 + 2 l -i0=0j∈{1,…,l}
ij=⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪ij−1+2l−jij−1ij−1+2l−jij−1ij−1+2l−j>k−1∧ad<acij−1+2l−j>k−1∧¬(ad<ac)ij−1+2l−j≤k−1∧bk−1,ij−1+2l−j<akij−1+2l−j≤k−1∧¬(bk−1,ij−1+2l−j<ak)
- il>kbk=(bk−1,1,…,bk−1,il−1,ak,bk−1,il,…,bk−1,k−1)
- bn
k−1k
Si noti che questo algoritmo è inefficiente in entrambe le modalità rispetto allo shuffle Fisher-Yates e unisci l'ordinamento poiché l'inserimento di un elemento in una posizione arbitraria è costoso se l'utilizzo di un array e la ricerca binaria richiedono tempo lineare se si utilizza un elenco. Ma forse una modifica dell'ordinamento heap o dell'albero in un modo simile potrebbe portare a un algoritmo più veloce.