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
c2
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