Autor: | Wojciech Muła |
---|---|
Dodany: | 9.03.2002 |
Sortowanie pozycyjne (ang. radix sort) jest jednym z efektywniejszych algorytmów sortowania. Jego złożoność obliczeniowa wynosi O(n), jego podstawową wadą są dość duże wymagania pamięciowe.
Poniżej przedstawiona została funkcja implementująca sortowanie pozycyjne o podstawie 2 (binarne).
// table - tablica // count - ilość elementów w tablicy // unsigned int ma 32 bity void radix_sort(unsigned int* table, int count) { unsigned int* temp0 = (unsigned int)malloc(count*sizeof(unsigned int)); unsigned int* temp1 = (unsigned int)malloc(count*sizeof(unsigned int)); unsigned int bit = 1; for (int i=1; i<32; i++) { unsigned int* t0 = temp0; unsigned int* t1 = temp1; for (int j=0; j<count; j++, table++) if (table[j] && bit) // rozdzielanie elementów *t1++ = table[j]; else *t0++ = table[j]; // tutaj następuje połączenie tablic temp0 i temp1 w jedną: int num0 = (int)t0-(int)temp0; int num1 = (int)t1-(int)temp1; memcpy(table, temp0, num0*sizeof(int)); memcpy(table+num0, temp1, num1*sizeof(int)); bit <<= 1; } free(temp0); free(temp1); }
Pamiętając, że sortowanych może być naprawdę dużo danych, należałoby jakoś wyeliminować wspomniane łączenie tablic. Poniższa funkcja pokazuje ideę proponowanej optymalizacji.
void sort(unsigned int* table, int count) { typedef unsigned int* ptr; ptr input0 = table; ptr input1 = (unsigned int)malloc(count*sizeof(unsignd int)); int count0 = count; int count1 = 0; ptr output1 = (unsigned int)malloc(count*sizeof(unsignd int)); ptr output1 = (unsigned int)malloc(count*sizeof(unsignd int)); unsigned int bit = 1; for (int i=0; i<32; i++) { ptr temp0 = output0; ptr temp1 = output1; for (int j=0; j<count0; j++) // sortujemy pierwszą if (input0[j] && bit) *temp1++ = input0[j]; else *temp0++ = input0[j]; for (int j=0; j<count1; j++) // i drugą cześć tablicy if (input1[j] && bit) *temp1++ = input1[j]; else *temp0++ = input1[j]; bit <<= 1; // teraz już nie kopiujemy danych, tylko zamieniamy wskaźniki // jednocześnie należy obliczyć nowe rozmiary tablic wejściowych count0 = (int)temp0 - (int)output0; count1 = (int)temp1 - (int)output1; ptr t; t = input0; input0 = output0; output0 = t; t = input1; input1 = output1; output1 = t; } // Dopiero na samym końcu trzeba połączyć tablice wyjściowe. // Jako że input0==table, tak więc pierwsza część tablicy // jest już "na miejscu". memcpy(table+count0, input1, count1); free(input1); free(output0); free(output1); }
Bez wątpienia wadą powyższego rozwiązania jest zwiększenie wymagań pamięciowych — o 33% większe niż w pierwszej wersji. O wiele istotniejszą zaletą jest jednak znaczne zmniejszenie ilości przenoszonych danych.
Koszt stały, wynikający z metody, to przesłanie count elementów w jednej iteracji. W przypadku pierwszej metody w każdej iteracji jest przesyłane dodatkowe count elementów.
Sumaryczna ilość danych przenoszona w trakcie sortowania to: