Transcript wyk3

Algorytmy i Struktury Danych
Typy algorytmów
WYKŁAD 3
PROWADZĄCY: DR PAWEŁ DROZDA
Plan wykładu
 Brute force
 Rekurencje
 Metoda zachłanna
 Programowanie dynamiczne
dr Paweł Drozda
Brute force
 Sukcesywne sprawdzanie wszystkich kombinacji, aż
do rozwiązania problemu
 Zazwyczaj nieoptymalna, prosta do implementacji
 Ogromna złożoność obliczeniowa
 Przykłady:



Łamanie hasła
Znajdowanie pary punktów najmniej odległych
Wyszukiwanie wzorca w tekście
dr Paweł Drozda
Wyszukiwanie wzorca w tekście – brute force
 Problem
Poszukiwanie wzorca długości M znaków w tekście o długości N
znaków
 Rozwiązanie
Indeksy i, j oznaczają miejsce poruszania się po wzorcu i po
tekście
Jeśli znajdziemy początek taki sam porównujemy kolejne znaki,
aż do znalezienia znaku niezgodnego – przesunięcie początku
przeszukania w tekście o 1, bądź do momentu przejścia całego
wzorca – zwrócony zostanie indeks początku wzorca w tekście
Po przejściu całego tekstu bez znalezienia wzorca – zwracany
komunikat o niepowodzeniu przeszukania
dr Paweł Drozda
Wyszukiwanie wzorca - implementacja
Szukaj (string wzorzec, string tekst){
int i=0, j=0;
while (i<strlen(wzorzec) && j<strlen(tekst)){
if wzorzec[i] != tekst[j] {
j-=i-1;
i=0;
}
else{
j++;
i++;
}
}
if (i==strlen(w)) cout << j-i;
else cout << -1;
}
dr Paweł Drozda
Rekurencje
 Przykład wprowadzający
Dziecko rozrzuciło klocki – musi je pozbierać do pudełka
zadanie polega na włożeniu po jednym klocku do pudełka do
momentu aż wszystkie klocki znajdą się w pudełku
 Cechy algorytmu rekurencyjnego
 zakończenie algorytmu jasno określone
 większy problem rozbity na problemy elementarne
dr Paweł Drozda
Rekurencje – ilustracja
 Problem
 Dla tablicy n liczb określić czy istnieje liczba x
 Rozwiązanie
 Weź pierwszy niezbadany element tablicy n-elementowej
 Jeśli jest to x wypisz sukces i zakończ
 W przeciwnym przypadku zbadaj pozostałą część tablicy
 Gdy po przejściu całej tablicy nie został znaleziony x wypisz
porażka
dr Paweł Drozda
Rekurencje – przykładowa implementacja
#include <iostream.h>
#include <stdlib.h>
const
n=10;
int tab[n]={1,2,3,2,-7,44,5,1,0,-3};
void szukaj(int tab[n],int left,int right,int x)
// left, right = lewa i prawa granica obszaru poszukiwań
// tab
= tablica
// x
= wartość do odnalezienia
{
if (left>right)
cout << "Element " << x << " nie został odnaleziony\n";
else
if (tab[left]==x)
cout << "Znalazłem szukany element "<< x << endl;
else
szukaj(tab,left+1,right,x);
}
dr Paweł Drozda
int main()
{
szukaj(tab,0,n-1,7);
szukaj(tab,0,n-1,5);
}
// wyniki programu:
// Element 7 nie został odnaleziony
// Znalazłem szukany element
Analiza algorytmu
 Zakończenie programu
 Element odnaleziony
 Przekroczenie zakresu tablicy
 Duży problem rozbity na problemy elementarne
 Z tablicy o wymiarze n schodzimy do tablicy o wymiarze n-1
 Instrukcja porównania
dr Paweł Drozda
Rekurencje - schemat wykonywania
 Przykład silnia
X=0?
unsigned long int silnia(int x)
{
if (x==0)
return 1;
else
return x*silnia(x-1);
}
3*2!
nie
X=0?
2*1!
nie
X=0?
X=0?
dr Paweł Drozda
1*0!
nie
tak
1
Rekurencje – pułapki (1)
 Wykonywanie tych samych obliczeń wiele razy
 Problem ciągu Fibonacciego
