Strategie optymalizacji

Podobne dokumenty
Temat: Algorytmy zachłanne

Wykład 3. Metoda dziel i zwyciężaj

Algorytm selekcji Hoare a. Łukasz Miemus

Algorytmy i struktury danych.

Struktury danych i złożoność obliczeniowa Wykład 2. Prof. dr hab. inż. Jan Magott

Programowanie dynamiczne

Wymiar musi być wyrażeniem stałym typu całkowitego, tzn. takim, które może obliczyć kompilator. Przykłady:

TEORETYCZNE PODSTAWY INFORMATYKI

Programowanie i struktury danych

Algorytmy i struktury danych. Co dziś? Tytułem przypomnienia metoda dziel i zwyciężaj. Wykład VIII Elementarne techniki algorytmiczne

Tablice. Monika Wrzosek (IM UG) Podstawy Programowania 96 / 119

Podstawy algorytmiki i programowania - wykład 6 Sortowanie- algorytmy

Strategia "dziel i zwyciężaj"

Zmienne i struktury dynamiczne

Część 4 życie programu

Wstęp do programowania INP001213Wcl rok akademicki 2017/18 semestr zimowy. Wykład 9. Karol Tarnowski A-1 p.

EGZAMIN MATURALNY Z INFORMATYKI

INFORMATYKA POZIOM ROZSZERZONY CZĘŚĆ I PRZYKŁADOWY ZESTAW ZADAŃ. Czas pracy 90 minut

1 Podstawy c++ w pigułce.

Zadania do wykonania. Rozwiązując poniższe zadania użyj pętlę for.

Dla każdej operacji łącznie tworzenia danych i zapisu ich do pliku przeprowadzić pomiar czasu wykonania polecenia. Wyniki przedstawić w tabelce.

Pętle i tablice. Spotkanie 3. Pętle: for, while, do while. Tablice. Przykłady

Podstawowe algorytmy i ich implementacje w C. Wykład 9

I. Podstawy języka C powtórka

Analiza algorytmów zadania podstawowe

Informacje wstępne #include <nazwa> - derektywa procesora umożliwiająca włączenie do programu pliku o podanej nazwie. Typy danych: char, signed char

8. Wektory. Przykłady Napisz program, który pobierze od użytkownika 10 liczb, a następnie wypisze je w kolejności odwrotnej niż podana.

WHILE (wyrażenie) instrukcja;

MACIERZE. Sobiesiak Łukasz Wilczyńska Małgorzata

Matematyka dyskretna - wykład - część Podstawowe algorytmy kombinatoryczne

Wstęp do programowania

Wstęp do programowania

Programowanie dynamiczne

tablica: dane_liczbowe

1,3,4,2,3,4,0,1,4,5,0. Wówczas największa suma trzech kolejnych liczb (zaznaczone na czerwono) wynosi:

Podejście zachłanne, a programowanie dynamiczne

typ y y p y z łoż o on o e n - tab a lice c e w iel e owym m ar a o r we, e stru r kt k ury

Temat: Technika zachłanna. Przykłady zastosowania. Własno wyboru zachłannego i optymalnej podstruktury.

Ćwiczenie 3 Programowanie dynamiczne

TABLICE W JĘZYKU C/C++ typ_elementu nazwa_tablicy [wymiar_1][wymiar_2]... [wymiar_n] ;

Wybrane algorytmy tablicowe

1 Podstawy c++ w pigułce.

Algorytm. a programowanie -

WHILE (wyrażenie) instrukcja;

Programowanie - wykład 4

a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a[10]

Wstęp do programowania

WYKŁAD 9. Algorytmy sortowania elementów zbioru (tablic) Programy: c4_1.c... c4_3.c. Tomasz Zieliński

Algorytmy i str ruktury danych. Metody algorytmiczne. Bartman Jacek

Rozwiązanie. #include <cstdlib> #include <iostream> using namespace std;

Ćwiczenie 3 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

Proste programy w C++ zadania

Sortowanie przez wstawianie Insertion Sort

Programowanie - instrukcje sterujące

