To jest długi tekst. Proszę o wyrozumiałość. Sprowadzone pytanie brzmi: czy istnieje praktyczny algorytm sortowania radix w miejscu ?
Wstępny
Mam ogromną liczbę małych ciągów o stałej długości, które używają tylko liter „A”, „C”, „G” i „T” (tak, zgadłeś: DNA ), które chcę posortować.
W tej chwili używam, std::sort
który wykorzystuje introsort we wszystkich popularnych implementacjach STL . To działa całkiem dobrze. Jestem jednak przekonany, że sortowanie radix idealnie pasuje do mojego zestawu problemów i powinno działać znacznie lepiej w praktyce.
Detale
Przetestowałem to założenie z bardzo naiwną implementacją i przy stosunkowo niewielkich nakładach (rzędu 10 000) było to prawdą (cóż, przynajmniej dwa razy szybciej). Jednak środowisko wykonawcze obniża się gwałtownie, gdy rozmiar problemu staje się większy ( N > 5 000 000).
Powód jest oczywisty: sortowanie radix wymaga skopiowania całych danych (tak naprawdę więcej niż raz w mojej naiwnej implementacji). Oznacza to, że umieściłem ~ 4 GiB w mojej głównej pamięci, co oczywiście zabija wydajność. Nawet jeśli nie, nie mogę sobie pozwolić na użycie tak dużej ilości pamięci, ponieważ rozmiary problemów stają się jeszcze większe.
Przypadków użycia
Idealnie, ten algorytm powinien działać z dowolną długością łańcucha od 2 do 100, zarówno dla DNA, jak i DNA5 (co pozwala na dodatkowy znak wieloznaczny „N”), a nawet DNA z kodami niejednoznaczności IUPAC (co daje 16 różnych wartości). Zdaję sobie jednak sprawę, że nie można uwzględnić wszystkich tych przypadków, więc cieszę się z każdej poprawy prędkości, jaką otrzymuję. Kod może dynamicznie decydować, do którego algorytmu wysłać.
Badania
Niestety artykuł Wikipedii na temat sortowania radix jest bezużyteczny. Część dotycząca wariantu na miejscu to kompletne śmieci. Sekcja NIST-DADS na temat sortowania radix jest prawie nieistniejąca. Istnieje obiecująco brzmiący artykuł o nazwie Efficient Adaptive In-Place Radix Sorting, który opisuje algorytm „MSL”. Niestety, ten artykuł również rozczarowuje.
W szczególności są następujące rzeczy.
Po pierwsze, algorytm zawiera kilka błędów i pozostawia wiele niewyjaśnionych. W szczególności nie wyszczególnia wywołania rekurencyjnego (po prostu zakładam, że zwiększa lub zmniejsza wskaźnik, aby obliczyć bieżące wartości przesunięcia i maski). Korzysta także z funkcji dest_group
i dest_address
nie podaje definicji. Nie widzę, jak efektywnie je wdrożyć (to znaczy w O (1); przynajmniej dest_address
nie jest to trywialne).
Na koniec algorytm osiąga miejsce w miejscu, zamieniając indeksy tablic na elementy wewnątrz tablicy wejściowej. To oczywiście działa tylko na tablice numeryczne. Muszę go używać na ciągach. Oczywiście mógłbym po prostu mocno wkręcić i pisać dalej, zakładając, że pamięć będzie tolerować przechowywanie indeksu, do którego on nie należy. Ale to działa tylko tak długo, jak długo mogę wycisnąć moje ciągi do 32 bitów pamięci (zakładając 32-bitowe liczby całkowite). To tylko 16 znaków (zignorujmy na razie, że 16> log (5 000 000)).
Kolejny artykuł jednego z autorów nie zawiera żadnego dokładnego opisu, ale podaje środowisko wykonawcze MSL jako sublinearne, co jest całkowicie błędne.
Podsumowując : Czy jest jakaś nadzieja na znalezienie działającej implementacji referencyjnej lub przynajmniej dobrego pseudokodu / opisu działającego na miejscu sortowania radix, który działa na łańcuchach DNA?