f(0)= 1, f(1)=1
f(n) = f(n-1) + f(n-2)
f(4)
f(3)
f(2)
f(1)
dr Paweł Drozda
f(2)
f(1)
f(0)
f(1)
f(0)
Rekurencje – pułapki (2)
 Wywoływanie rekurencji w nieskończoność
int StadDoWiecznosci(int n)
{
if (n==1) return 1; else
if ((n %2) == 0) // n parzyste
return StadDoWiecznosci(n-2)*n;else
return StadDoWiecznosci(n-1)*n;
}
Dla parzystych n – odwołania w nieskończoność
dr Paweł Drozda
Rozwiązywanie rekurencji
 Merge Sort
O(1)
T ( n)  
T ( n / 2  T ( n / 2)  (n)
 Rozwiązanie
T (n)  (n lg n)
 Założenie
 n jest całkowite
 T(n) jest stałe dla małych n
T (n)  2T (n / 2)  (n)
dla
n 1
n 1
Metoda podstawiania
T (n)  2T (n / 2)  n
zgadujemy : T (n)  O(n lg n)
czyli:
założenie:
T (n)  cn lg n, c  0
T (n / 2)  c(n / 2) lg( n / 2)
T (n)  2c(n / 2) lg(n / 2)  n
 cn lg(n / 2)  n  cn lg n  cn lg 2  n  cn lg n  cn  n
dla c  1
 cn lg n
Metoda podstawiania
 warunek brzegowy:
T (1)  c lg1, c ?
T (2)  2 *1  2  4
T (2)  c2 lg 2
c2
T (3)  2 *1  3  5
T (3)  c3 lg 3
Metoda podstawiania
 Zamiana zmiennych
T (n)  2T
 n  lg n
m  lg n, n  2 m


T (2 m )  2T 2 m / 2  m
S ( m)  T ( 2 m )
S ( m )  2 S ( m / 2)  m
S (m)  T (2 m )  O (m lg m)
T (n)  O (lg n lg lg n)
Metoda iteracyjna
T (n)  3T (n / 4)  n
 n  3T (n / 4)
 n  3(n / 4  3T (n / 16))
 n  3(n / 4  3(n / 16  3T (n / 64)))
 n  3n / 4  9n / 16  27T (n / 64)
i-ty składnik ciągu:
iterowanie kończymy gdy:
3i n / 4i
n  1 lub n / 4i   1
i  log4 n
Metoda iteracyjna
i
i
n
/
4

n
/
4
 
T (n)  n  3n / 4  9n / 16  27n / 64   3log4 n (1)
szereg geometryczny
log4 n

1
T (n)  n  (3 / 4)  n (3 / 4)  n
 4n  O(n)
1 3 / 4
k 0
k 0
i
i
Drzewo rekurencji
T (n)  2T (n / 2)  n2
T (nn)2
2
T(n(n/ /22) )
n2
2(n / 2)  n / 2
2
2
T(n(n/ /22) )
4(n / 4) 2  n2 / 4
T(n(n/ 4/ 4
) 2) T(n(n/ 4/ 4
) 2) T(n(n/ 4/ 4
) 2) T(n(n/ 4/ 4
) 2)
n  n / 2  n / 4   n
2
2
2
2

2
 (1 / 2)
i 0
i
 O( n 2 )
Drzewo rekurencji
T (n)  2T (n / 2)  n
n
n/2
n
n/2
n
lg n
n/4
n/4
n/4
n/4
n
O(n lg n)
Metoda rekurencji uniwersalnej
T (n)  aT (n / b)  f (n),
a  1, b  1
koszt dzielenia/łączenia
a podproblemów
rowiązywanych w
czasie n/b

2) jesli f (n)  n
3) jesli f (n)  n

, toT (n)  n
 dla   0

1) jesli f (n)  O n logb a  dla   0, toT (n)   n logb a
logb a 
logb a 
logb a
lg n


