Począwszy od tego cytatu ze Skeeta:
Nie jest to sposób tasowania, który mi się podoba, głównie ze względu na to, że jest to O (n log n) bez dobrego powodu, kiedy łatwo jest zaimplementować tasowanie O (n). Kod w pytaniu „działa” w zasadzie nadając każdemu elementowi losową ( miejmy nadzieję unikalną! ) Liczbę, a następnie porządkując elementy według tego numeru.
Pójdę dalej, wyjaśniając powód, miejmy nadzieję, wyjątkowego!
Teraz z Enumerable.OrderBy :
Ta metoda wykonuje stabilne sortowanie; to znaczy, jeśli klucze dwóch elementów są równe, kolejność elementów zostaje zachowana
To jest bardzo ważne! Co się stanie, jeśli dwa elementy „otrzymają” tę samą liczbę losową? Zdarza się, że pozostają w tej samej kolejności, w jakiej znajdują się w tablicy. Jaka jest więc możliwość, że tak się stanie? Trudno jest dokładnie obliczyć, ale jest Problem Urodzinowy, który jest właśnie tym problemem.
Czy to jest prawdziwe? Czy to prawda?
Jak zawsze, jeśli masz wątpliwości, napisz kilka linii programu: http://pastebin.com/5CDnUxPG
Ten mały blok kodu tasuje tablicę 3 elementów określoną liczbę razy, używając algorytmu Fishera-Yatesa wykonanego wstecz, algorytmu Fishera-Yatesa wykonanego do przodu (na stronie wiki są dwa algorytmy pseudokodu ... wyników, ale jeden jest wykonywany od pierwszego do ostatniego elementu, a drugi od ostatniego do pierwszego elementu), naiwny zły algorytm http://blog.codinghorror.com/the-danger-of-naivete/ i przy użyciu .OrderBy(x => r.Next())
i .OrderBy(x => r.Next(someValue))
.
Teraz Random.Next jest
32-bitowa liczba całkowita ze znakiem, która jest większa lub równa 0 i mniejsza niż MaxValue.
więc jest to odpowiednik
OrderBy(x => r.Next(int.MaxValue))
Aby sprawdzić, czy ten problem istnieje, możemy powiększyć tablicę (coś bardzo wolnego) lub po prostu zmniejszyć maksymalną wartość generatora liczb losowych ( int.MaxValue
nie jest to „specjalna” liczba… To po prostu bardzo duża liczba). Ostatecznie, jeśli algorytm nie jest obciążony stabilnością wartości OrderBy
, to każdy zakres wartości powinien dać ten sam wynik.
Następnie program testuje niektóre wartości z zakresu 1 ... 4096. Patrząc na wynik, jest całkiem jasne, że dla niskich wartości (<128) algorytm jest bardzo obciążony (4-8%). Przy 3 wartościach potrzebujesz przynajmniej r.Next(1024)
. Jeśli powiększysz tablicę (4 lub 5), to nawet r.Next(1024)
nie wystarczy. Nie jestem ekspertem w tasowaniu i matematyce, ale myślę, że na każdy dodatkowy bit długości tablicy potrzebne są 2 dodatkowe bity o maksymalnej wartości (ponieważ paradoks urodzin jest powiązany z sqrt (numvalues)), więc że jeśli maksymalna wartość to 2 ^ 31, powiem, że powinieneś móc sortować tablice do 2 ^ 12/2 ^ 13 bitów (4096-8192 elementów)