Przeskocz do treści

Delta mi!

Jak znaleźć „second min”?

Jakub Radoszewski

o artykule ...

  • Publikacja w Delcie: maj 2012
  • Publikacja elektroniczna: 28-04-2012
  • Wersja do druku [application/pdf]: (99 KB)

Jednym z pierwszych zadań, z jakimi musi zmierzyć się każdy uczący się algorytmów lub programowania, jest znajdowanie minimum w tablicy (ciągu liczb). Oznaczmy taką tablicę przez math

Poszukiwanie zaczynamy, naturalnie, od elementu math  który staje się kandydatem na minimum. Jeśli teraz math  to math  pozostaje kandydatem na minimum, a jeśli, przeciwnie,  math  to kandydatem na minimum staje się math  Łatwo zobaczyć, co będzie dalej: każdy kolejny element tablicy, math  porównujemy z obecnym kandydatem na minimum i aktualizujemy tegoż kandydata, jeśli math okazał się od niego mniejszy (patrz pseudokod na marginesie).

Łatwo oszacować, ile operacji wykonujemy w tym algorytmie: mamy dokładnie math porównań oraz co najwyżej math przypisań. Co prawda, powyższy algorytm jest naprawdę prosty, ale zawsze można dla pewności spytać: czy dałoby się lepiej? Na przykład, czy można znaleźć minimum w tablicy, wykonując mniej niż  math porównań?

obrazek

Rys. 1 Graf porównań odpowiadający tablicy math  po wykonaniu porównań: math z math math  z  math math  z  math math z  math  i math  z  math

Rys. 1 Graf porównań odpowiadający tablicy math  po wykonaniu porównań: math z math math  z  math math  z  math math z  math  i math  z  math

Intuicyjnie widać, że nie, czyli że trzeba wykonać co najmniej math porównań. Tę intuicję można także wesprzeć formalnym uzasadnieniem. Ustalmy pewien algorytm wyznaczający minimum w tablicy math  Dla danego przebiegu (wykonania) tego algorytmu możemy skonstruować graf porównań, którego wierzchołki math reprezentują numery elementów w tablicy, a krawędź łączy dwa wierzchołki wtedy, gdy odpowiadające im elementy zostały porównane w tym przebiegu algorytmu (patrz Rys. 1). Wystarczy teraz zauważyć, że jeśli na końcu takiego przebiegu graf porównań nie jest spójny, to rozważany algorytm nie może być poprawny: bez dodatkowych porównań nie jesteśmy w stanie stwierdzić, w której spójnej składowej faktyczne minimum się znajduje. Ponieważ graf spójny o math wierzchołkach musi mieć co najmniej math krawędzi, więc rzeczywiście potrzebnych jest math porównań.

Skoro wiemy już wszystko o wyznaczaniu minimum, to może teraz zastanowimy się, jak efektywnie znaleźć drugi co do wielkości element tablicy? Będzie to właśnie tytułowe second min.

Najprościej wykorzystać poprzedni algorytm: znajdujemy minimum, usuwamy je z tablicy (np. odpowiednio je oznaczając), po czym znajdujemy minimum wśród pozostałych elementów tablicy. W ten sposób wykonujemy math porównania. Można to jednak zrobić lepiej, jeśli tylko wykorzystamy inny algorytm wyszukiwania minimum.

obrazek

Rys. 2 Turniej związany z tablicą math  Zwycięzcą (czyli minimum) jest math

Rys. 2 Turniej związany z tablicą math  Zwycięzcą (czyli minimum) jest math

Pokażemy, jak znaleźć second min za pomocą math porównań. Zaczynamy od zbudowania turnieju, czyli „drabinki meczów” między elementami tablicy math : w każdym „meczu” mniejszy element wygrywa i w ten sposób wyłaniany jest zwycięzca, czyli minimum. Gdyby math było potęgą dwójki, ów turniej moglibyśmy reprezentować jako pełne drzewo binarne; w ogólnym przypadku na ostatnim poziomie drzewa mogą występować luki (patrz Rys. 2).

Widzimy, że za pomocą turnieju znów udaje nam się wyznaczyć minimum w tablicy za pomocą math porównań (meczów). Rzeczywiście, po każdym porównaniu jeden element odpada z turnieju. A gdzie w drabince turniejowej znajduje się element second min? Otóż mógł on przegrać w turnieju jedynie z elementem min – no bo z nikim innym. Na każdym poziomie drabinki jest co najwyżej jeden element, który przegrał z minimum, czyli łącznie mamy co najwyżej math kandydatów na second min. Przykładowo, w turnieju z rysunku 2 kandydatami na second min są math   math  i math  Wystarczy zatem wybrać minimum spośród tych właśnie elementów, do czego, jak wiemy, potrzeba już tylko math porównań. Łącznie wykonaliśmy math porównań, czyli tyle, ile chcieliśmy.