Programowanie w C++ Wykład 7. Katarzyna Grzelak. 23 kwietnia K.Grzelak (Wykład 7) Programowanie w C++ 1 / 40

Zadania język C++ Zad. 1. Napisz program wczytujący z klawiatury wiek dwóch studentów i wypisujący informację o tym, który z nich jest starszy.

Tablice są typem pochodnym. Poniżej mamy przykłady deklaracji różnych tablic:

Aby uzyskać zaliczenie w pierwszym terminie (do 30 stycznia 2018) rozliczyć trzeba co najmniej 8 projektów, po 4 z każdej z części: C++ oraz Python.

Wstęp do programowania

Programowanie dynamiczne i algorytmy zachłanne

Tablice i struktury. czyli złożone typy danych. Programowanie Proceduralne 1

Programowanie dynamiczne cz. 2

Metody Metody, parametry, zwracanie wartości

Definicja. Ciąg wejściowy: Funkcja uporządkowująca: Sortowanie polega na: a 1, a 2,, a n-1, a n. f(a 1 ) f(a 2 ) f(a n )

Wstęp do programowania

Zadanie 1. Doskonała inaczej (6 pkt) Poniższy algorytm wyznacza wszystkie dzielniki liczby naturalnej n 1, mniejsze od n.

Wskaźniki. Programowanie Proceduralne 1

// Liczy srednie w wierszach i kolumnach tablicy "dwuwymiarowej" // Elementy tablicy są generowane losowo #include <stdio.h> #include <stdlib.

Inteligencja obliczeniowa Laboratorium 2: Algorytmy genetyczne

Schemat programowania dynamicznego (ang. dynamic programming)

Języki i paradygmaty programowania 1 studia stacjonarne 2018/19. Lab 9. Tablice liczbowe cd,. Operacje na tablicach o dwóch indeksach.

Podstawy informatyki. Informatyka stosowana - studia niestacjonarne. Grzegorz Smyk

Algorytmy zachłanne. dr inż. Urszula Gałązka

Wykład 6. Metoda eliminacji Gaussa: Eliminacja z wyborem częściowym Eliminacja z wyborem pełnym

Transponowanie macierzy Mnożenie macierzy Potęgowanie macierzy Wyznacznik macierzy

Algorytmy i Struktury Danych.

Programowanie w C++ Wykład 6. Katarzyna Grzelak. 1 kwietnia K.Grzelak (Wykład 6) Programowanie w C++ 1 / 43

4. Funkcje. Przykłady

INFORMATYKA WYBRANE ALGORYTMY OPTYMALIZACYJNE KRYPTOLOGIA.

Czas pracy: 60 minut

if (wyrażenie ) instrukcja

Kolokwium ze wstępu do informatyki, I rok Mat. (Ściśle tajne przed godz. 10 : grudnia 2005.)

Algorytmy decyzyjne będące alternatywą dla sieci neuronowych

INFORMATYKA Z MERMIDONEM. Programowanie. Moduł 5 / Notatki

Ćwiczenie 7 z Podstaw programowania. Język C++, programy pisane w nieobiektowym stylu programowania. Zofia Kruczkiewicz

Język C++ wykład VIII

Projektowanie i analiza algorytmów

Algorytmy i Struktury Danych, 2. ćwiczenia

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

EGZAMIN MATURALNY Z INFORMATYKI POZIOM PODSTAWOWY MAJ 2014 CZĘŚĆ I WYBRANE: Czas pracy: 75 minut. Liczba punktów do uzyskania: 20 WPISUJE ZDAJĄCY

Język C, tablice i funkcje (laboratorium)

Wstęp do programowania

> C++ dynamiczna alokacja/rezerwacja/przydział pamięci. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Programowanie proceduralne INP001210WL rok akademicki 2017/18 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

PRÓBNY EGZAMIN MATURALNY Z INFORMATYKI STYCZEŃ POZIOM ROZSZERZONY Część I

Zestaw 1-1 Organizacja plików: Oddajemy tylko źródła programów (pliki o rozszerzeniach.cpp)!!!

Lab 9 Podstawy Programowania

Sortowanie Shella Shell Sort