i jesli af (n / b)  cf (n) dla c  1, toT (n)  ( f (n))
Metoda rekurencji uniwersalnej
T (n)  9T (n / 3)  n
a  9, b  3,
f ( n)  n
n logb a  n log3 9  (n 2 )
f (n)  O(n 2 ) dla   1 (przypadek1), toT (n)  (n 2 )
T (n)  T (2n / 3)  1
a  1, b  3 / 2, f (n)  1
n logb a  n log3 / 2 1  n 0  1
f (n)  (n logb a ), wiec T (n)  (lg n)
Metoda rekurencji uniwersalnej
T (n)  3T (n / 4)  n lg n
a  3, b  4, f (n)  n lg n
n logb a  n log4 3  O(n 0,793 )
f (n)  (n log4 3 ) dla   0,2 (przypadek3)
dla dostatecznie dużych n:
af (n / b)  3(n / 4) lg(n / 4)  cf (n)  cn lg n gdy c  3 / 4
więc:
T (n)  (n lg n)
Metoda zachłanna
 Główne zastosowanie – problemy optymalizacji
 Wybór w danej chwili najkorzystniejszy
 „Nadzieja” otrzymania globalnie optymalnego
rozwiązania
 Przykłady zastosowania:



Znajdowanie minimum (maksimum) w tablicy N liczb
Ciągły problem plecakowy
Kody Huffmana – kompresja danych wykorzystując tablicę
częstości występowania znaków
dr Paweł Drozda
Znajdowanie minimum - implementacja
int min(int tab[]){
int i,minimum=tab[0];
for (i=1; i<length(tab);i++)
if (tab[i]<minimum) minimum=tab[i];
return minimum;
}
dr Paweł Drozda
Problem plecakowy
 Sformułowanie problemu
Złodziej rabujący sklep znalazł n przedmiotów. Każdy z przedmiotów
ma pewną wartość i pewną wagę. Problem polega na zmieszczeniu
jak najwartościowszego łupu do plecaka mogącego pomieścić
pewną liczbę kilogramów
 Problem dyskretny
Każdy przedmiot jest kradziony w całości – część przedmiotu jest
bezwartościowa np. księgarnia, sklep monopolowy, skarbiec ze
sztabami złota
 Problem ciągły
przedmiot można podzielić – część przedmiotu też ma wartość np.
sklep mięsny, odzież na wagę, skarbiec ze złotym piaskiem
dr Paweł Drozda
Problem plecakowy – przykład (1)
 Dyskretny
waga 15kg
wartość
120 zł
waga 5kg
wartość 60 zł
wartość kg:
p1 = 12zł
dr Paweł Drozda
waga 10kg
wartość
100 zł
p2=10 zł
waga 15kg
wartość
120 zł
p3=8 zł
waga 5kg
wartość 60 zł
waga 10kg
wartość
100 zł
Metoda
zachłanna
waga 10kg
wartość
100 zł
Rozwiązanie
optymalne
Problem plecakowy – przykład (2)
 Ciągły
waga 10kg
wartość
80 zł
waga 5kg
wartość 60 zł
waga 5kg
wartość 60 zł
wartość kg:
p1 = 12zł
dr Paweł Drozda
waga 10kg
wartość
100 zł
p2=10 zł
waga 15kg
wartość
120 zł
p3=8 zł
waga 10kg
wartość
100 zł
Metoda zachłanna =
rozwiązanie optymalne
Programowanie dynamiczne
 Główne zastosowanie – problem optymalizacji
 Podobne do metody „dziel i zwyciężaj”
 Stosowane gdy podproblemy nie są niezależne
 Każdy podproblem rozwiązywany tylko raz – wynik
rozwiązania zapamiętywany
dr Paweł Drozda
Etapy programowania dynamicznego
 Scharakteryzowanie struktury optymalnego
rozwiązania
 Rekurencyjne zdefiniowanie kosztu optymalnego
rozwiązania
 Obliczenie optymalnego kosztu metodą wstępującą
 Znalezienie optymalnego rozwiązania
dr Paweł Drozda
Przykład – linie montażowe
a1,1
a1,2
a1,3
a1,n
e1
t1,1
t1,2
x1
e2
t2,1
t2,2
x2
a2,1
a2,2
a2,3
a2,n
Dwie linie montażowe – każda z linii ma n stanowisk
Na i-tym stanowisku linii 1 jest wykonywana ta sama czynność co na i-tym
stanowisku linii 2
Czasy wykonania czynności są różne
Czasy e i x są to odpowiednio czas umieszczenia elementu i zdjęcia elementu
z linii
Czasy t oznaczają czas potrzebny na przeniesienie elementu z jednej linii na
drugą
dr Paweł Drozda
Linie montażowe
 Sformułowanie problemu