Naturalnie nasuwa się pytanie, czy da się lepiej, tzn. czy aby na pewno przy wyznaczaniu second min trzeba wykonać math porównań. W związku z uwagą na marginesie warto tu zadać bardziej precyzyjne pytanie: czy każdy algorytm znajdujący second min wykonuje w pesymistycznym przypadku co najmniej math porównań? Innymi słowy, czy każdemu takiemu algorytmowi możemy tak złośliwie dobrać dane wejściowe, żeby musiał wykonać podaną liczbę porównań? Odpowiedź na to pytanie jest twierdząca, co spróbujemy uzasadnić. Odtąd założymy dla uproszczenia, że wszystkie elementy w tablicy math  są różne; poniższe rozumowanie można lekko zmodyfikować tak, aby działało także w przypadku powtarzających się elementów w math  Przede wszystkim musimy wyjaśnić jedną rzecz: każdy algorytm wyznaczający second min musi przy okazji znaleźć element min, tzn. na końcu jego działania musi być jednoznacznie wyznaczone, który element tablicy jest minimalny. Faktycznie, gdyby na końcu algorytmu wciąż byli dwaj kandydaci na minimum (czyli mniejsi od wszystkich elementów, z którymi byli porównywani), to któryś z nich mógłby równie dobrze być elementem second min.

Aby dokończyć rozumowanie, musimy pokazać pewną kluczową i trochę nieintuicyjną własność. Otóż w każdym algorytmie wyznaczającym minimum w tablicy (a więc także w każdym wyznaczającym second min) element min jest porównywany w pesymistycznym przypadku co najmniej math razy.

Zanim to pokażemy, zastanówmy się, co nam to da. Przyjrzyjmy się grafowi porównań algorytmu wyznaczającego second min. Wiemy – a dokładniej, będziemy wiedzieli, jak udowodnimy – że w pesymistycznym przypadku z elementu min wychodzi w tym grafie co najmniej math krawędzi. Usuńmy z grafu element min; reszta musi pozostać spójna, gdyż w przeciwnym przypadku mielibyśmy w niej więcej niż jednego kandydata na second min. Stąd, reszta grafu musi zawierać co najmniej math krawędzie, czyli łącznie musi być co najmniej tyle krawędzi, ile należało pokazać.

Pozostał nam już tylko dowód tajemniczej własności wszystkich algorytmów wyznaczających minimum. Teraz całą sytuację przedstawimy jako grę...

Mamy dwoje graczy. Pierwszy gracz będzie symulował działanie algorytmu wyszukującego minimum, czyli będzie zadawał pytania postaci: „Czy math?”. Zawartość tablicy math  nie jest jednak znana a priori. Drugi gracz odpowiada na pytania pierwszego, przy czym jego odpowiedzi nie mogą nigdy być sprzeczne – to znaczy, że po każdej sekwencji jego odpowiedzi musi istnieć zawartość tablicy math zgodna ze wszystkimi odpowiedziami, jakich udzielił. Celem pierwszego gracza jest wyznaczyć minimum w tablicy, wykonując mniej niż math porównań dotyczących min, a celem drugiego – spowodować, żeby pierwszy gracz musiał zadać co najmniej math pytań dotyczących min.

Podamy teraz strategię wygrywającą dla drugiego gracza. Z każdym elementem tablicy math  zwiążemy licznik math ; początkowo wszystkie liczniki są ustawione na 1. Będziemy dbali o to, aby wszystkie liczniki były zawsze nieujemne i całkowite oraz aby ich suma była równa math Będziemy też utrzymywać niezmiennik, że wszystkie dodatnie liczniki odpowiadają kandydatom na minimum, czyli elementom, które w żadnym porównaniu nie okazały się większe.

Metoda odpowiadania na pytania jest następująca: jeśli pierwszy gracz chce porównać elementy math  i math  to odpowiadamy, że mniejszy jest ten element, który ma nie mniejszy licznik. Wówczas licznik mniejszego elementu zwiększamy o wartość licznika większego elementu, a drugi z tych liczników zerujemy (patrz przykład na rysunku 3). Jeśli tylko przed wykonaniem zapytania co najmniej jeden z liczników math  math był niezerowy, to postępując zgodnie z tą metodą, na pewno nigdy nie udzielimy odpowiedzi sprzecznej z poprzednimi, gdyż porównanie wygra kandydat na minimum. A jeżeli oba liczniki były zerowe, to musimy po prostu odpowiedzieć tak, żeby nie skłamać: jeśli na podstawie dotychczasowych odpowiedzi można wywnioskować, który z elementów math   math  jest mniejszy, to odpowiadamy zgodnie z prawdą, a jeśli nie, to odpowiadamy jakkolwiek.

Łatwo sprawdzić, że podana metoda spełnia wszystkie żądane niezmienniki. Na końcu gry element minimalny musi mieć licznik równy math a pozostałe liczniki muszą być zerowe. A ponieważ po każdym pytaniu licznik elementu minimalnego może się co najwyżej podwoić, więc łącznie wykonaliśmy na tym liczniku co najmniej math operacji. To kończy dowód!