n, m : int; S, a, b : double. Gdy wartości sumy składowej nie można obliczyć, to przyjąć Sij = 1.03 Dla obliczenia Sij zdefiniować funkcję.

Wstęp do programowania

Transkrypt:

Strategie optymalizacji Algorytm siłowy, algorytm brute force (z ang. "brutalna siła") opiera się na kolejnym sprawdzeniu wszystkich możliwych kombinacji rozwiązania problemu. Jest to zwykle nieoptymalna czasowo, ale najprostsza pod względem implementacji i najbardziej skuteczna metoda postępowania. Algorytm zachłanny (ang. greedy algorithm) w każdym kolejnym kroku dokonuje wyboru zachłannego, tj. optymalnego w danym momencie. Nie dla każdego problemu ten algorytm odnajduje optymalny wynik. Programowanie dynamiczne opiera się na podziale rozwiązywanego problemu na podproblemy (niekoniecznie rozłączne jak w metodzie dziel-i-rządź). Wyznacza się funkcję celu dla całego problemu na podstawie optymalnych wartości funkcji celu dla podproblemów. Rozwiązania kolejnych podproblemów prowadzi się poczynając od najmniejszego podproblemu i zapisując optymalne wartości w tablicy. Ostateczne rozwiązanie jest zawsze optymalne. Zadanie dla Ciebie: Niedawno (rok 2009) producent zupek błyskawicznych, firma Amino, ogłosiła loterię. Na opakowaniach zupek umieszczano liczby naturalne. Aby coś wygrać należało zebrać takie opakowania, z których liczby po zsumowaniu dawały wartości z zakresu od 1000 do 10000 podzielne przez 1000, a więc 1000, 2000, 3000,, 10000. Wykorzystując dane z pliku: amino.txt (przykładowy zestaw wartości liczbowych zebranych z opakowań zupek Amino) sprawdź czy udałoby się coś wygrać Tu zajmę się problemem pakowanie plecaka ( ang. knapsack problem) i rozwiążę go na trzy sposoby, wykorzystując powyższe trzy algorytmy. Mam zbiór elementów, każdy element o określonej wadze i cenie. Należy wybrać z nich taki zestaw, który będzie mieć najwyższą wartość (sumę cen) i nie przekroczy dopuszczalnej wagi plecaka. Wczytuję dane z pliku plecak.txt: dopuszczalną wagę plecaka, oraz dane elementów (wagę i cenę) do wektora. vector to klasa która odpowiada tablicy 1-wymiarowej o zmiennym rozmiarze. Elementy dołącza się na koniec vector a metodą push_back(). Odwołania do elementów - za pomocą indeksów, jak w zwykłej tablicy. Potrzebne jest #include <vector> Rozwiązanie brute-force skuteczne ale czasochłonne Dla n elementów mam m=2 n możliwych zestawów. Użyję reprezentacji binarnej kolejnych liczb i z zakresu od 0 do m-1, na przykład dla i=20 binarnie = 10100, oznacza: weź pierwszy i trzeci element. Rozważę wszystkie możliwe kombinacje elementów. Wybiorę z nich te kombinacje, których waga nie przekracza dopuszczalnej wagi i poszukam wśród nich maksymalnej wartości sumy cen. struct element { int waga, cena; ; int main() { element e; int W; vector <element> E; // pojedynczy element // dopuszczalna waga plecaka // wektor elementów, wymaga #include <vector>

