![]() |
Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2010 mgr
Jerzy Wałaszek
|
Pomoce:
Biblioteka procedur obsługi konsoli znakowej - newconio
Rekurencja powstaje wtedy, gdy funkcja wywołuje samą siebie do rozwiązania części problemu. Rekurencję często stosuje się w różnych algorytmach w informatyce (niektóre z nich bez rekurencji nie są w stanie efektywnie działać - np. algorytmy grafowe). Każde wywołanie funkcji w języku C++ odkłada na stosie procesora tzw. adres powrotny (miejsce w programie, do którego powraca procesor po wykonaniu kodu funkcji). Również wszystkie zmienne lokalne są tworzone na tym stosie. Zatem jeśli funkcja wywoływała by samą siebie w "nieskończoność", to już po kilkuset milionach wywołań stos procesora rozrósłby się na całą dostępną dla programu pamięć. Dlatego rekurencja musi posiadać ściśle określony koniec, tzn. zaprzestanie dalszych wywołań i zakończenie funkcji.
Dla przykładu utwórzmy program wyliczający rekurencyjnie silnię n!. Silnia n! to iloczyn kolejnych liczb naturalnych od 1 do n:
n! = 1 × 2 × ... × (n - 1) × n.
Np. 5! = 1 × 2 × 3 × 4 × 5 = 120
Silnię możemy zdefiniować rekurencyjnie:
1! = 1
n! = n × (n - 1)!
Zatem silnia np. 5! jest równa 5 × 4!. Aby ją policzyć musimy znaleźć 4!, co jest równe 4 × 3!. Postępując tak aż do jeden otrzymamy kolejne wartości silni, które później należy jedynie przez siebie przemnożyć:
5! = 5 × 4!
5! = 5 × 4 × 3!
5! = 5 × 4 × 3 × 2!
5! = 5 × 4 × 3 × 2 × 1! - koniec rekurencji,
ponieważ 1! = 1.
// (C)2010 Koło Informatyczne I LO
//
// Silnia liczona rekurencyjnie
//--------------------------------
#include <iostream>
using namespace std;
unsigned long long silnia(unsigned long long n)
{
if(n == 1) return 1;
else return n * silnia(n - 1);
}
int main()
{
unsigned long long n;
cin >> n;
cout << silnia(n) << endl;
return 0;
}
|
Zadanie polega na rozmieszczeniu na szachownicy 8 hetmanów w taki
sposób, aby się na wzajem nie atakowały. Dodatkowo będziemy sprawdzać liczbę
takich układów dla szachownic o rozmiarze
![]() |
|||||||
![]() |
|||||||
![]() |
|||||||
![]() |
|||||||
![]() |
|||||||
![]() |
|||||||
![]() |
Zadanie rozwiążemy rekurencyjnie w sposób następujący:
Najpierw w pierwszym wierszu ustawimy hetmana na pierwszym polu szachownicy:
| 1 |
![]() |
|||||||
| 2 | ||||||||
| 3 | ||||||||
| 4 | ||||||||
| 5 | ||||||||
| 6 | ||||||||
| 7 | ||||||||
| 8 |
Następnie przejdziemy do wiersza drugiego i znajdziemy w nim takie pole, które nie jest atakowane przez hetmana w wierszu pierwszym:
| 1 |
![]() |
|||||||
| 2 | ||||||||
| 3 | ||||||||
| 4 | ||||||||
| 5 | ||||||||
| 6 | ||||||||
| 7 | ||||||||
| 8 |
Postępując podobnie w wierszu trzecim znajdziemy pole, które nie atakują hetmany z wierszy 1 i 2:
| 1 |
![]() |
|||||||
| 2 | ||||||||
| 3 |
![]() |
|||||||
| 4 | ||||||||
| 5 | ||||||||
| 6 | ||||||||
| 7 | ||||||||
| 8 |
I analogicznie postępujemy w wierszu czwartym:
| 1 |
![]() |
|||||||
| 2 | ||||||||
| 3 |
![]() |
|||||||
| 4 |
![]() |
|||||||
| 5 | ||||||||
| 6 | ||||||||
| 7 | ||||||||
| 8 |
Oraz w kolejnych wierszach postępujemy podobnie. Jeśli dotrzemy do 8 wiersza i postawimy w nim hetmana, to układ mamy gotowy i możemy go zaprezentować. Jeśli w danym wierszu nie możemy postawić hetmana, bo wszystkie pola są atakowane, to wracamy do poprzedniego wiersza i szukamy w nim nowego pola do postawienia hetmana, po czym wznawiamy wypełnianie wierszy hetmanami.
Zwróć uwagę, iż można ten proces sprowadzić do rekurencji:
Jeśli hetmany ustawione są we wszystkich wierszach szachownicy, to prezentujemy układ i kończymy rekurencję.
W przeciwnym razie w wierszu i-tym szukamy kolejnych pól, które nie są atakowane przez hetmany z wierszy poprzednich. Dla każdego takiego pola wywołujemy rekurencyjnie ten sam algorytm z numerem wiersza o 1 większym od bieżącego.
Do reprezentowania szachownicy w pamięci komputera użyjemy prostej tablicy ph[] (pozycje hetmanów). Zwróć uwagę, że w każdej kolumnie może znajdować się dokładnie jeden hetman. Zatem komórki tej tablicy będą reprezentowały numer wiersza szachownicy, w którym w danej kolumnie znajduje się hetman, Na przykład:
| ph | 3 | 6 | 2 | 7 | 1 | 4 | 8 | 5 |
| 1 |
![]() |
|||||||
| 2 | ||||||||
| 3 |
![]() |
|||||||
| 4 |
![]() |
|||||||
| 5 | ![]() |
|||||||
| 6 |
![]() |
|||||||
| 7 | ![]() |
|||||||
| 8 | ![]() |
Umówmy się, iż brak hetmana w kolumnie będziemy zaznaczali liczbą 0. Sytuacja taka ma miejsce, gdy w danym wierszu nie umieściliśmy jeszcze hetmana.
Kolejnym problemem będzie ustalenie, czy dane pole w w-tym wierszu jest atakowane przez hetmany w wierszach poprzedzających. Wystarczy wykryć jedną taką sytuację, aby pole zostało zdyskwalifikowane. Po pierwsze wybieramy pola i-te w w-tym wierszu, dla których odpowiadająca im komórka ph[i] zawiera wartość 0. Jeśli jest tam inna wartość, to w danej kolumnie jest już hetman i na tym polu nie możemy postawić następnego. Gdy znajdziemy takie pole, to przeglądamy kolejne komórki ph[j]. Jeśli zawierają wartość różną od zera, to musimy sprawdzić, czy reprezentujący ją hetman nie atakuje naszego pola po przekątnej. Będzie tak, jeśli odległość w kolumnach od tego hetmana i naszego pola będzie równa odległości w wierszach - wtedy oba hetmany są na tej samej przekątnej szachownicy i się atakują:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |
| 1 | ||||||||
| 2 |
![]() |
|||||||
| 3 | ||||||||
| 4 | ||||||||
| 5 |
![]() |
|||||||
| 6 | ||||||||
| 7 | ||||||||
| 8 |
Odległość w kolumnach = 7 - 4 = 3
Odległość w wierszach = 5 - 2 = 3
// (C)2010 Koło Informatyczne I LO
//
// Problem hetmanów
//------------------
#include "newconio.h"
#include <iostream>
using namespace std;
const int N = 12; // rozmiar szachownicy
int ph[N+1]; // tablica pozycji hetmanów
int licznik; // zlicza znalezione układy hetmanów
// funkcja wypisuje szachownicę z hetmanami
//-----------------------------------------
void szachownica()
{
gotoxy(0,0);
textcolor(WHITE);
cout << "\nPOZYCJA " << ++licznik << endl << endl;
textcolor(BLACK);
for(int i = 1; i <= N; i++)
{
for(int j = 1; j <= N; j++)
{
if((i + j) % 2) textbackground(YELLOW);
else textbackground(GREEN);
if(ph[j] == i) cout << "H";
else cout << " ";
}
textbackground(BLACK);
cout << endl;
}
}
// Wyszukuje układ hetmanów,
// które nawzajem się nie atakują
//--------------------------------
void szukaj(int w)
{
if(w > N) szachownica(); // koniec rekurencji, wszystkie hetmany ustawione
else
{
for(int i = 1; i <= N; i++) // szukamy wolnego pola w wierszu w-tym
if(ph[i] == 0) // jeśli jest takie pole
{
bool t = true; // to sprawdzamy, czy nie jest atakowane
for(int j = 1; j <= N; j++)
if(ph[j] && (abs(i-j) == w - ph[j]))
{
t = false; // jest atakowane, dalej już nie szukamy
break;
}
if(t) // jeśli pole nie jest atakowane
{
ph[i] = w; // ustawiamy na nim hetmana
szukaj(w + 1); // przechodzimy do następnego wiersza
ph[i] = 0; // usuwamy hetmana i szukamy kolejnego pola
}
}
}
}
int main()
{
_cinit();
for(int i = 1; i <= N; i++) ph[i] = 0;
licznik = 0;
szukaj(1); // rozpoczynamy od wiersza nr 1
return 0;
}
|
Za pomocą tego programu zbadaj liczbę układów hetmanów dla szachownic o N = 4,5,6,7...
![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe