Rys historyczny

Początki sortowania pozycyjnego sięgają XIX wieku, kiedy to w roku 1890 Herman Hollerith po raz pierwszy zastosował maszynę licząco‑analityczną do opracowania spisu ludności USA. Wynalazek swój opatentował w 1889 r.

R18gYKRa2sxqT
Maszyna licząco‑analityczna
Źródło: dostępny w internecie: epo.org, tylko do użytku edukacyjnego.

Sortowanie pozycyjne dat – omówienie

Algorytm, którym się zajmujemy, jest bardzo podobny do algorytmu sortowania pozycyjnego słówPXnUDqfi5algorytmu sortowania pozycyjnego słów oraz sortowania pozycyjnego liczbPjBWIbxmPsortowania pozycyjnego liczb.

Algorytm sortowania pozycyjnego dat polega na sortowaniu dat według cyfr na kolejnych pozycjach, rozpoczynając od najmniej znaczącej (czyli cyfry jedności dnia). Aby było to możliwe, przyjmujemy format zapisu daty, obsługiwany przez algorytm. W tym materiale wybieramy standard ISO 8601. Jednym ze sposobów zapisu daty w tym formacie jest postać RRRR‑MM‑DD.

RRRR oznacza czterocyfrowy zapis roku, np. 1998. MM oznacza dwucyfrowy zapis miesiąca, np. czerwiec zapiszemy jako 06. DD oznacza dwucyfrowy zapis dnia w danym miesiącu, np. 19. Takim sposobem otrzymujemy jednoznaczny zapis daty: 1998‑06‑19 (19 czerwca 1998 r.).

Ważne jest, aby nie pomijać wiodących zer. Błędem będzie zapisanie daty 1998‑06‑19 jako 1998‑6-19.

Przydatność tego zapisu w algorytmie wynika z następujących cech:

  • cyfry są uszeregowane od najmniej znaczącej do najbardziej znaczącej (od prawej do lewej),

  • długość daty jest stała i wynosi 10,

  • separatory (znak myślnika) występują na z góry określonych pozycjach: jest to czwarty i siódmy indeks, przy założeniu, że liczymy od zera.

Przy tak reprezentowanej dacie algorytm sortowania pozycyjnego sprowadza się do sortowania dat według kolejnych pozycji od najmniej znaczącej do najbardziej znaczącej cyfry (od prawej do lewej), z pominięciem separatorów na odpowiednich indeksach.

Omówmy przykładowe zadanie, w którym naszym celem jest posortowanie podanego zbioru dat w kolejności niemalejącej przy użyciu algorytmu sortowania pozycyjnego.

Przykład 1

Z = {1960‑03‑21, 2003‑04‑09, 1526‑01‑30, 1680‑07‑14, 2050‑11‑02, 1250‑12‑17, 1337‑09‑05}

Daty przechowywane są zgodnie z wcześniej przyjętym formatem: RRRR‑MM‑DD.

Sortując daty, rozpatrujemy kolejne cyfry zapisu, rozpoczynając od najmniej znaczącej. Najpierw korzystając z pomocniczego stabilnego algorytmu sortowaniastabilny algorytm sortowaniastabilnego algorytmu sortowania, sortujemy daty kolejno według:

  • cyfry jedności liczby reprezentującej dni,

  • cyfry dziesiątek liczby reprezentującej dni,

R16nMWBEDrjSu
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.
  • cyfry jedności liczby reprezentującej miesiąc,

  • cyfry dziesiątek liczby reprezentującej miesiąc,

RRRYt3bgLe1dT
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.
  • cyfry jedności liczby reprezentującej rok,

  • cyfry dziesiątek liczby reprezentującej rok,

  • cyfry setek liczby reprezentującej rok,

  • cyfry tysięcy liczby reprezentującej rok.

RnC55wcZCwzUW
Źródło: Contentplus.pl Sp. z o.o., licencja: CC BY-SA 3.0.

Po wykonaniu ostatniego kroku lista dat jest posortowana.

Zasada działania algorytmu sortowania pozycyjnego dat

Do posortowania elementów na konkretnych, odpowiadających sobie pozycjach w datach możemy użyć dowolnego algorytmu sortowania stabilnegostabilny algorytm sortowaniaalgorytmu sortowania stabilnego.

Algorytm pomocniczy musi być stabilnym algorytmem sortowania – w przeciwnym wypadku sortowanie dat według kolejnych pozycji nie miałoby sensu. Np. przy sortowaniu dat: 1998‑11‑05 i 1998‑11‑06 po poprawnym posortowaniu według cyfry jedności dni, algorytm bez gwarancji stabilności mógłby zamienić daty, sortując według pozostałych, jednakowych cyfr. W konsekwencji: również sam algorytm sortowania pozycyjnego jest stabilny.

W tym e‑materiale jako algorytm pomocniczy wybieramy sortowanie kubełkowe, ze względu na znaną z góry liczbę kubełków równą 10 (liczba cyfr w systemie dziesiętnym). Algorytm ten został dokładnie omówiony w e‑materiale Sortowanie kubełkowePPpmzST7zSortowanie kubełkowe.

Na początku wydzielamy kubełki, które posłużą za miejsce do przechowania liczb w trakcie wykonywanych operacji. Każdy kubełek oznaczamy jedną cyfrą. Musimy mieć przewidziany oddzielny kubełek dla każdej z cyfr w zależności od zastosowanego systemu liczbowego, w którym zapisana jest data.

Kubełki służą do tego, by w momencie wykonywania operacji przechować daty, które mają tę samą cyfrę na analizowanej pozycji (czyli np. w wyniku analizy cyfr jedności w liczbie dni takie daty jak 1995‑12‑25 oraz 2001‑09‑15 znalazłyby się w jednym kubełku). Następnie „wyjmujemy” wszystkie daty z kubełków w kolejności, w jakiej je tam wstawiliśmy. Dzięki temu możemy mówić o posortowaniu liczb stabilnie według odpowiedniej cyfry (w tym przykładzie według cyfry jedności w liczbie dni).

Wielokrotne powtórzenie tego procesu dla kolejnych cyfr (od najmniej do najbardziej znaczącej) pozwoli szybko otrzymać oczekiwane rezultaty. Zasada gospodarowania kubełkami jest prosta: istnieje tyle kubełków, ile jest cyfr w analizowanym systemie liczbowym. Następnie sprawdzamy, jaka cyfra znajduje się na aktualnie rozpatrywanej pozycji, i dopasowujemy ją do kubełka.

Przykład sortowania pozycyjnego dat z użyciem sortowania kubełkowego

Posortujmy niemalejąco następujący zestaw dat:

  • 1960‑03‑21

  • 2003‑04‑09

  • 1526‑01‑30

  • 1680‑07‑14

  • 2050‑11‑02

  • 1250‑12‑17

  • 1337‑09‑05

Algorytm będzie pracował na datach zapisanych w zbiorze dat. Wygląda on następująco:

AIndeks dolny 0 = {1960‑03‑21, 2003‑04‑09, 1526‑01‑30, 1680‑07‑14, 2050‑11‑02, 1250‑12‑17, 1337‑09‑05}

Tak przygotowany zbiór dat możemy zacząć sortować przy użyciu pomocniczego, stabilnego algorytmu sortowania kubełkowego. Umieszczamy daty w kubełkach na podstawie aktualnie sprawdzanej cyfry. Zaczynamy od ostatniej cyfry daty, czyli mniej znaczącej cyfry opisującej dzień:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

1526‑01‑30

1960‑03‑21

2050‑11‑02

1680‑07‑14

1337‑09‑05

1250‑12‑17

2003‑04‑09

Następnie wyjmujemy daty z kubełków, zaczynając od kubełka 0:

AIndeks dolny 1 = {1526‑01‑30, 1960‑03‑21, 2050‑11‑02, 1680‑07‑14, 1337‑09‑05, 1250‑12‑17, 2003‑04‑09}

Posortowaliśmy daty według cyfry jedności dnia. Teraz powtarzamy proces dla przedostatniej pozycji, sortując przy tym daty według cyfr dziesiątek w numerze oznaczającym dzień:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

2003‑04‑09 1337‑09‑05 2050‑11‑02

1250‑12‑17 1680‑07‑14

1960‑03‑21

1526‑01‑30

Ponownie wyjmujemy daty z każdego kubełka. Niektóre kubełki zawierają więcej niż jedną wartość. W takim przypadku ważne jest, abyśmy wyciąganie zaczynali od daty umieszczonej najwcześniej, czyli zapisanej najniżej. Jest to istotne dla zachowania zasady sortowania stabilnego:

AIndeks dolny 2 = {2050‑11‑02, 1337‑09‑05, 2003‑04‑09, 1680‑07‑14, 1250‑12‑17, 1960‑03‑21, 1526‑01‑30}

Następnie przetwarzamy pozycję, na której w przyjętym zapisie daty znajduje się znak „-”. Pomijamy go i przechodzimy do przetwarzania kolejnej pozycji.

Docieramy do porządkowania według cyfry jedności liczby miesiąca:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

1526‑01-30 2050‑11-02

1250‑12-17

1960‑03-21

2003‑04-09

1680‑07-14