ifstream we("plecak.txt"); // czytanie danych int x; we>>w; cout<<"dopuszczalna waga="<<w<<endl; while (we>>x){ // dopóki jest jakaś dana na wejsciu... e.waga=x; we>>e.cena; cout<<" waga="<<e.waga<<" cena="<<e.cena<<endl; E.push_back(e); // dodanie nowego elementu //na koniec wektora E we.close(); int n=e.size(); // n = liczba elementów wektora E // n elementów daje m=2 n możliwości zestawów elementów int m=1; for (int i=1; i<=n; i++) m=m*2; Reprezentacja binarna liczby dziesiętnej d wyrażona przy pomocy n cyfr binarnych string bin(int d, int n) { string s=""; while (d>0) { if (d%2==0) s="0"+s; else s="1"+s; d=d/2; while (s.length()<n) s="0"+s; return s; cout<<n<<" elementow daje "<<m<<" kombinacji upakowania\n"; cout<<"wybiorę z nich tylko te kombinacje ktore maja wage <= "<<W<<"\n\n"; int cmaxi; // max cena int wmaxi=0; // waga przy max cenie int mmaxi=0; // kombinacja elementów m która zapewnia max cenę for (int i=0; i<m; i++) { // przegląd wszystkich kombinacji elementów string b=bin(i,n); int w=0, c=0; // waga i cena bieżącej kombinacji for (int j=0; j<n; j++) { if (b[j]=='1'){ // jeżeli w reprezentacji binarnej na j-tym miejscy jest 1 to weź j-ty element w=w+e[j].waga; c=c+e[j].cena; if (w<=w) // jeżeli waga tej kombinacji nie przekracza dopuszczalnej... { cout<<bin(i,n)<<" waga="<<w<<" wartosc="<<c<<endl; if (c>cmaxi) // jeśli cena jest większa od bieżącego maksimum {cmaxi=c; wmaxi=w; mmaxi=i; cout<<"\nnajkorzystniej:\n"; cout<<bin(mmaxi,n)<<" waga="<<wmaxi<<" wartosc="<<cmaxi<<endl; system("pause"); return EXIT_SUCCESS; Rozwiązanie brute-force ma sens tylko w przypadku niewielkiej liczby elementów, inaczej czas działania algorytmu wydłuża się tak bardzo, że nie ma szans doczekać końca obliczeń.

