Na granicy możliwości
Algorytm to sposób rozwiązania pewnego problemu. Informatyka i matematyka od dawna badają różnego rodzaju problemy, szukając dla nich algorytmów, najczęściej możliwie szybkich. My jednak tym razem postąpimy wręcz przeciwnie: zajmiemy się algorytmami wyjątkowo wolnymi.
Najpierw wypada powiedzieć, że istnieją problemy, dla których w ogóle nie istnieje algorytm, który je rozwiązuje. Nie tylko nie znamy ani jednego, ale nawet potrafimy udowodnić, że żadnego nie ma. Takie problemy nazywa się nierozstrzygalnymi; najsławniejszy to problem stopu. Formułuje się go tak: dany jest pewien algorytm i należy stwierdzić, czy program, który będzie go realizował, kiedyś się zatrzyma. Nierozstrzygalność tego problemu oznacza więc, że nie istnieje żaden ogólny sposób, żeby stwierdzić, czy program, któremu właśnie się przyglądamy, zatrzyma się w pewnym momencie czy też będzie pracował w nieskończoność.
Tym razem zajmiemy się jednak metodą konstruowania algorytmów dla problemów, które co prawda można rozwiązać, ale nie istnieje dla nich żaden algorytm działający w jakimkolwiek rozsądnym czasie. O takich problemach mówimy, że nie są pierwotnie rekurencyjne. Można śmiało stwierdzić, że są one prawie nierozstrzygalne – na granicy możliwości rozwiązania. Żeby lepiej wyobrazić sobie, jak dziwne muszą być takie zagadnienia, sprecyzujmy, co rozumiemy przez problem, który nie jest pierwotnie rekurencyjny. Powiedzmy, że dane wejściowe są wielkości jeśli mają bitów – w tym sensie, na przykład, liczba
da się zapisać na 11 bitach, czyli ma wielkość 11.
Zdefiniujmy teraz funkcje gdzie przebiega zbiór liczb naturalnych. Przyjmujemy oraz
dla Czyli
Można sobie tylko próbować wyobrazić, co dzieje się dalej. Funkcje, dla których istnieje oszacowanie z góry przez pewną funkcję (dla ), nazywamy funkcjami pierwotnie rekurencyjnymi. Zatem funkcje, które nie są pierwotnie rekurencyjne, to takie, które rosną jeszcze szybciej niż funkcje Przykład funkcji, która nie jest pierwotnie rekurencyjna, to funkcja Czytelnik Wnikliwy może się przekonać, że istotnie dla każdej funkcji istnieje takie że Podobnie definiuje się inny znany przykład: funkcję Ackermanna.
Powiemy też, że problem nie jest pierwotnie rekurencyjny, jeśli nie istnieje żaden algorytm dla tego problemu, który dla każdych danych wielkości wykonuje co najwyżej operacji dla pewnych O dziwo, znamy jednak techniki, które pozwalają nam projektować algorytmy działające w czasie nieograniczonym przez funkcję pierwotnie rekurencyjną. Najpierw przedstawmy jednak głównego bohatera, czyli badany problem.
Będziemy rozważać wektory składające się z liczb naturalnych, czyli elementy zbioru Dany jest wektor początkowy wektor końcowy oraz skończony zbiór wektorów krokowych Zwróćmy uwagę na to, że w wektorach krokowych dopuszczamy ujemne współrzędne! Pytanie brzmi: czy możemy w skończonej liczbie ruchów dojść z punktu do punktu o ile mamy do dyspozycji następujące ruchy:
- 1.
- dodanie wektora krokowego, o ile to nie spowoduje, że wyjdziemy poza : gdzie ;
- 2.
- zmniejszenie którejś współrzędnej, o ile to nie spowoduje, że wyjdziemy poza : gdzie
Jeśli istnieje takie przejście, to nazwiemy je ścieżką z do
Dla powyższego problemu można podać algorytm działający w czasie rzędu (czyli ten problem jest pierwotnie rekurencyjny). Ale właśnie ten przykład świetnie ilustruje techniki, za pomocą których rozwiązuje się nawet dużo trudniejsze problemy, w tym takie, które nie są pierwotnie rekurencyjne.
Na początek przedstawimy nasze główne narzędzie: lemat Dicksona. Powiemy, że wektor jest mniejszy lub równy niż wektor jeśli dla każdej współrzędnej ; piszemy wtedy
Lemat 1 (Dicksona). Niech będzie nieskończonym ciągiem wektorów z Wówczas istnieją pewne dwa indeksy takie, że
Algorytm sprawdzający, czy istnieje ścieżka z do składa się z dwóch procedur działających równocześnie (lub – jak kto woli – wykonujących kroki na zmianę). Pierwsza – nazwijmy ją pozytywną – usiłuje znaleźć ścieżkę z do (czyli konstruktywny dowód na to, że taka istnieje), druga natomiast – nazwijmy ją negatywną – próbuje znaleźć dowód, że nie ma żadnej ścieżki z do Istotne jest, żebyśmy dobrze zaprojektowali obie procedury: jeśli szukana ścieżka istnieje, to procedura pozytywna musi w pewnym momencie ją znaleźć, a jeśli ścieżka nie istnieje, to procedura negatywna ma w pewnym momencie otrzymać dowód tego faktu. Wówczas w każdym przypadku jedna z procedur się zatrzyma i nasz algorytm zakończy się, zwracając właściwą odpowiedź. Zauważmy, że a priori nie mamy żadnego oszacowania na to, kiedy program zakończy działanie, i dla niektórych problemów rzeczywiście oczekiwanie na ten moment będzie trwać bardzo długo.
Zaprojektowanie procedury pozytywnej jest łatwe. Po prostu sprawdzane są wszystkie możliwe ciągi ruchów, zaczynając od najkrótszych, a potem przegląda się coraz dłuższe. Dla każdego z nich testujemy, czy może przypadkiem prowadzi on z do i nie powoduje nigdzie po drodze zejścia poniżej zera.
Problem leży w konstrukcji procedury negatywnej. Jak w ogóle może wyglądać dowód nieistnienia ścieżki między danymi punktami? Rozważmy zbiór wszystkich takich wektorów że istnieje z nich ścieżka do wektora końcowego Zauważmy, że zbiór jest zamknięty w górę: jeśli oraz dla pewnego wektora to również Istotnie, możemy bowiem z łatwo dojść do (zmniejszając niektóre współrzędne), a stąd do Jeżeli z nie da się dojść do to zbiór spełnia następujące warunki:
- ;
- jest zamknięty w górę;
- nie da się wejść spoza do
Taki zbiór nazwiemy separatorem. Dodatkowo, zauważmy, że jeśli istnieje jakikolwiek separator, to nie da się dojść z do Tu właściwie wystarczą tylko pierwszy i trzeci warunek, drugi przyda się jedynie do szukania separatora. Podsumowując, separator istnieje wtedy i tylko wtedy, gdy nie ma ścieżki z do Procedura negatywna będzie zatem szukała separatora.
Jak to zrealizować? Nie można przecież przeszukiwać wszystkich zbiorów wektorów. Po pierwsze, taki zbiór może być nieskończony (czyli nie damy rady zapamiętać wszystkich elementów). Poza tym nie da się wszystkich zbiorów ustawić w ciąg i posprawdzać po kolei (jest ich nieprzeliczalnie wiele) i właściwie nie wiadomo, jak testować warunek drugi i trzeci. Musimy koniecznie znaleźć przynajmniej jakiś sposób zapamiętania takiego zbioru w skończonej pamięci. Teraz właśnie przychodzi nam z pomocą lemat Dicksona.
Rozważmy zbiór minimalnych wektorów danego separatora – wektor minimalny to taki, że separator nie zawiera elementów mniejszych od niego w porządku Dzięki lematowi Dicksona wiemy, że jest ich skończenie wiele. Przypuśćmy bowiem, że mamy nieskończenie wiele wektorów minimalnych. Ustawiamy je w ciąg i z lematu Dicksona dostajemy, że dla pewnych indeksów Mamy sprzeczność z minimalnością ! Zatem każdy separator składa się ze skończenie wielu elementów minimalnych oraz wszystkich wektorów większych od któregoś z nich. Możemy go zatem reprezentować poprzez zbiór
Procedura negatywna przeszukuje więc wszystkie skończone zbiory wektorów i sprawdza, czy zbiór wszystkich wektorów większych od któregoś z nich nie jest separatorem. Z podanych powyżej trzech warunków drugi jest spełniony zawsze. Pierwszy sprawdza się łatwo, wystarczy porównać współrzędne wektorów i wektorów reprezentujących separator. Trzeci również można rozstrzygać w miarę nietrudno – Czytelnik Ambitny może śmiało spróbować to udowodnić.
Tym samym zakończyliśmy opis obu procedur, a więc i algorytmu. Najistotniejszym spostrzeżeniem było, że elementów minimalnych w separatorze jest skończenie wiele. To typowa sytuacja: kluczem do zaprojektowania algorytmu, który zawsze się zatrzymuje, ale, być może, działa bardzo wolno, jest obserwacja, że pewien obiekt pojawiający się w analizie problemu jest skończony. Do otrzymywania takich wyników nadaje się znakomicie lemat Dicksona lub następujący lemat Higmana, stosowany w przypadku słów, a nie wektorów.
Lemat 2 (Higmana). Niech będzie nieskończonym ciągiem słów nad skończonym alfabetem. Wówczas istnieją takie indeksy że słowo zawiera się w słowie (czyli ciąg liter słowa jest podciągiem ciągu liter słowa ).
Na pierwszy rzut oka widać podobieństwo do lematu Dicksona. Tak naprawdę oba te lematy są szczególnymi przypadkami pewnych bardziej ogólnych twierdzeń. To już jednak temat na zupełnie inną opowieść.