Byłem niezmiernie sfrustrowany z wielu implementacji python radix sortowania tam w Internecie.Pchanie sortowanie Radix (i python) do jego granic
Konsekwentnie używają one pozycji dziesiętnej i otrzymują cyfry liczb, które powtarzają, dzieląc przez potęgę 10 lub biorąc log10 liczby. Jest to niezwykle nieefektywne, ponieważ log10 nie jest szczególnie szybką operacją w porównaniu do zmiany bitów, która jest prawie 100 razy szybsza!
O wiele skuteczniejsza implementacja wykorzystuje podstawienie 256 i sortuje liczbę bajtów według bajtów. Pozwala to na wykonanie "pobierania bajtów" za pomocą niewiarygodnie szybkich operatorów bitowych. Niestety, wydaje się, że absolutnie nikt tam nie zaimplementował sortowania radix w pythonie, który używa operatorów bitowych zamiast logarytmów.
Więc wziąłem sprawy w swoje ręce i wyszedł z tej bestii, czyli około połowy prędkości klasyfikowane na małych tablic i działa prawie tak szybko na większe (np len
około 10.000.000):
import itertools
def radix_sort(unsorted):
"Fast implementation of radix sort for any size num."
maximum, minimum = max(unsorted), min(unsorted)
max_bits = maximum.bit_length()
highest_byte = max_bits // 8 if max_bits % 8 == 0 else (max_bits // 8) + 1
min_bits = minimum.bit_length()
lowest_byte = min_bits // 8 if min_bits % 8 == 0 else (min_bits // 8) + 1
sorted_list = unsorted
for offset in xrange(lowest_byte, highest_byte):
sorted_list = radix_sort_offset(sorted_list, offset)
return sorted_list
def radix_sort_offset(unsorted, offset):
"Helper function for radix sort, sorts each offset."
byte_check = (0xFF << offset*8)
buckets = [[] for _ in xrange(256)]
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
return list(itertools.chain.from_iterable(buckets))
Ta wersja sortowania radix polega na znalezieniu bajtów, według których ma sortować (jeśli przekażesz tylko liczby całkowite poniżej 256, będzie sortować tylko jeden bajt, itd.), A następnie posortuj każdy bajt od LSB w górę przez zrzucenie ich w wiadra, aby następnie połączyć łańcuchy. Powtórz to dla każdego bajtu, który należy posortować, a masz ładną posortowaną tablicę w czasie O (n).
Nie jest to jednak tak szybkie, jak mogłoby być, i chciałbym je przyspieszyć, zanim napiszę o nim jako o lepszym sortowaniu radix niż wszystkie inne rodzaje radików.
Running cProfile
na to mówi mi, że dużo czasu wydawane są na metodzie append
na listach, co sprawia, że myślę, że tego bloku:
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
w radix_sort_offset
je dużo czasu. Jest to również blok, który, jeśli naprawdę na to patrzysz, stanowi 90% pracy dla całego rodzaju. Wygląda na to, że kod ten może być numpy
, co, jak sądzę, spowodowałoby znaczny wzrost wydajności. Niestety, nie jestem zbyt dobry z bardziej złożonymi funkcjami, więc nie byłem w stanie tego rozgryźć. Pomoc byłaby bardzo doceniana.
Obecnie używam itertools.chain.from_iterable
do spłaszczenia buckets
, ale jeśli ktoś ma szybszą sugestię, jestem pewien, że to również pomogłoby.
Oryginalnie miałem funkcję get_byte
, która zwróciła jeden bajt numeru o numerze n
, ale podkreślenie kodu dało mi ogromną poprawę prędkości, więc zrobiłem to.
Wszelkie inne uwagi dotyczące wdrożenia lub sposoby zwiększenia wydajności są również mile widziane. Chcę usłyszeć cokolwiek i wszystko, co masz.
Dobre rzeczy. Prowadzi to do dość silnych przyspieszeń i umożliwia sortowanie radix sortowane na liście o wartości 10 000 000 z radikami 4096, ale powoduje to, że na krótkich listach jest ono zawstydzająco słabe. EDIT: Właśnie zdałem sobie sprawę, że jesteś facetem, który napisał timsort. Mój kapelusz jest dla ciebie, sir. – reem
Heh - Założę się, że nie masz żadnych ujemnych liczb całkowitych na tej liście ;-) Sortowanie Radix jest świetne, ale bit-fiddling staje się trudniejszy, gdy przechodzisz poza nie-negatywne int. l BTW, napisałem 'list.sort()' w języku Python i nie obrażam się, że twój jest szybszy :-) –