Dany jest program zawierający metodę silnia.
public class SilniaDZ
{
public static void main(String[] args)
{
final int N = 3;
System.out.println(N + "! = " + silnia(N));
}
static long silnia(int n)
{
if (n == 0)
{
return 1;
}
else
{
return n*silnia(n-1);
}
}
}
Analizujemy wywołanie silnia(3)
Uwaga: Kolejne wywołania metody silnia zostały ponumerowane przy pomocy indeksów dolnych.
silnia1(3) --> silnia2(2) --> silnia3(1) --> silnia4(0)
Program wypisze: 3! = 6
f(n) - złożoność obliczeniowa wywołania silnia(n), gdzie n ∈ N ∪ {0}
Złożoność obliczeniową mierzymy zazwyczaj liczbą instrukcji przypisań i porównań wykonanych w trakcie działania programu.
Wyznaczamy równanie rekurencyjne na podstawie analizy wywołań:
• silnia(0) f(0) = 1 + 1 = 2 • silnia(1) f(1) = 1 + 1 + f(0) = 2 + f(0) • silnia(2) f(2) = 1 + 1 + f(1) = 2 + f(1) ... • silnia(n) f(n) = 1 + 1 + f(n-1) = 2 + f(n-1)
Uzasadnienie podamy na przykładzie wywołania silnia(0) i silnia(1)
• silnia(0) - wywołujemy metodę silnia z parametrem aktualnym 0
f(0) = - wyznaczamy złożoność obliczeniową wywołania silnia(0)
1 - w momencie wywołania metody silnia parametr formalny n przyjmuje wartość
parametru aktualnego, czyli 0, a zatem mamy niejawne podstawienie, które
+ liczymy za 1
1 - wchodzimy do ciała metody silnia, napotykamy instrukcję if..else, sprawdzamy
warunek logiczny z nagłówka instrukcji, co liczymy za 1, warunek jest prawdziwy,
bo 0 = 0, a zatem wchodzimy do pierwszego ciała instrukcji i zwracamy wartość 1
• silnia(1) - wywołujemy metodę silnia z parametrem aktualnym 1
f(1) = - wyznaczamy złożoność obliczeniową wywołania silnia(1)
1 - w momencie wywołania metody silnia parametr formalny n przyjmuje wartość
parametru aktualnego, czyli 1, a zatem mamy niejawne podstawienie, które
+ liczymy za 1
1 - wchodzimy do ciała metody silnia, napotykamy instrukcję if..else, sprawdzamy
warunek logiczny z nagłówka instrukcji, co liczymy za 1, warunek jest fałszywy,
+ bo 1 ≠ 0, a zatem pomijamy pierwsze ciało i wchodzimy do drugiego ciała instrukcji
f(0) - w drugim ciele instrukcji if..else zwracamy wartość wyrażenie 1*silnia(0) w którym
pojawia się wywołanie rekurencyjne silnia(0) o złożoności obliczeniowej równej f(0)
2 ; n = 0
f(n) =
2 + f(n-1) ; n > 0
Sposób I f(n) = 2 + f(n-1) = 2 + 2 + f(n-2) = 2 + 2 + 2 + f(n-3) = * Z: n - k = 0 => k = n * = 2 + 2 + 2 + ... + 2 + f(n-k) = 2k + f(n-k) = 2n + f(0) = 2n + 2
Sposób II f(0) = 2 f(1) = 2 + f(0) = 2 + 2 f(2) = 2 + f(1) = 2 + 2 + 2 f(3) = 2 + f(2) = 2 + 2 + 2 + 2 ... f(n) = 2 + f(n-1) = 2n + 2
1. f(0) = 2 f(0) = 2*0 + 2 = 2 2. Z: f(n) = 2n + 2 T: f(n+1) = 2(n+1) + 2 Dowód 1 f(n+1) = 2 + f(n) = 2 + 2n + 2 = 2n + 2 + 2 = 2(n+1) + 2 Dowód 2 f(n+1) = 2(n+1) + 2 = 2n + 2 + 2 = 2 + 2n + 2 = 2 + f(n) prawda
1. f(0) = 2 f(0) = 2*0 + 2 = 2 2. Wykażemy, że rozwiązanie f(n) = 2n + 2 spełnia równanie funkcyjne f(n) = 2 + f(n-1) 2 + f(n-1) = 2 + 2(n-1) + 2 = 2 + 2n - 2 + 2 = 2n + 2 = f(n)
f(n) = 2n + 2 ∈ Θ(n) "f jest dokładnie rzędu n"
g(n) - złożoność pamięciowa wywołania silnia(n), gdzie n ∈ N ∪ {0}
Złożoność pamięciową mierzymy zazwyczaj liczbą deklaracji komórek pamięci wykonanych w trakcie działania programu.
Wyznaczamy równanie rekurencyjne na podstawie analizy wywołań:
• silnia(0) g(0) = 1 • silnia(1) g(1) = 1 + g(0) = 1 + g(0) • silnia(2) g(2) = 1 + g(1) = 1 + g(1) ... • silnia(n) g(n) = 1 + g(n-1) = 1 + g(n-1)
Uzasadnienie podamy na przykładzie wywołania silnia(0) i silnia(1)
• silnia(0) - wywołujemy metodę silnia z parametrem aktualnym 0
g(0) = - wyznaczamy złożoność pamięciową wywołania silnia(0)
1 - w momencie wywołania metody silnia parametr formalny n przyjmuje wartość
parametru aktualnego, czyli 0, a zatem mamy deklarację zmiennej n, którą
liczymy za 1
wchodzimy do ciała metody silnia, napotykamy instrukcję if..else, sprawdzamy
warunek logiczny z nagłówka instrukcji, warunek jest prawdziwy, bo 0 = 0,
a zatem wchodzimy do pierwszego ciała instrukcji i zwracamy wartość 1
• silnia(1) - wywołujemy metodę silnia z parametrem aktualnym 1
g(1) = - wyznaczamy złożoność pamięciową wywołania silnia(1)
1 - w momencie wywołania metody silnia parametr formalny n przyjmuje wartość
parametru aktualnego, czyli 1, a zatem mamy deklarację zmiennej n, którą
+ liczymy za 1
g(0) - wchodzimy do ciała metody silnia, napotykamy instrukcję if..else, sprawdzamy
warunek logiczny z nagłówka instrukcji, warunek jest fałszywy, bo 1 ≠ 0,
a zatem pomijamy pierwsze ciało i wchodzimy do drugiego ciała instrukcji
w drugim ciele instrukcji if..else zwracamy wartość wyrażenie 1*silnia(0) w którym
pojawia się wywołanie rekurencyjne silnia(0) o złożoności pamięciowej równej g(0)
1 ; n = 0
g(n) =
1 + g(n-1) ; n > 0
Sposób I g(n) = 1 + g(n-1) = 1 + 1 + g(n-2) = 1 + 1 + 1 + g(n-3) = * Z: n - k = 0 => k = n * = 1 + 1 + 1 + ... + 1 + g(n-k) = k + g(n-k) = n + g(0) = n + 1
Sposób II g(0) = 1 g(1) = 1 + g(0) = 1 + 1 g(2) = 1 + g(1) = 1 + 1 + 1 g(3) = 1 + g(2) = 1 + 1 + 1 + 1 ... g(n) = 1 + g(n-1) = n + 1
g(n) = n + 1 ∈ Θ(n) "g jest dokładnie rzędu n"