1337‑09-05

Umieszczamy daty w tablicy zgodnie z zasadami:

AIndeks dolny 3 = {2050‑11‑02, 1526‑01‑30, 1250‑12‑17, 1960‑03‑21, 2003‑04‑09, 1680‑07‑14, 1337‑09‑05}

Ponownie umieszczamy wszystkie daty w kubełkach, tym razem zwracając uwagę na bardziej znaczącą cyfrę miesiąca:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

1337‑09‑5 1680‑07‑14 2003‑04‑09 1960‑03‑21 1526‑01‑30

1250‑12‑17 2050‑11‑02

Zbiór dat ma obecnie postać:

AIndeks dolny 4 = {1526‑01‑30, 1960‑03‑21, 2003‑04‑09, 1680‑07‑14, 1337‑09‑05, 2050‑11‑02, 1250‑12‑17}

Ponownie na kolejnej pozycji znajduje się znak „-”, więc pomijamy go.

Następnie przechodzimy do sortowania według cyfry jedności roku:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

1250‑12‑17 2050‑11‑02 1680‑07‑14 1960‑03‑21

2003‑04‑09

1526‑01‑30

1337‑09‑05

Ponownie wyjmujemy wszystkie daty. Teraz zbiór dat prezentuje się następująco:

AIndeks dolny 5 = {1960‑03‑21, 1680‑07‑14, 2050‑11‑02, 1250‑12‑17, 2003‑04‑09, 1526‑01‑30, 1337‑09‑05}

Wykonanie algorytmu dla cyfry dziesiątek roku poskutkuje powstaniem kubełków:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

2003‑04‑09

1526‑01‑30

1337‑09‑05

1250‑12‑17 2050‑11‑02

1960‑03‑21

1680‑07‑14

Otrzymujemy następującą listę:

AIndeks dolny 6 = {2003‑04‑09, 1526‑01‑30, 1337‑09‑05, 2050‑11‑02, 1250‑12‑17, 1960‑03‑21, 1680‑07‑14}

Przedostatni przebieg algorytmu (dla cyfry setek roku) utworzy następującą strukturę:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

2050‑11‑02 2003‑04‑09

1250‑12‑17

1337‑09‑05

1526‑01‑30

1680‑07‑14

1960‑03‑21

Po wypisaniu dat w odpowiedniej kolejności otrzymujemy:

AIndeks dolny 7 = {2003‑04‑09, 2050‑11‑02, 1250‑12‑17, 1337‑09‑05, 1526‑01‑30, 1680‑07‑14, 1960‑03‑21}

Pozostaje ostatnie powtórzenie, w którym uwzględniamy cyfrę tysięcy roku:

Kubełek

0

1

2

3

4

5

6

7

8

9

Zawartość

1960‑03‑21 1680‑07‑14 1526‑01‑30 1337‑09‑05 1250‑12‑17

2050‑11‑02 2003‑04‑09

Wynikowy zbiór ma postać:

AIndeks dolny wyjściowa = {1250‑12‑17, 1337‑09‑05, 1526‑01‑30, 1680‑07‑14, 1960‑03‑21, 2003‑04‑09, 2050‑11‑02}

Jak widać, algorytm spełnił swoje zadanie, wykonując tylko osiem powtórzeń algorytmu sortowania kubełkowego (tyle, ile jest cyfr w dacie).

Pseudokod

Zgodnie z założeniami pseudokod będzie działał na datach zapisanych w formacie RRRR‑MM‑DD. Dla innego formatu należałoby go odpowiednio dostosować.

Specyfikacja problemu:

Dane:

  • rozmiar – liczba całkowita przechowująca informację dotyczącą liczby sortowanych dat

  • tablica[0..rozmiar - 1] – tablica jednowymiarowa przechowująca sortowane daty w formie napisu; daty w tablicy przechowywane są jako napisy w formacie RRRR‑MM‑DD

Wynik:

  • tablica[0..rozmiar - 1] – zawiera daty posortowane chronologicznie (od najwcześniejszej do najpóźniejszej)

Aby posortować daty pozycyjnie, utworzymy pętlę dla uwzględniającą wszystkie pozycje, według których powinniśmy posortować daty w tablicy tablica[]. Jest ich 10. Pominiemy również separatory znajdujące się w każdej dacie na indeksach 4 i 7 (licząc od 0).

Linia 1. dla pozycja znak równości 9 przecinek 8 przecinek kropka kropka kropka przecinek 0 wykonuj dwukropek. Linia 2. jeżeli pozycja ≠ 4 i pozycja ≠ 7 dwukropek. Linia 3. sortowanie podkreślnik kubełkowe otwórz nawias okrągły pozycja zamknij nawias okrągły.

