Rekurencja - jak wszyscy wiemy - jest jednym z tych problemów - że otulenie głowy wydaje się osiągnięciem „kamienia milowego” w podróży programistycznej.
Ale jeśli chodzi o faktyczne wykorzystanie go w rzeczywistych problemach - znajomość mechaniki rekurencji NIE wystarcza - należy także zrozumieć naturę problemów, w których rekurencja jest najbardziej odpowiednim rozwiązaniem.
Moje pytanie brzmi więc ...
- jakie są „wzorce problemów”, które wymagają rozwiązania rekurencji
- jest rekursją formą strategii „dziel i rządź” lub formą „ponownego wykorzystania kodu” - lub jest wzorzec projektowy sam w sobie
- czy możesz podać nam przykład rzeczywistego problemu, w którym rekurencja przychodzi na myśl jako natychmiastowe rozwiązanie
-- AKTUALIZACJA --
wiele odpowiedzi odnosi się do „prawdziwych problemów”, takich jak przemierzanie drzewa, silnia itp. Wolałbym „PRAWDZIWE prawdziwe problemy” - dam wam przykład ...
Mieliśmy DUŻĄ porcję tekstu (około 30 MB tekstu jako połączonej listy structs
) i musieliśmy stworzyć jego indeks do przeszukiwania pełnego tekstu. Musieliśmy zachować cały indeks w pamięci i ponownie indeksować tekst co 10 minut.
Co 10 minut porównujemy cały tekst (dwie połączone listy, linia po linii) z nowo wygenerowanym fragmentem tekstu - aby zobaczyć, który wiersz został zmieniony - a następnie ponownie indeksujemy tylko ten wiersz - w ten sposób moglibyśmy uniknąć konieczności ponownego indeksowania CAŁEGO tekstu. Pamiętaj - musieliśmy znaleźć punkty różnic między dwiema połączonymi listami o wielkości 30 MB.
Jeden z moich kolegów wymyślił fantastyczny program, który wykorzystywał rekurencję HEAVY do porównywania linii - a następnie zbierał pozycje, w których uchwyty różniły się w tablicy - tak, wiem, że to brzmi zagadkowo - jak tu może pomóc rekurencja - ale to zrobiło.
Chodzi o to - jak mógł zobaczyć, że problem ten można rozwiązać mądrze przy użyciu rekurencji?
Odpowiedzi:
Nie powiedziałbym, że istnieje coś takiego jak wzorzec problemowy dla użycia rekurencji. Każda funkcja, która może być zaimplementowana za pomocą rekurencji, może być również zaimplementowana iteracyjnie, często przez wypychanie i otwieranie stosu.
To kwestia ekspresji, a także wydajności. Algorytmy iteracyjne często mają lepszą wydajność i łatwiej je zoptymalizować. Jednak algorytmy rekurencyjne korzystają z wyraźniejszego wyrażenia i dlatego często są łatwiejsze do odczytania, zrozumienia i wdrożenia.
Niektórych rzeczy nawet nie da się wyrazić bez rekurencji, na przykład nieskończonych list. Tak zwane języki funkcjonalne w dużej mierze polegają na rekurencji, ponieważ jest to ich naturalny sposób wyrażania. Mówi się: „Programowanie rekurencyjne to prawidłowe funkcjonowanie programowania”.
Nie nazwałbym tego wzorem. To kwestia ekspresji. Czasami wyrażenie rekurencyjne jest po prostu mocniejsze i bardziej ekspresyjne, a zatem prowadzi do lepszego i czystszego kodu.
Wszystko, co musi przejść przez drzewa, zostanie poprawnie wyrażone przez algorytm rekurencyjny.
źródło
Ani. Dziel i rządź używa rekurencji. Ale rekurencja niekoniecznie dzieli i zwycięża, ponieważ ta ostatnia oznacza podzielenie problemu na dwie (lub więcej) części i rozwiązanie każdej z nich symetrycznie. W rekursji tego nie robisz.
Ponowne użycie kodu jest całkowicie niezwiązane, a wzorzec projektowy wchodzi w grę na znacznie wyższym poziomie. Na przykład nawet dzielenie i podbijanie (również wzorzec wyższego poziomu niż rekurencja) nadal nie jest uważane za wzorzec projektowy - jest raczej wzorcem algorytmicznym .
Przechodzenie przez drzewo. Lub bardziej ogólnie przejście przez wykres. Dotyczy to w szczególności przejścia przez strukturę folderów.
I oczywiście wszystko, co wykorzystuje program dziel i zwyciężaj lub programowanie dynamiczne, ponieważ oba są naturalnie wyrażane w kategoriach rekurencji.
źródło
Wywodząc się z samopodobieństwa fraktali, powiedziałbym, że równość lub tożsamość (lub jak się to nazywa) jest typowym wzorem problemu dla rekurencji. Tj. Problem można podzielić na podproblemy, które mają tę samą strukturę co problem główny.
We wspomnianym przejściu do drzewa każde pod-drzewo samo w sobie jest pełnym drzewem, podobnie jak główne drzewo, a główne drzewo może być poddrzewem w innym drzewie.
Sądzę więc, że twój kolega odkrył właściwości równości twojego problemu z indeksowaniem. Albo odwrócił się i przekształcił problem w samowystarczalną reprezentację.
źródło
Cóż, rekurencję można łatwo zrozumieć, jeśli spróbujemy przekształcić imperatywne pętle w funkcje funkcjonalne. W każdym razie spróbujmy odpowiedzieć na wszystkie pytania:
Jeśli masz strukturę lub algorytm przypominający drzewo, potrzebujesz rekurencji. Jeśli twój imperatywny kod dotyczy stosu, będziesz potrzebować rekurencji. Jeśli określony krok w algorytmie zależy od poprzednich kroków (pętli), potrzebujesz rekurencji. Potrzebę tutaj należy interpretować jako możliwą do wykorzystania.
Żaden. Dziel i rządź używa rekurencji, ale może być implementowane za pomocą stosów. Ponowne użycie kodu odnosi się do czegoś innego. Wzory projektowe są bardziej skomplikowane niż zwykła rekurencja.
Analiza składniowa i wszystko, co dotyczy struktur drzewiastych. Nawet ukryte struktury drzew.
źródło
Jeśli istnieje sposób na uproszczenie go tak, aby był łatwy, może to być wskazówka, że rekurencja może zadziałać. Możesz podjąć sortowanie i poszukiwanie przykładów, w których istnieją rozwiązania rekurencyjne, takie jak Merge Sort i Binary Search .
Należy pamiętać o tym, że niektóre problemy można słabo rozwiązać za pomocą rekurencji jak czynnik.
Jeśli chodzi o przykład z prawdziwego świata, w którym użyłbym rekurencji, wyszukiwanie książki z półki na książki można zrobić dość łatwo w sposób rekurencyjny. Patrzę tylko na książkę i jeśli nie jest to, czego chcę, przechodzę do następnej. Zatrzymuję się, gdy albo znajduję książkę, albo trafiam na koniec rzędu. Zapętlenie sprawdzania książki i przejścia do następnej można wykonać rekurencyjnie. Być może jest to zbyt prawdziwy przykład.
źródło
Mówiąc bardzo ogólnie, rekurencja jest wymagana, gdy rozwiązujesz problem, w którym f (x) = f (g (x)) . Jeśli nie masz nic przeciwko nieskończonej rekurencji, g (x) nie powinno oceniać na x .
Żadne z powyższych. To tylko sposób na powtarzanie tego samego, czasami w oparciu o zmiany na wejściu. Pomysł był o wiele dłuższy niż wzorce projektowe, ponowne użycie kodu, a nawet komputery.
Czynniki, sekwencję Fibonacciego, przechodzenie przez drzewa i wiele innych problemów można rozwiązać za pomocą recusrionu. Rekurencja w znaczeniu funkcji wywołującej samą siebie niekoniecznie jest najlepszym sposobem na zaimplementowanie tego rodzaju rzeczy; istnieją inne sposoby osiągnięcia tego samego efektu (np. stos i pętla), które mogą być bardziej pożądane.
źródło
Kiedy piszesz algorytm rekurencyjny, zwykle tłumaczysz rekurencyjną definicję problemu na kod. Wtedy nawet nie musisz wiedzieć, jak zostanie to wykonane.
Tak dzieje się w programowaniu funkcjonalnym. W rzeczywistości określasz, co (definicja), a nie jak . Innymi słowy, definiujesz podstawę, a następnie definiujesz swój problem w ramach podproblemu.
Rozważmy na przykład
Factorial
algorytmNastępnie, gdy napotkasz problem, powinieneś pomyśleć, czy możesz go zdefiniować rekurencyjnie, czy nie, jeśli możesz zdefiniować go rekurencyjnie, prawie go rozwiązałeś.
Jednak żadna funkcja rekurencyjna nie powinna być definicją rekurencyjną. Możesz zdefiniować podstawę i powiązać (zdefiniować) rozwiązanie głównego problemu z rozwiązaniem (definicją) podproblemów. Ale dla tej relacji może być potrzebna procedura.
Przykładem
MergeSort
, w którymmerge
może być konieczne procedury odnoszą się do definicji lub roztwór sortowania całą tablicę do sortowania tablic podrzędnych.źródło