Porządkowanie w Pythonie
Treści zawarte w tym e‑materiale wykraczają poza podstawę programową. Jeśli jednak:
interesujesz się informatyką i programowaniem,
chcesz poszerzyć swoją wiedzę i umiejętności rozwiązywania problemów w języku Python,
poznać inne metody sortowania
zachęcamy cię do zapoznania się z tym e‑materiałem.
Codzienność wymaga skutecznego wyszukiwania i przyswajania informacji. Problemem jednak jest ilość danych, do których mamy dostęp, a także ich niedostateczne uporządkowanie. Jednym z podstawowych sposobów przetwarzania dużej liczby danych jest ich sortowanie. Dane uporządkowane względem jakiejś cechy, np. niemalejąco lub alfabetycznie, są łatwiejsze do przeszukania. W jednym z poprzednich materiałów poznaliśmy już algorytm sortowania przez wybieranie. W tym materiale zajmiemy się metodą porządkowania przez zliczanie.
Zliczanie ocen
Policzymy, ile poszczególnych ocen otrzymał uczeń w trakcie roku szkolnego. Wyniki przedstawimy w postaci listy (liczby wystąpień danej oceny) oraz listy uporządkowanych ocen.
Jak zrealizować takie zadanie przy użyciu kartki i długopisu? Najprostsza metoda polega na przeglądaniu kolejnych ocen i ich zliczaniu. Zliczanie może polegać na stawianiu kresek pod etykietami ocen, ale to wymagało będzie późniejszego ich policzenia. Możemy też zapisywać kolejną liczbę wystąpień danej oceny pod etykietą. Poniżej przykład wykorzystujący ten drugi sposób.
Przeanalizujmy zliczanie liczb. Chcemy zliczyć każde wystąpienie danej liczby z listy ocen:
oceny = [3, 2, 3, 1, 6, 4, 5, 4, 2, 5]
Przygotowujemy listę liczników, która posłuży nam do zliczania wystąpień ocen. Oceny będą indeksami liczników. Liczniki mają wartość zero.
Oceny = Indeksy | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
Liczniki | 0 | 0 | 0 | 0 | 0 | 0 |
Przeglądamy kolejne oceny i zwiększamy liczniki:
Indeksy liczników | |||||||
|---|---|---|---|---|---|---|---|
Krok | Ocena | 1 | 2 | 3 | 4 | 5 | 6 |
1 | 3 | 1 | |||||
2 | 2 | 1 | |||||
3 | 3 | 2 | |||||
4 | 1 | 1 | |||||
5 | 6 | 1 | |||||
6 | 4 | 1 | |||||
7 | 5 | 1 | |||||
8 | 4 | 2 | |||||
9 | 2 | 2 | |||||
10 | 5 | 2 | |||||
Liczniki: | 1 | 2 | 2 | 2 | 2 | 1 | |
Otrzymujemy listę, którą uzupełniamy o licznik zero, mimo że nie mamy takiej oceny. Robimy tak, ponieważ będziemy chcieli zapisać ten algorytm w postaci programu w języku Python. Do przechowywania ocen oraz liczników wykorzystamy listy, a one są indeksowane od 0:
liczniki = [0, 1, 2, 2, 2, 2, 1]
Teraz przeglądamy listę, zaczynając od drugiego elementu i sumujemy wartości aktualnego i poprzedzającego licznika. Uzyskaną sumę zapisujemy w aktualnym liczniku. W kolejnych krokach lista będzie wyglądała następująco.
Operacja: liczniki[1] = liczniki[1] + liczniki[0] = 1 + 0 = 1
Operacja: liczniki[2] = liczniki[2] + liczniki[1] = 2 + 1 = 3
Operacja: liczniki[3] = liczniki[3] + liczniki[2] = 2 + 3 = 5
Operacja: liczniki[4] = liczniki[4] + liczniki[3] = 2 + 5 = 7
Operacja: liczniki[5] = liczniki[5] + liczniki[4] = 2 + 7 = 9
Operacja: liczniki[6] = liczniki[6] + liczniki[5] = 1 + 9 = 10
W ten sposób zliczyliśmy liczniki.
Zliczenie liczników to jednak nie koniec. Konieczne jest posortowanie elementów na podstawie zliczonych liczników.
Obliczone wartości liczników pomniejszone o 1 wyznaczą ostatnią pozycję, na której należy umieścić porządkowaną wartość wskazywaną przez indeks licznika. Warto również zauważyć, że różnice między sąsiednimi licznikami informują, ile ocen należy umieścić w liście posortowanej.
Np. różnica między licznikiem o wartości 10, a licznikiem go poprzedzającym (o wartości 9) wynosi 1, co oznacza, że ocena 6 wystąpiła raz i powinna zostać umieszczona na pozycji (indeksie) 10 - 1, tj. na pozycji o indeksie 9 w liście posortowanej.
Przed przystąpieniem do sortowania przygotowujemy listę dla posortowanych ocen, która będzie zawierała tyle samo elementów, co pierwotna lista ocen.
Zaczynamy przeglądać listę ocen od końca. Ostatnią oceną jest ocena 5. Ocenę wykorzystujemy jako indeks, za pomocą którego odczytujemy licznik z listy liczników: liczniki[5] = 9. Uzyskana wartość pomniejszona o 1 wskazuje pozycję (indeks), na której umieszczamy ocenę w liście posortowanej: posortowane[9 - 1] = 5. Następnie zmniejszamy wartość licznika, aby wskazywał pozycję dla ewentualnej następnej takiej samej oceny: liczniki[5] -= 1. W kolejnym kroku otrzymujemy następujące listy:
Operację powtarzamy dla następnej oceny od końca, tj. 2. W kolejnym kroku listy przyjmą postać:
Po odczytaniu wszystkich ocen otrzymujemy uporządkowaną listę:
Indeksy | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
Oceny | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 |
Algorytm porządkowania metodą przez zliczanie
Napiszemy program, który uporządkuje w sposób niemalejący listę ocen z wykorzystaniem algorytmu porządkowania metodą przez zliczanie.
Algorytm porządkowania metodą przez zliczanie nadaje się do sortowania liczb całkowitych z dowolnego przedziału, w tym materiale podajemy specyfikację oraz implementację, zakładając, że porządkujemy liczby naturalne większe od 0.
Specyfikacja algorytmu:
Dane wejściowe:
oceny– lista ocen z zakresu<1;6>n– dodatnia liczba naturalna, liczba ocen
Wynik:
liczniki– lista zawierająca liczby wystąpień poszczególnych ocenposortowane– lista uporządkowanych niemalejąco ocen
Zmienne pomocnicze:
ocena– liczba naturalna z zakresu<1;6>– kolejne oceny z listyliczba_licznikow– liczba naturalna, liczba licznikówi– liczba naturalna, indeks elementów listy
Zapiszemy algorytm w postaci listy kroków.
Do
liczba_licznikowprzypisz wartośćmax(oceny) + 1.Utwórz listę
licznikizawierającąliczba_licznikowwartości0.Dla każdego elementu
ocenalistyocenywykonaj:3.1. Zwiększ wartość
liczniki[ocena]o1.Wypisz listę
liczniki.Dla elementów listy
licznikio indeksachiod1doliczba_licznikow - 1wykonaj:5.1. Zwiększ wartość
liczniki[i]o wartośćliczniki[i - 1].Utwórz listę
posortowanezawierającąnwartości0.Dla elementów listy
ocenyo indeksachiodn - 1do0wykonaj:7.1. Zmiennej
ocenaprzypisz wartośćoceny[i].7.2. Zmiennej
licznikprzypisz wartośćliczniki[ocena].7.3. Zmiennej
posortowane[licznik - 1]przypisz wartośćocena.7.4. Zmniejsz wartość
liczniki[ocena]o1.Wypisz listę
posortowane.
W algorytmie można wyróżnić kilka operacji:
Zliczanie – indeksy elementów listy
licznikiodpowiadają zliczanym wartościom, w tym wypadku ocenom. Np. w elemencieliczniki[1]będą zliczane oceny1. Indeks ostatniego elementu musi być więc równy maksymalnej zliczanej wartości –max(oceny), a liczba elementów listylicznikimusi być większa o1, ponieważ pierwszy element listy ma indeks0. Dodatkowo początkowe wartości liczników muszą równać się0. Z każdym wystąpieniem danej oceny, odpowiedni indeks listylicznikibędzie zwiększany o 1.Sumowanie – po zliczeniu sumujemy wartości licznika i licznika go poprzedzającego, zaczynając od licznika o indeksie
1, wynik zastępuje dotychczasową wartość licznika.Porządkowanie – odczytujemy z listy oceny zaczynając od ostatniej, z listy liczników odczytujemy wartość indeksu wskazaną przez ocenę, ocenę wstawiamy do listy
posortowanepod odczytanym indeksem pomniejszonym o jeden, indeks w liście liczników pomniejszamy o1i przechodzimy do kolejnej oceny.
Implementacja programu
Napiszemy teraz program, który zrealizuje opisany algorytm.
Do pisania programu użyj dostępnego w swoim środowisku edytora, np. IDLE dołączonego do standardowej instalacji Pythona.
Zaczniemy od kodu, w którym podamy listę ocen i ich liczbę.
Jeżeli liczba ocen nie będzie dana z góry, można ją wyznaczyć za pomocą funkcji len(), która zwraca liczbę elementów listy, np.:
Zliczanie ocen
Do zliczania użyjemy listy, której indeksy odpowiadają zliczanym wartościom. Każdy element listy – licznik – zainicjujemy wartością 0. Zadanie takie realizuje się zazwyczaj w pętli. W języku Python możliwe jest również użycie następującej instrukcji:
gdzie [wartość] oznacza pojedynczy element powielony w liście, a liczba określa, ile razy element [wartość] zostanie umieszczony na liście.
Wykonaj podane niżej polecenia w trybie interaktywnym interpretera Pythona (np. w IDLE) i odpowiedz na pytania.
Długość listy liczniki zależy od maksymalnej zliczanej wartości. Powyżej użyliśmy wbudowanej instrukcji max(), ale w implementacji użyjemy następującej funkcji:
Zakres w pętli for wykorzystuje zmienną n, czyli liczbę ocen, która została zdefiniowana globalnie poza funkcją znajdz_maks (w sekcji „Implementacja programu”). Użycie zmiennej n jest tutaj możliwe, ponieważ Python automatycznie przeszukuje zakresy, aby znaleźć zmienną o danej nazwie. Jeśli nie znajdzie jej w lokalnym zakresie (wewnątrz zdefiniowanej funkcji), sprawdzi globalny zakres (w całym programie).
Liczba liczników musi być większa o 1 od wartości zwróconej przez funkcję znajdz_maks(), ponieważ indeksy listy zaczynają się od 0.
W programie umieść poniższe instrukcje:
Pamiętaj, aby definicję funkcji znajdz_maks umieścić na początku programu (jest to zgodne z dobrymi praktykami).
Na końcu programu dodaj instrukcję iterującą po ocenach i zwiększającą licznik oceny w liście liczników. Po wykonaniu operacji zliczania wypisz listę liczniki.
Zliczanie polega na przeglądaniu ocen w pętli i inkrementacjiinkrementacji wskazanego przez ocenę elementu listy liczników o 1.
Użyj instrukcji for i funkcji print().
Następną operacją jest zaktualizowanie listy liczniki według zasady, że wartość licznika zastępowana jest jego wartością powiększoną o wartość licznika poprzedniego. W pętli for za pomocą funkcji range() generujemy indeksy od 1 do liczba_licznikow - 1, tj. 6. Wewnątrz pętli zwiększamy wartości liczników.
Do programu dopisz kod:
Porządkowanie ocen
Rozpoczynamy od przygotowania listy dla posortowanych ocen, której elementy inicjujemy wartością 0. Listę nazwiemy posortowane. Wykorzystujemy podobny kod, jak w przypadku listy liczniki. Do programu dopisujemy:
Następnie w pętli for przeglądamy listę oceny zaczynając od końca. Instrukcja range(n - 1, -1, -1) zwraca liczby od wartości n - 1, czyli indeksu ostatniego elementu listy ocen, do wartości 0 – indeksu pierwszej oceny, z krokiem -1.
Do programu dopisujemy:
W kolejnym kroku za pomocą zmiennej pomocniczejzmiennej pomocniczej ocena odczytujemy licznik, tj liczbę wystąpień tej oceny w liście oceny: licznik = liczniki[ocena]. Wartość licznika pomniejszona o jeden jest indeksem, pod którym umieszczamy ocenę w liście posortowanych ocen: posortowane[licznik - 1] = ocena. Na koniec dekrementujemydekrementujemy wartość licznika: liczniki[ocena] -= 1, aby można było wyznaczyć pozycję, na której należy umieścić kolejną taką samą ocenę.
Kod realizujący opisane operacje dopisujemy do instrukcji wykonywanych w omawianej pętli for. Po wykonaniu pętli wypisujemy listę posortowanych ocen:
Pełny kod programu:
Uruchom program i przeanalizuj wyniki jego działania.
Po uruchomieniu programu wyświetlone zostaną trzy zawartości listy liczniki. Druga z nich (wyświetlana za pomocą instrukcji z linii 20) to lista wynikowa, pozostałe dwie pozwalają ci przeanalizować działanie programu i jego poprawność.
O niektórych algorytmach sortowania możemy powiedzieć, że są stabilne. Oznacza to, że w przypadku tych algorytmów utrzymana jest kolejność występowania elementów o tej samej wartości klucza (cechy, według której odbywa się sortowanie). Ma to znaczenie w sytuacji, kiedy element poza kluczem, według którego sortujemy, zawiera również inne informacje.
Dane są pary liczb:
Naszym zadaniem jest posortowanie ich niemalejąco według pierwszego elementu każdej pary.
Wynik sortowania algorytmem stabilnym:
Zwróć uwagę na pary (2, 2) i (2, 5) oraz (3, 1) i (3, 3). W liście wejściowej para (2, 2) znajdowała się przed (2, 5), a (3, 1) przed (3, 3). Zostało to zachowane w liście wynikowej.
Notatnik
Gra edukacyjna
Zagraj w grę, następnie odpowiedz na pytanie.
Należy posortować bombki choinkowe, których rozmiary oznaczono liczbami: [9,3,9,3,7,3,7,5]. Przyjmij, że koszom z gry odpowiada lista liczników w algorytmie sortowania przez zliczanie. Podaj zawartość listy liczników po zliczeniu bombek.
Przykład
Zestaw bombek: [6,4,2,4,2,8,6]
Lista liczników po zliczeniu: [0,0,2,2,4,4,6,6,7]
Zestaw ćwiczeń interaktywnych
Do zakodowania algorytmu porządkowania metodą przez zliczanie użyjesz: Możliwe odpowiedzi: 1. instrukcji pobierania, 2. kilku pętli
for, 3. instrukcji warunkowej, 4. instrukcji dodającej elementy do listyDo przygotowania liczników dla wartości z zakresu
<0;10> użyjesz instrukcji: Możliwe odpowiedzi: 1. liczniki = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 2. text=liczniki = 10 * (0), 3. liczniki = 11 * [0], 4. liczniki = 11 * [0]Lista liczników po zsumowaniu indeksów wygląda następująco:
[0, 1, 4, 4, 6, 8, 11, 13, 13, 14]. Ile liczb o wartości 8 jest w porządkowanej liście? Możliwe odpowiedzi: 1. listę liczników należy przeglądać od końca, 2. wartości trzeba zliczać od najmniejszej, 3. trzeba dodać operację porównywaniaSpecyfikacja problemu:
Dane wejściowe:
nieposortowane– lista zawierająca nieposortowane liczby
Wyniki:
Wypisana lista liczb uporządkowanych w sposób niemalejący przy użyciu metody przez zliczanie.
Ćwiczenie wykonaj testerce.
Słownik
oznacza zmniejszanie wartości zmiennej o 1
oznacza zwiększenie wartości zmiennej o 1
zmienna tymczasowo przechowująca pośredni wynik obliczeń lub jakąś wartość