Jak widać, skorzystaliśmy z pomocniczego sortowania kubełkowego. Przedstawmy jego zapis w postaci pseudokodu.

Niech tablica kubełki[0..9] będzie tablicą list tymczasowo przechowujących rozpatrywane aktualnie daty. ListalistaLista jest obiektem, który działa na podobnej zasadzie co tablica, lecz nie musi mieć stałego rozmiaru – można zarówno dopisywać, jak i wyciągać z niej kolejne liczby.

Funkcja kod(znak) zamienia znak znak na jego kod ASCII.

Linia 1. funkcja sortowanie podkreślnik kubełkowe otwórz nawias okrągły pozycja zamknij nawias okrągły dwukropek. Linia 2. dla n znak równości 0 przecinek 1 przecinek kropka kropka kropka przecinek rozmiar minus 1 wykonuj dwukropek. Linia 3. prawy ukośnik prawy ukośnik konwertuj znak tablica otwórz nawias kwadratowy n zamknij nawias kwadratowy otwórz nawias kwadratowy pozycja zamknij nawias kwadratowy na liczbę z zakresu otwórz nawias ostrokątny 0 przecinek 9 zamknij nawias ostrokątny. Linia 4. cyfra ← kod otwórz nawias okrągły tablica otwórz nawias kwadratowy n zamknij nawias kwadratowy otwórz nawias kwadratowy pozycja zamknij nawias kwadratowy zamknij nawias okrągły minus kod otwórz nawias okrągły cudzysłów 0 cudzysłów zamknij nawias okrągły. Linia 5. umieść wewnątrz kubełki otwórz nawias kwadratowy cyfra zamknij nawias kwadratowy wartość tablica otwórz nawias kwadratowy n zamknij nawias kwadratowy. Linia 7. x ← 0. Linia 8. dla cyfra znak równości 0 przecinek 1 przecinek kropka kropka kropka przecinek 9 wykonuj. Linia 9. dopóki kubełki otwórz nawias kwadratowy cyfra zamknij nawias kwadratowy zawiera liczby wykonuj. Linia 10. tablica otwórz nawias kwadratowy x zamknij nawias kwadratowy ← pobierz najwcześniej dodaną wartość z kubełki otwórz nawias kwadratowy cyfra zamknij nawias kwadratowy i usuń ją. Linia 11. x ← x plus 1.

Cechy sortowania pozycyjnego dat

Sortowanie pozycyjne jest wydajną metodą sortowania liczb naturalnych o podobnej liczbie cyfr. Dlatego też świetnie nadaje się do sortowania dat, które w większości przypadków będą liczbami dokładnie ośmiocyfrowymi (z pominięciem ewentualnych separatorów takich jak myślniki czy kropki).

Sortowanie pozycyjne jest stabilne. A zatem jeśli w zbiorze wejściowym wystąpią powtarzające się daty, ich kolejność względem siebie się nie zmieni.

Oszacujmy złożoność czasowązłożoność czasowazłożoność czasową tego algorytmu sortowania. Wprowadźmy następujące oznaczenia:

  • – liczba dat do posortowania,

  • – liczba cyfr w danym formacie daty bez separatorów.

Rozpoczynamy od analizy liczby operacji dodawania i usuwania dat z kubełków w funkcji sortowanie_kubełkowe(). Instrukcje dodawania elementów do kubełków wykonają się  razy. Instrukcje usuwania elementów z kubełka umieszczone w pętli dopóki wykonają się również  razy (tyle, ile liczb znalazło się sumarycznie w kubełkach). Zatem złożoność obliczeniowa tej funkcji to .

Główna pętla programu wykona się razy. Pomijając separatory, będzie to .

W niepominiętych iteracjach (ich liczba wynosi ) wywołana zostanie funkcja sortowanie_kubełkowe(), której złożoność to . Zatem złożoność całego programu to .

Jeżeli przyjmiemy  jako stałą równą 8, to sortowanie pozycyjne dat ma złożoność liniową.

Słownik

lista
lista

struktura danych służąca do reprezentacji zbiorów dynamicznych, w wykorzystaniu praktycznym różni się od tablicy tym, że jej elementy można na bieżąco modyfikować

stabilny algorytm sortowania
stabilny algorytm sortowania

algorytm sortowania gwarantujący zachowanie kolejności elementów o tej samej wartości w tablicy wynikowej, identycznej jak w tablicy wejściowej

złożoność czasowa
złożoność czasowa

cecha algorytmu określająca tendencję, z jaką rośnie czas potrzebny na wykonanie algorytmu wraz ze wzrostem rozmiaru danych