Rozwiązanie zachłanne nie zawsze daje optymalny wynik Najpierw sortuję elementy według ceny w porządku malejącym. Jeszcze lepszym pomysłem byłoby posortowanie według ciężaru jednostkowego (stosunku masy do wagi) każdego elementu. Następnie biorę elementy po kolei elementy począwszy od najcenniejszego, sprawdzam czy zmieści się wagowo w plecaku i jeśli tak to go wrzucam. Powtarzam do wyczerpania elementów lub plecaka. Tu akurat wyniki są identyczne z rozwiązaniem siłowym, ale często w przypadku problemu dyskretnego (gdy elementów nie można dzielić na mniejsze części) rozwiązanie zachłanne nie daje optymalnego wyniku. struct element { int waga, cena; ; int main() { element e; vector <element> E; int W; // pojedynczy element // wektor elementów, wymaga #include <vector> // dopuszczalna waga plecaka ifstream we("plecak.txt"); // czytanie danych we>>w; cout<<"dopuszczalna waga="<<w<<endl; cout<<"\nelementy:\n"; int x; while (we>>x) { e.waga=x; we>>e.cena; cout<<"waga="<<e.waga<<" cena="<<e.cena<<endl; E.push_back(e); // dodanie elementu do wektora we.close(); int n=e.size(); // liczba elementów wektora E Sortowanie wektora D, algorytm InsertionSort : Dopóki j>0 oraz cena poprzedniego [j-1] jest mniejsza od bieżącego[j] przestawiaj elementy: [j] oraz [j-1] Wektor przykazywany jest do funkcji przez referencję void sort( vector <element> &D, int n) { for (int i=1; i<n; i++) { int j=i; while (j>0 && (D[j-1].cena<D[j].cena)) { swap(d[j-1],d[j]); j--; sort(e,n); // sortuj wektor E, n-elementowy, malejąco według ceny cout<<"\npo sortowaniu wedlug ceny: \n"; for (int i=0; i<n; i++) cout<<" waga="<<e[i].waga<<" cena="<<e[i].cena<<endl; int w=0; // bieżąca waga plecaka int c=0; // bieżąca wartość plecaka cout<<"pusty plecak\n"; int i=0; // indeks elementu przymierzanego do plecaka while ( w<w && i<n ){ if (w+e[i].waga <=W) { w=w+e[i].waga; c=c+e[i].cena; cout<<"dorzucam "<<E[i].waga<<" kg warte "<<E[i].cena<<" zl, "; cout<<"razem waga="<<w<<", cena="<<c<<endl; i++; cout<<"koniec pakowania\n\n"; system("pause"); return EXIT_SUCCESS;

Rozwiązanie metodą programowania dynamicznego daje zawsze optymalny wynik {w 1,, w n - wagi elementów {c 1,, c n ceny (wartości) elementów W - maksymana pojemność plecaka Rozpatruję wszystkie moŝliwe wartosci wagi plecaka od 0 do W. Kolejno próbuję umieszczać elementy, poczynając od pierwszego elementu i najmniejszej moŝliwej pojemności plecaka. Patrzę co się zmieści i co będzie więcej warte. A(i,j) - wartość optymalnie wypełnionego plecaka o wadze j przedmiotami, których indeksy mieszczą się między 1 a i. A(0,j) = 0 A(i,j) = max { cj + S(i - wj ) : wj <= i A(n,W) będzie rozwiązaniem problemu największą moŝliwą wartością plecaka. for(j = 0; j <= W; j++) W[j] = 0; for(i = 1; i< n; i++) { for(j = 0; j <= W; j++) { // zerowanie górnego wiersza tabeli // (wartość plecaka o pojemności j gdy nie zawiera Ŝadnego elementu) // próbujemy dorzucić i-ty element do plecaka o pojemnosci j // dla wszystkich moŝliwych pojemności plecaka if( w[i] <= j ) // jezeli waga i-tego elementu nie przekracza j { // wybierz wiekszą wartość z dwóch moŝliwości: // dotychczasowej wartości plecaka bez i-tego elementu (z poprz. wiersza), lub // (wartość plecaka z poprz. wiersza lŝejszego o w i ) + wartość c i i-tego elem. A[i,j] = max( A[i-1,j], A[i-1,j - w[i]] + c[i]); 10 zł z poprzedniego wiersza tej samej kolumny czy wartość z poprzedniego wiersza 2 kolumny w lewo(o 2 kg mniej ) 10 zł + cena elementu z tego wiersza 1 zł = 11 zł? oczywiście 11 zł wszystkie możliwe wartości wagi plecaka od 0 do 15 kg i\j 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 waga cena 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 chleb 9 10 1 0 0 tu się 0 9-kilowy 0 0 chleb 0 nie 0 zmieści 0 0 10 10 10 10 10 10 10 cola 12 7 2 0 0 tu się 0 nie zmieści 0 0 ani 0 chleb 0 ani 0 cola 0 10 10 10 10 10 10 10 sól 2 1 3 0 0 1 tu 1 się zmieści 1 1 sól 1 1 1 10 10 11 11 11 11 11 mąka 7 3 4 0 0 1 1 1 1 1 3 3 10 10 11 11 11 11 11 ryż 5 2 5 0 0 1 1 1 2 2 3 3 10 10 11 11 11 12 12 11 zł z poprzedniego wiersza tej samej kolumny czy wartość z poprzedniego wiersza 5 kolumn w lewo(o 5 kg mniej ) 10 zł + cena elementu z tego wiersza 2 zł = 12 zł? oczywiście 12 zł wynik Tabela A ij przechowuje jedynie łączną wartość optymalnie spakowanego plecaka. Aby móc pokazać zestaw rzeczy składajacych sie na optymalnie spakowany plecak, najwygodniej jest zbudować drugą tabelę: Bij, która przechowywać będzie informację o elementach pakowanych do plecaka: Bij=1 jeśli element i został zapakowany do plecaka o pojemności j, lub Bij=0 jeśli nie został zapakowany.

struct element { int waga,cena; ; int main( ) { vector <element> E; element e = {0,0; // dodaję na początku wektora element zerowy pusty E.push_back(e); // aby ułatwić zapis indeksowania elementów: od 1 do n int W; // dopuszczalna waga plecaka int n=0; // liczba elementów ifstream we("plecak.txt"); // czytanie danych we>>w; cout<<"dopuszczalna waga="<<w<<endl; while (we>>e.waga) { we>>e.cena; E.push_back(e); n++; cout<<"element nr "<<n<<" waga="<<e.waga<<" cena="<<e.cena<<endl; we.close(); // przygotowanie do pakowania... vector < vector<int> > A, B; // wektory dwuwymiarowe [i][j] o rozmiarach i=<0,n>, j=<0,w> // A[i][j] - optymalne wartości plecaka o wadze j z wyborem elementow od 1 do i // B[i][j] - informacja czy i-element jest "wzięty" do j-plecaka: 1 czy odrzucony: 0 for (int i=0; i<=n; i++) { // wypełniam wstępnie wektory A i B elementami o wartościach 0 A.push_back(vector<int>()); B.push_back(vector<int>()); for (int j=0; j<=w; j++) { A[i].push_back(0); B[i].push_back(0); cout<<"\npakujemy...\n"; // pakowanie for (int i=1; i<=n; i++) { for (int j=1;j<=w; j++) { if (E[i].waga>j) A[i][j]=A[i-1][j]; // jezeli waga elementu przekracza pojemność plecaka // to pozostaw plecak nie zmieniony (przepisz wartość z poprzedniego wiersza) else { int jp=j-e[i].waga; // waga j pomniejszona o wagę i-tego elementu int ap=a[i-1][jp]+e[i].cena; // wartość plecaka o tej_pomniejszonej wadze + cena i_tego elementu if (ap>a[i-1][j]) { // jeŝeli warto dołoŝyc ten element... A[i][j]=ap; // to umieść w tablicy Aij nową większą wartość plecaka B[i][j]=1; // i odnotuj to w tabeli "wziętych" elementów else { A[i][j]=A[i-1][j]; // pozostaw plecak niezmieniony (przepisz wartość z poprzedniego wiersza) B[i][j]=0; // i nie notuj elementu w tabeli "wziętych" elementów for (int j=0;j<=w; j++) { cout.width(3); cout<<a[i][j]<<" "; cout<<"\nnajwieksza wartosc plecaka = "<<A[n][W]<<endl<<endl; // tu jest rozwiązanie for (int i=1; i<=n; i++) { for (int j=0;j<=w; j++) cout<<b[i][j]<<" "; // wyświetlam jeszcze zawartość wektora B

cout<<"\nnalezy zapakowac elementy o numerach: "; int k=w; // plecak ma wagę W // lista zapakowanych elementów for (int i=n; i>0; i--) { // zaczynam od n-tego wiersza i W-tej kolumny wektora B ij if (B[i][k]==1) { // jeśli jest 1 cout<<i<<" "; // to znaczy Ŝe i-ty element jest zapakowany k=k-e[i].waga; // zmniejszam wagę plecaka o wagę tego elementu if (k<1) {break; // nastepnego elementu szukam w wierszu i-1 i kolumnie k system("pause"); return EXIT_SUCCESS; Inne problemy, które można próbować rozwiązać powyższymi metodami: Problem kasjera/bankomatu wypłacenie określonej kwoty pieniędzy: Dysponujesz nominałami: 1zł, 2 zł, 5 zł, 10 zł, 20 zł, 50 zł, każdy nominał w nieograniczonej ilości. Napisz program który pobiera z klawiatury wartość żądanej kwoty, a następnie wypłaca ją przy pomocy minimalnej liczby monet/banknotów. Załadunek (z arkusza maturalnego) Na rampie magazynu znajdują się paczki o różnych masach. Ładowanie paczek na naczepę tir-a musi być prowadzone w sposób następujący: z rampy do samochodu wkładane są paczki od najcięższej do najlżejszej; załadunek naczepy zostaje przerwany, gdy dołożenie kolejnej paczki spowodowałoby, że średnia arytmetyczna masy załadowanych na naczepę paczek będzie mniejsza od granicznej wartości D lub została już osiągnięta maksymalna liczba paczek M, które zmieszczą się na naczepie, lub załadowano wszystkie paczki. Napisz program który pobiera dane: D: wartość graniczna dodatnia liczba całkowita, M: maksymalna liczba paczek, które zmieszczą się na naczepie dodatnia liczba całkowita, N: liczba paczek wystawionych na rampę dodatnia liczba całkowita, We[1...N]: tablica zawierająca masy paczek dodatnie, różne liczby całkowite. zaś oblicza i zwraca liczbę paczek załadowanych na naczepę.