Wskazanie stanowisk montażowych na obu liniach tak, aby czas
montażu był jak najkrótszy
 Algorytm brute force
Dla każdej możliwej ścieżki obliczany jest czas montażu, a
następnie wybór najkrótszego czasu
Nie do przyjęcia – złożoność obliczeniowa jest nie mniejsza od 2n
co dla dużych n jest nie do policzenia w zadawalającym czasie
dr Paweł Drozda
Linie montażowe – przykład
7
9
3
4
8
4
2
2
3
1
3
4
3
4
2
1
2
2
1
2
8
5
6
Rozwiązanie optymalne:
linia 1: stanowiska 1,3,6
JAK DO TEGO DOJŚĆ???
dr Paweł Drozda
4
5
7
linia 2: stanowiska 2,4,5
Rozwiązanie – programowanie dynamiczne(1)
 Etap 1 – struktura optymalnego rozwiązania
Najszybszy sposób montażu do stanowiska i-tego pierwszej linii:
dla i=1 – istnieje tylko jeden sposób
dla i>1 – dwa sposoby:
• przejście ze stanowiska i-1 pierwszej linii – koszt przejścia pomijany
• Przejście ze stanowiska i-1 drugiej linii – koszt równy t2,n-1
Dla stanowisk i-1 koszt przejścia jest optymalny – Własność
optymalnej podstruktury
Analogicznie dla stanowiska i-tego drugiej linii
 Rozwiązanie problemu dla stanowiska i na każdej z
linii
znalezienie rozwiązania podproblemów stanowisk i-1 dla każdej z linii
dr Paweł Drozda
Rozwiązanie – programowanie dynamiczne(2)
 Etap 2 – rozwiązanie rekurencyjne
f= min(f1[n]+ x1 , f2[n] + x2) – najkrótszy czas montażu
Wartości dla stanowisk 1:
f1[1]=e1 + a1,1
f2[1]=e2 + a2,1
Sformułowanie równania dla dowolnego i:
f1[i]=min(f1[i-1]+ a1,i, f2[i-1]+ t2,i-1 + a1,i)
f2[i]=min(f2[i-1]+ a2,i, f1[i-1]+ t1,i-1 + a2,i)
dr Paweł Drozda
Rozwiązanie – programowanie dynamiczne(3)
 Obliczenia
funkcja f oznacza optymalne rozwiązanie
 tabele s1, s2 dla i-tego stanowiska zawierają numer linii z której
pochodzi i-1 stanowisko w optymalnym rozwiązaniu

f1[1]=e1 + a1,1
f2[1]=e2 + a2,1
for i=1 to n
if (f1[i-1]+ a1,i < f2[i-1]+ t2,i-1 + a1,i)
f1[i]=f1[i-1]+ a1,i, s1[i]=1
else f1[i]=f2[i-1]+ t2,i-1 + a1,i, s2[i]=2
analogicznie dla drugiej linii
if (f1[n]+ x1 < f2[n] + x2)
f= f1[n]+ x1, s=1
else
f = f2[n] + x2, s=2
dr Paweł Drozda
Rozwiązanie – programowanie dynamiczne(4)
 Etap 4 – optymalne rozwiązanie
Odczytanie odpowiednich numerów linii ze zmiennej s oraz z tablic
s1, s2 w kolejności od n-tego do pierwszego stanowiska montażu
dr Paweł Drozda
Problem montażu – rozwiązanie liczbowe
7
9
3
4
8
4
2
2
3
1
3
4
3
4
2
1
2
2
1
2
8
f1[1]=9
f2[1]=12
f1[4]=24, s1[4]=1
f2[4]=25, s2[4]=1
5
6
4
5
7
f1[2]=min(9+9, 12+2+9)=18, s1[2]=1
f1[3]=20, s1[3]=2
f2[2]=min(12+5, 9+2+5)=16, s2[2]=1
f2[3]=22, s2[3]=2
f1[5]=32, s1[5]=1
f1[6]=35, s1[6]=2
f2[5]=30, s2[5]=2
f2[6]=37, s2[6]=2
f=38, s=1
numery linii dla poszczególnych wierzchołków od końca: 6-1, 5-2, 4-2, 3-1, 2-2, 1-1
dr Paweł Drozda