Я был очень расстроен тем, что многие из реализаций python radix сортировались там в Интернете.Pushing Radix Sort (и python) до его пределов
Они последовательно используют радиус 10 и получают цифры номеров, которые они перебирают, делясь на мощность 10 или принимая log10 номера. Это невероятно неэффективно, поскольку log10 не является особенно быстрой операцией по сравнению с сдвигом бит, что почти в 100 раз быстрее!
В гораздо более эффективной реализации используется радиус 256 и сортируется побайтовый байт. Это позволяет выполнить все «байтовую обработку» с использованием смехотворно быстрых операторов бит. К сожалению, кажется, что абсолютно никто там не реализовал сортировку radix в python, которая использует битовые операторы вместо логарифмов.
Итак, я взял дело в свои руки и придумал этот зверь, который проходит около половины скорости сортируется на небольших массивов и работает почти так же быстро, на более крупных (например, len
вокруг 10000000):
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))
Эта версия сортировки radix работает путем определения того, какие байты она должна сортировать (если вы передадите ей только целые числа ниже 256, она будет сортировать только один байт и т. Д.), А затем сортировать каждый байт из LSB, сбросив их в ковши для того, чтобы просто соединить ведра вместе. Повторите это для каждого байта, который нужно отсортировать, и у вас есть хороший отсортированный массив в O (n) времени.
Однако это не так быстро, как могло бы быть, и я хотел бы сделать это быстрее, прежде чем писать об этом как о лучшем радисе, чем все остальные радики.
Запуск cProfile
на это говорит мне, что много времени тратится на методе append
для списков, что заставляет меня думать, что этот блок:
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
в radix_sort_offset
ест много времени. Это также блок, который, если вы действительно смотрите на него, выполняет 90% работы для всего вида. Этот код выглядит так, как будто это может быть numpy
-ized, что, я думаю, приведет к значительному повышению производительности. К сожалению, я не очень хорош с более сложными функциями numpy
, поэтому не смог понять это. Помощь будет очень оценена.
В настоящее время я использую itertools.chain.from_iterable
, чтобы сгладить buckets
, но если у кого есть более быстрое предложение, я уверен, что это тоже поможет.
Первоначально у меня была функция get_byte
, которая вернула n
-й байт числа, но вставка кода дала мне огромное ускорение скорости, поэтому я сделал это.
Любые другие комментарии к реализации или способы выжать больше производительности также приветствуются. Я хочу услышать что угодно и все, что у тебя есть.
Хороший материал. Это приводит к довольно сильным ускорениям и позволяет этой сортировке radix сортироваться по списку в 10 000 000 с основанием 4096, хотя это делает его плохо сбитым в коротких списках. EDIT: Просто понял, что вы парень, который написал timsort. Моя шляпа с тобой, сэр. – reem
Хе-хе-х, у вас нет никаких отрицательных целых чисел в этом списке ;-) Сорт Radix замечательный, но бит-скриптинг становится сложнее, когда вы выходите за пределы неотрицательных ints. l BTW, я написал 'list.sort()' Python, и я не обижаюсь, что ваш быстрее :-) –