Sono stato immensamente frustrato con molte delle implementazioni di Python Radix sort là fuori sul web.Pushing Radix Sort (e python) ai suoi limiti
Essi utilizzano costantemente una radice di 10 e ottengono le cifre dei numeri su cui iterano suddividendo dividendo per una potenza di 10 o prendendo il log10 del numero. Questo è incredibilmente inefficiente, dato che log10 non è un'operazione particolarmente veloce rispetto al cambio di bit, che è quasi 100 volte più veloce!
Un'implementazione molto più efficiente utilizza una radice di 256 e ordina il numero byte per byte. Ciò consente di eseguire tutto il "byte get" utilizzando gli operatori di bit ridicolmente rapidi. Sfortunatamente, sembra che nessuno in assoluto abbia implementato un ordinamento per la radix in python che utilizza operatori bit anziché logaritmi.
Così, ho preso le cose nelle mie mani e si avvicinò con questa bestia, che corre a circa la metà della velocità di ordinato in piccoli array e funziona quasi più rapidamente su quelli più grandi (ad esempio len
intorno 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))
Questa versione di radix sort funziona individuando quali byte deve ordinare (se si passano solo interi inferiori a 256, si ordina solo un byte, ecc.) Quindi si ordina ciascun byte da LSB verso l'alto scaricandoli nei secchi per poi mettere insieme i secchi. Ripeti questo per ogni byte che deve essere ordinato e hai la tua bella matrice ordinata in tempo O (n).
Tuttavia, non è veloce come potrebbe essere, e mi piacerebbe renderlo più veloce prima di scrivere su di esso come una sorta di radix migliore rispetto a tutti gli altri tipi di radix in circolazione.
esecuzione cProfile
su questo mi dice che un sacco di tempo viene speso sul metodo append
per gli elenchi, che mi fa pensare che questo blocco:
for num in unsorted:
byte_at_offset = (num & byte_check) >> offset*8
buckets[byte_at_offset].append(num)
in radix_sort_offset
sta mangiando un sacco di tempo. Questo è anche il blocco che, se lo si guarda veramente, fa il 90% del lavoro per l'intero genere. Sembra che questo codice potrebbe essere numpy
-ized, che a mio avviso porterebbe a un notevole incremento delle prestazioni. Sfortunatamente, non sono molto bravo con le funzioni più complesse di numpy
, quindi non sono stato in grado di capirlo. L'aiuto sarebbe molto apprezzato
Attualmente sto usando itertools.chain.from_iterable
per appiattire il buckets
, ma se qualcuno ha un suggerimento più veloce sono sicuro che sarebbe di aiuto pure.
Originariamente, avevo una funzione get_byte
che restituiva il numero n
di un numero, ma l'inlining del codice mi ha dato un enorme aumento di velocità, quindi l'ho fatto.
Anche altri commenti sull'implementazione o sui modi per spremere più prestazioni sono apprezzati. Voglio sentire tutto e tutto quello che hai.
Roba buona. Questo porta a velocizzazioni abbastanza forti e permette a questo ordinamento di radix di battere ordinati su una lista di 10.000.000 di lunghezza con una radice di 4096, anche se questo lo rende piuttosto imbarazzante in poche liste. EDIT: Ho appena capito che sei il ragazzo che ha scritto Timsort. Il mio cappello è pronto per te, signore. – reem
Heh - Scommetto che non ci sono numeri interi negativi in quella lista ;-) L'ordinamento di Radix è ottimo, ma il bit-trick diventa più complicato quando si passa oltre gli interi non negativi. l BTW, ho scritto Python's 'list.sort()', e non sono offeso che il tuo sia più veloce :-) –