Problem plecakowy. Dokumentacja. 3 kwietnia. Algorytm dokładny oparty na metodzie programowania dynamicznego

Podobne dokumenty
Programowanie dynamiczne cz. 2

Podstawy Programowania C++

Schemat programowania dynamicznego (ang. dynamic programming)

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Expo Composer Garncarska Szczecin tel.: info@doittechnology.pl. Dokumentacja użytkownika

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

Modele i narzędzia optymalizacji w systemach informatycznych zarządzania

6.4. Efekty specjalne

Algorytm. a programowanie -

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

Rozdział 4 KLASY, OBIEKTY, METODY

Sortowanie zewnętrzne

Programowanie obiektowe

Analiza i projektowanie oprogramowania. Analiza i projektowanie oprogramowania 1/32

Programowanie obiektowe

Makropolecenia w Excelu

Algorytmy sortujące i wyszukujące

Złożoność obliczeniowa zadania, zestaw 2

Temat: Algorytmy zachłanne

XQTav - reprezentacja diagramów przepływu prac w formacie SCUFL przy pomocy XQuery

Dodatek Solver Teoria Dodatek Solver jest częścią zestawu poleceń czasami zwaną narzędziami analizy typu co-jśli (analiza typu co, jeśli?

Algorytmy i struktury danych.

Spis treści. I. Skuteczne. Od autora... Obliczenia inżynierskie i naukowe... Ostrzeżenia...XVII

OSTASZEWSKI Paweł (55566) PAWLICKI Piotr (55567) Algorytmy i Struktury Danych PIŁA

utworz tworzącą w pamięci dynamicznej tablicę dwuwymiarową liczb rzeczywistych, a następnie zerującą jej wszystkie elementy,

Wykład 2 Składnia języka C# (cz. 1)

Tak przygotowane pliki należy umieścić w głównym folderze naszego programu. Klub IKS

OPROGRAMOWANIE DEFSIM2

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

Maciej Piotr Jankowski

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Algorytmy i struktury danych Laboratorium Nr 4

Projektowanie i analiza algorytmów

Papyrus. Papyrus. Katedra Cybernetyki i Robotyki Politechnika Wrocławska

4.2. Ustawienia programu

Zad. 3: Układ równań liniowych

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

SYSTEMY OPERACYJNE I SIECI KOMPUTEROWE

Zagadnienia programowania liniowego dotyczą modelowania i optymalizacji wielu problemów decyzyjnych, na przykład:

Tabele przestawne tabelą przestawną. Sprzedawcy, Kwartały, Wartości. Dane/Raport tabeli przestawnej i wykresu przestawnego.

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

lekcja 8a Gry komputerowe MasterMind

Informatyka II. Laboratorium Aplikacja okienkowa

Układy VLSI Bramki 1.0

ZAGADNIENIE TRANSPORTOWE

Utworzenie aplikacji mobilnej Po uruchomieniu Visual Studio pokazuje się ekran powitalny. Po lewej stronie odnośniki do otworzenia lub stworzenia

Tom 6 Opis oprogramowania Część 8 Narzędzie do kontroli danych elementarnych, danych wynikowych oraz kontroli obmiaru do celów fakturowania

Optymalizacja systemów

1. Pamięć wirtualna. 2. Optymalizacja pliku pamięci wirtualnej

Programowanie obiektowe

Aplikacje w środowisku Java

Zastosowanie Informatyki w Medycynie

Instrukcja obsługi programu Do-Exp

Microsoft EXCEL SOLVER

5.4. Tworzymy formularze

Projektowanie baz danych za pomocą narzędzi CASE

Za pierwszy niebanalny algorytm uważa się algorytm Euklidesa wyszukiwanie NWD dwóch liczb (400 a 300 rok przed narodzeniem Chrystusa).

Wykład 8: klasy cz. 4

wagi cyfry pozycje

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

Zadania laboratoryjne i projektowe - wersja β

Zapisywanie algorytmów w języku programowania

Programowanie celowe #1

Polcode Code Contest PHP-10.09

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

Ćwiczenia nr 4. Arkusz kalkulacyjny i programy do obliczeń statystycznych

Sposób tworzenia tabeli przestawnej pokażę na przykładzie listy krajów z podstawowymi informacjami o nich.

Wprowadzenie do projektu QualitySpy

Instrukcja obsługi. Helpdesk. Styczeń 2018

Podstawy informatyki. Elektrotechnika I rok. Język C++ Operacje na danych - wskaźniki Instrukcja do ćwiczenia

DODAWANIE ARTYKUŁÓW DO STRONY INTERNETOWEJ

Zaawansowane algorytmy i struktury danych

Pomorski Czarodziej 2016 Zadania. Kategoria C

Jak przygotować pliki gotowe do publikacji w sieci za pomocą DigitLabu?

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

1 Wprowadzenie do algorytmiki

Rozdział 5: Style tekstu

Algorytmy i Struktury Danych

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Stawiamy pierwsze kroki

4. Funkcje. Przykłady

Modelowanie diagramów klas w języku UML. Łukasz Gorzel @stud.umk.pl 7 marca 2014

Rys.1. Technika zestawiania części za pomocą polecenia WSTAWIAJĄCE (insert)

Definicje. Algorytm to:

Projekt współfinansowany przez Unię Europejską w ramach Europejskiego Funduszu Społecznego

PROE wykład 3 klasa string, przeciążanie funkcji, operatory. dr inż. Jacek Naruniec

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

Teraz bajty. Informatyka dla szkoły podstawowej. Klasa VI

Projekt ZSWS. Instrukcja uŝytkowania narzędzia SAP Business Explorer Analyzer. 1 Uruchamianie programu i raportu. Tytuł: Strona: 1 z 31

UMOWY INSTRUKCJA STANOWISKOWA

Instrukcja laboratoryjna cz.3

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy

Wykład 3 Składnia języka C# (cz. 2)

Programowanie dynamiczne

Tutorial prowadzi przez kolejne etapy tworzenia projektu począwszy od zdefiniowania przypadków użycia, a skończywszy na konfiguracji i uruchomieniu.

Rozkład materiału do zajęć z informatyki. realizowanych według podręcznika

Rozpoznawanie obrazu. Teraz opiszemy jak działa robot.

dr inż. Jarosław Forenc

Zaawansowane aplikacje WWW - laboratorium

Transkrypt:

Dokumentacja 3 kwietnia 2009 Problem plecakowy Algorytm dokładny oparty na metodzie programowania dynamicznego Optymalizacja w systemach dyskretnych Prowadzący: dr inż. Jarosław Pempera Termin: ŚR/P/9 15 Paweł Porombka (148955)

1. PROBLEM PLECKOWY ROZWIĄZNIE PROBLEM PLECAKOWY 1.1 Problem Plecakowy - (ang. Knapsack problem), jest jednym z typowych problemów dyskretnych. Bardzo często przytaczany jako przykład w literaturze i bardzo często właśnie na tym problemie tłumaczone są techniki programowania dynamicznego. Formalnie dyskretny problem plecakowy możemy sformułować następująco: Mamy zbór przedmiotów A: gdzie każdy przedmiot posiada dwie właściwości w postaci liczb całkowitych (dla nas nieujemnych): Jeżeli zdefiniujemy pewien plecak (pojemnik), który posiada skończoną pojemność P, oraz zbiór zmiennych binarnych możemy zdefiniować następujący problem optymalizacyjny, który będzie polegał na maksymalizacji funkcji celu postaci: przy spełnionych ograniczeniach: W ten sposób sformułowaliśmy dyskretny problem plecakowy. Oczywiście możemy rozważać zmienne, wówczas mówilibyśmy o ciągłym problemie plecakowym, jednakże program rozważa dyskretną formę problemu. Otrzymaliśmy zatem konkretny problem, który polega na znalezieniu takiej grupy przedmiotów (zbioru), spośród określonych wcześniej, których wartość będzie największa uzyskamy największy zysk, ale które to przedmioty zmieszczą się do plecaka, czyli ich sumaryczna waga nie przekroczy dopuszczalnej wagi plecaka. Potocznie zwany problemem złodzieja okradającego jubilera, problem plecakowy jest praktycznym problemem, z którym borykają się choćby firmy transportowe. Wówczas polega on na optymalnym załadowaniu nie tylko przedmiotów o określonej wadze do środka transportu, ale również na odpowiednim rozłożeniu tych przedmiotów, by zmieściło się ich jak najwięcej. Jest to jednak problem bardziej złożony. 2

PROGRAMOWANIE DYNAMICZNE 1.2 Jednymi z interesujących technik programistycznych, są techniki programowania dynamicznego. Strategia programowania dynamicznego została wymyślona przez Richarda Bellman a. Jej założenia są dość proste rozłożyć problem na mniejsze podproblemy, które dość łatwo możemy rozwiązać, jeżeli tylko spełniają one własność optymalnej podstruktury 1. Takie rozwiązania cząstkowe pozwalają systematycznie zbliżać się do optimum problemu, przy czym a kolejnych krokach wykorzystujemy najlepsze poprzednie rozwiązania. Metoda ta pozwala wykorzystać rekurencyjną zależność między rozwiązaniami w kolejnych krokach algorytmu i tym samym obliczać każdy problem tylko raz. Parametry wykorzystywane w programowaniu dynamicznym (najczęściej w liczbie dwóch) ustalamy arbitralnie, gdyż to my budujemy algorytm, który ma rozwiązać nasz problem. Zatem bardzo ważnym elementem okazuje się być odpowiedni wybór parametrów i dostosowanie ich do postaci wykorzystywanej w technice programowania dynamicznego. Zdarza się oczywiście, że liczba parametrów wzrasta a ich dobór jest dość skomplikowany, jednakże należy pamiętać, że w programowaniu dynamicznym najczęściej wykorzystanie zasobów (pamięci) wzrasta proporcjonalnie do iloczynu największych wartości parametrów, zatem i czas wykonywania się wydłuża. Z tych powodów rzadko stosuje się więcej niż dwa parametry, gdyż przestaje to być efektywne. W jaki sposób definiować problem dla programowania dynamicznego? Na początek należy poznać podstawowe pojęcie programowania dynamicznego: etap (stadium) fragment procesu szukania rozwiązania, jest charakteryzowany przez pewien stan rozpatrywanego procesu. Z etap możemy przyjąć pewien odcinek czasu, czy drogi. Stadium może stanowić również pewien etap w myśleniu o procesie. Najogólniej rzecz ujmując etap polega na wprowadzeniu nowego wariantu rozwiązania. Dokładnie ilustruje to Rys.1. Widzimy tutaj zależność wykorzystania poprzedniego wyniku by wykonać kolejny krok. Oczywiście nie zawsze musimy korzystać z poprzedniego wyniku, może to być wiele wyników wstecz. x 1 x 2 x n S 0 S 1 S 2 S n-1 S n Rys.1. Schemat wieloetapowego procesu decyzyjnego, który doskonale pokazuje sposób realizacji programowania dynamicznego. S i to wyniki poprzednich problemów (w zerowym kroku najczęściej 0), x i to kolejne decyzje podejmowane w każdym etapie. 1 Dotyczy problemów rozwiązywalnych algorytmami. Własność optymalnej podstruktury spełniają rozwiązania optymalne, które są funkcją rozwiązań optymalnych podproblemów. 3

W ten sposób znając pewne własności możemy podsumować teorię programowania dynamicznego podstawowymi dwoma stwierdzeniami, które są filarami całej techniki: a) n wymiarowa funkcja celu powinna mieć postać sumy n funkcji celu, o jednej zmiennej decyzyjnej dla różnych etapów procesu, b) Wartość uzyskana na pewnym etapie zależy od stanu w etapie poprzednim, oraz decyzji uzyskanej w rozważanym stadium. Taka wartość nie powinna jednak zależeć od tego w jaki sposób (jaką drogą) zostało uzyskane rozwiązanie tzw. własność Markowa. Do procesu spełniającego własność Markowa stosuje się podstawową własność strategii optymalnej Bellman a: Polityka optymalna ma tę własność, że niezależnie od początkowego stanu i początkowej decyzji pozostałe decyzje muszą stanowić politykę optymalną ze względu na stan wynikający z pierwszej decyzji. 4

PROGRAMOWANIE DYNAMICZNE PROBLEM PLECAKOWY 1.3 Spróbujmy rozważyć problem plecakowy, którym mamy się zająć, przy użyciu techniki programowania dynamicznego. Na początek należy przyjąć, które ze zmiennych zdefiniowanych w problemie ( Rozdz. I ) będą wykorzystane do stworzenia struktury wieloetapowej algorytmu. Biorąc pod uwagę, że problem ma zostać podzielony na podproblemy, które stanowią częściowe rozwiązanie i powinny być elementarne, spróbujmy przeanalizować funkcję celu: przy ograniczeniach: Pierwszą ze zmiennych z pewnością będzie numer kolejno wybieranego elementu - i, gdyż będziemy w stanie określić wagę i wartość tego elementu. Kolejna zmienna decyzyjna powinna zawężać lub poszerzać problem, zatem będzie to rozmiar plecaka P. Im mniejszy rozmiar tym prostsze znalezienie optymalnego upakowania. W ten sposób możemy już stworzyć odpowiednią tablicę (Rys.2.), która pozwoli realizować algorytm. 0 1 2 3 n i-ty element 0 0 0 0 0 0 1 0 2 0 3 0 P 0 OPT optimum Pojemność plecaka Rys.2. Przyjęta tablica rozwiązań cząstkowych dla programowania dynamicznego Umieszczone zera w pierwszym wierszu i kolumnie zostaną wyjaśnione później. Zatem poszukujemy takiego upakowania plecaka, by otrzymać jak największą wartość i nie przekroczyć ograniczeń. Z tego powodu w tabeli będziemy umieszczać kolejne optymalne wartości plecaka przy konkretnej pojemności plecaka oraz konkretny przedmiot, brany pod uwagę. Stąd definiujemy tabelę Q i,j która zawiera wartość plecaka (jego stan) w danym etapie procesu. Ściślej jest to funkcja celu, o nieco zmienionych ograniczeniach: 5

Należy już tylko ustalić w jaki sposób będziemy obliczali stany poszczególnych etapów (wartości komórek tabeli) procesu rozwiązywania problemu. Otóż zaproponujmy następujący algorytm rozwiązania stosujący wprowadzone oznaczenia, który ilustruje pseudokod na Rys.3. FOR TO DO BEGIN END FOR TO DO BEGIN END IF THEN := //(1) ELSE //(2) //rozwiązanie optymalne Rys.3. Algorytm rozwiązujący problem plecakowy, przy użyciu technik dynamicznych Analizując algorytm łatwo zauważyć skąd zera w pierwszej kolumnie i pierwszym wierszu. Otóż algorytm działa w ten sposób, że wykorzystuje znalezione wcześniej optymalne rozwiązanie dla danych etapów. Rozwiązanie składa się z dwóch kroków: 1. Jeżeli aktualny przedmiot nie zmieści się w bieżącym plecaku p i, to zwyczajnie przepisz poprzednie optymalne upakowanie z etapu wcześniejszego i-1 (1) 2. W przeciwnym wypadku sprawdź, które upakowanie będzie lepsze (większa wartość), to z poprzedniego etapu, czy to, które będzie zawierać aktualny element (2). W tej sytuacji dodajemy wartość aktualnego elementu, do stanu plecaka, który był optymalny w chwili, gdy pojemność plecaka była mniejsza o wagę bieżącego elementu (j w i ). W ten sposób odwołujemy się do innego najlepszego upakowania plecaka, które zostało już policzone. Zauważmy jak prosty jest algorytm a jego złożoność jest równa. Widzimy, zatem, że dla małych wielkości pojemności plecaka, oraz dla małej puli przedmiotów algorytm jest wielomianowy, jednakże w miarę wzrostu obu wartości staje się bardzo złożony, stąd nazwa algorytm pseudowielomianowy. Algorytmy te są o tyle praktyczne, że pozwalają na zalezienie rozwiązania danego problemu w czasie pseudowielomianowym. Sens tych technik kryje się właśnie w tej własności. Problemy, które dla innych algorytmów rozwiązań są NP-trudne, mają wykładniczą złożoność, dla pewnej grupy instancji problemów dają się rozwiązać w czasie wielomianowym. Jak się okazuje wiele problemów trudnych można rozwiązać dla praktycznych danych w czasie wielomianowym. 6

Analizując pseudo kod przedstawiony powyżej, możemy dojść do wniosku, że jest możliwe zredukowanie zużycia pamięci operacyjnej poprzez ograniczenie tablicy do jednego wektora głównego V1 j i jednego pomocniczego V0 j, gdzie oba odpowiadają kolumnom tabeli Q i,j. Wówczas otrzymamy następujący pseudokod: FOR TO DO BEGIN CHANGE //(1) END FOR TO DO BEGIN END IF THEN := //(2) ELSE //(3) //rozwiązanie optymalne Rys.4. Zoptymalizowany algorytm rozwiązujący problem plecakowy, przy użyciu technik dynamicznych Widzimy, że jedyną zmianą w naszym algorytmie jest dodanie nowego kroku (1), który zamienia nam miejscami dwa wektory. Możemy to oczywiście wykonać w innej formie, bez zamiany wektorów, a jedynie biorąc pod uwagę na przemian wektor V1 jako V0 i odwrotnie. W implementacji zastosowano język C#, który operując na referencjach nie wymaga tutaj żadnego nakładu obliczeniowego, stąd zamiana jest natychmiastowa. 7

2. ROZWIĄZANIE IMPELEMENTACYJNE W celu rozwiązania DPP (dyskretnego problemu plecakowego), czyli znalezienia rozwiązania optymalnego, w aplikacji wykorzystano programowanie dynamiczne. W klasie rozwiązującej ten problem zaimplementowano 2 formy funkcji rozwiązującej. Było to spowodowane ewolucją rozwiązań. W rezultacie udało się osiągnąć pojedynczy wektor o długości P, na którym pracuje algorytm zamiast tablicy 2D (n x P). Złożoność tak otrzymanego algorytm wynosi cały czas, jednakże po optymalizacji zajętość pamięci modyfikujemy z n x P ilości komórek tablicy do zaledwie P. CELE PROJEKTU 2.1 1. Rozwiązanie problemu plecakowego przy użyciu technik programowania dynamicznego. 2. Możliwość przetwarzania dużej liczby przedmiotów. 3. Określenie granicznego rozmiaru plecaka, z powodów wydajności. 4. Możliwość przetwarzania wsadowego plików z danymi (parametry linii komend). 5. Możliwość zapisu rozwiązania optymalnego do pliku. 6. Realizacja projektu w języku C# 3.0 (Windows Presentation Foundation) na platformie.net. 7. Wizualizacja obiektów z możliwością edycji ich parametrów. 8. Realizacja okienka Splash-Screen 2. 2 Ten element traktujemy jako dodatkowy, nie musi zostać zrealizowany. 8

SPECYFIKACJA WYMAGAŃ 2.2 1. ROZWIĄZANIE - PROGRAMOWANIE DYNAMICZNE A. Rozwiązanie problemu plecakowego, zrealizowane przy użyciu technik programowania dynamicznego. B. Optymalizacja algorytmu w taki sposób, by nie było potrzeby tworzenia nierzadko ogromnej tablicy danych na potrzeby programowania dynamicznego. C. Wynikiem powinna być pojedyncza liczba określająca wartość (sumę wartości wszystkich przedmiotów), przy optymalnie upakowanym plecaku. 2. WYDAJNOŚĆ A. Ograniczenie liczby przedmiotów do 100 000 obiektów. B. Ograniczenie rozmiaru plecaka do 500. C. Ograniczenie grafiki rastrowej w interfejsie programu, na korzyść grafiki wektorowej ze względu na charakter WPF. D. Pewna ograniczona (najlepiej stała 10..20) liczba przedmiotów, wykorzystywana do wizualizacji (patrz pkt. 4.A.). 3. PRZETWARZANIE WSADOWE A. Możliwość przetwarzania wsadowego plików z gotowymi danymi opisującymi przedmioty oraz plecak. B. Dane w plikach wejściowych są w określonym formacie, opisanym poniżej a) Plik jest w formacie tekstowym UTF-8/ASCII b) Plik zbudowany jest z dwóch kolumn c) Nagłówek pliku: - Pierwsza kolumna N = ilość wszystkich przedmiotów - Druga kolumna W = pojemność plecaka (maksymalna waga) d) Kolejne N wierszy (i = 1 N): - Pierwsza kolumna waga i-tego przedmiotu - Druga kolumna wartość i-tego przedmiotu C. Pliki wyjściowe to pliki w formacie UTF-8, zawierające jedną liczbę optymalne rozwiązanie problemu. D. Podczas przetwarzania wsadowego nie powinno pojawiać się okienko aplikacji. E. Po skończeniu przetwarzania pliku wejściowego, zapisywany jest wynik w pliku wyjściowym, a program zostaje zamknięty. 9

4. JĘZYK PROGRAMOWANIA C# 3.0/XAML (WPF), PLATFORMA.NET A. Wykonanie ciekawego interfejsu graficznego z prezentacją wizualną pewnej puli przedmiotów, których parametry (waga i wartość) mogą być modyfikowane w prosty sposób. Pula tych przedmiotów nie musi być wczytywana z pliku, może być zdefiniowana w programie. B. Musi być możliwość modyfikowania zdefiniowanych wartości w.w. przedmiotów oraz pojemności plecaka, w celu arbitralnego parametryzowania problemu. C. Program powinien wykorzystywać MS Framework 3.0/3.5. 5. REALIZACJA OKIENKA POWITALNEGO SPLASH-SCREEN 3 A. Okienko pojawiające się przed załadowaniem właściwego kodu programu oraz głownego okna aplikacji. B. Zabrania się umieszczania animacji i dużej ilości grafiki na tym okienku, ze względu na jego przeznaczenie. Musi się ono błyskawicznie załadować do pamięci. 3 Ten punkt specyfikacji wymagań jest opcjonalny 10

SPECYFIKACJA PROJEKTU 2.3 1. ROZWIĄZANIE PRZY UŻYCIU TECHNIK PROGRAMOWANIA DYNAMICZNEGO A. Optymalizacja algorytmu do postaci wykorzystującej tylko jeden 4 wektor danych w pamięci, zamiast dwuwymiarowej tablicy rozwiązań cząstkowych, co powoduje znaczne zmniejszenie zajętości pamięci B. Za wynik przyjmujemy liczbę znajdującą się w przetwarzanym wektorze w ostatniej jego komórce. W przypadku analizowania tablicy 2D byłby to element znajdujący się w prawym dolnym rogu tabeli. 2. UWZGLĘDNIENIE OGRANICZEŃ WYDAJNOŚCIOWYCH A. Ograniczenie liczby przedmiotów do 100 000 obiektów oraz pojemności plecaka do 500, realizuje konkretna funkcja TryAddParameters( ). Polega to wyłącznie na weryfikacji wejściowych parametrów pod względem zgodności z założeniami. 3. PRZETWARZANIE WSADOWE PLIKÓW Z DANYMI A. Przetwarzanie wsadowe zostaje włączone tylko i wyłącznie, gdy liczba parametrów linii komend pliku aplikacji jest równa 3. Pierwszy parametr to typ operacji jaki chcemy wykonać, następne dwa to kolejno plik wejściowy i wyjściowy. Argumenty w linii poleceń wyglądają następująco: program.exe -wsadowo plik_wejściowy.txt wynik.txt W tej sytuacji zostanie przetworzony plik plik_wejściowy.txt, a wynik zapisany w pliku wynik.txt. B. Podczas przetwarzania wsadowego nie pojawia się okienko aplikacji (nie jest w ogóle ładowane), natomiast pod koniec przetwarzania otrzymujemy komunikat w postaci okna dialogowego (podsumowanie i wynik). Po kliknięciu przycisku OK., program zostaje zamknięty. 4. REALIZACJA PROJEKTU W JĘZYKU C# 3.0/XAML (TECHNOLOGIA WPF), PLATFORMA.NET A. Program został wykonany w oparciu o MS Framework 3.5. B. Interfejs graficzny został zrealizowany w postaci pojedynczego okna głównego, w którego obszarze roboczym pracujemy. Kolejne okienka stanowią zwyczajnie kolejną warstwę (siatkę), która pojawia się (animacja) po konkretnym zdarzeniu. W ten sposób uzyskano dość efektowną aplikację. C. Wizualizacja obiektów polega na przedstawieniu zbioru ikonek (piłka, lornetka, itd.), które w rzeczywistości są komponentami stworzonymi na potrzeby aplikacji. Każdy komponent zawiera dodatkowe właściwości: wagę, wartość oraz nazwę danego przedmiotu. W ten sposób są one identyfikowane (patrz. Dokumentacja Interfejsu Graficznego). 4 Nie do końca jeden, gdyż zawsze musi być dodatkowy wektor tymczasowy. Oba wektory są przetwarzane na zmianę. Co zapobiega przepisywaniu wartości z jednego do drugiego. 11

D. Modyfikacja obiektów odbywa się w prosty sposób, klikając dwukrotnie na daną ikonkę. Wówczas mamy możliwość zmodyfikowania również pojemności plecaka. Więcej na ten temat w części Dokumentacja Interfejsu Graficznego. 5. REALIZACJA OKIENKA POWITALNEGO SPLASH-SCREEN A. Udało się zrealizować okienko pojawiające się podczas ładowania głównej części aplikacji. B. Okienko Splash-Screen, musi wywoływać ładowanie pozostałej części aplikacji w osobnym wątku STA (interfejsu graficznego). Wynika to ze struktury realizacji interfejsu w WPF. C. Po utworzeniu obiektu aplikacji głównej oraz załadowaniu bibliotek pojawia się nam okienko główne aplikacji, natomiast Splash-Screen znika. D. Zgodnie z przyjętymi ograniczeniami, okno powitalne nie realizuje żadnych skomplikowanych animacji, a jedynie jest wariacją dwóch obrazków plecaka z przedmiotami (jeden to odblask na podłodze) oraz tekst LOADING. 12

MODEL ROZWIĄZANIA 2.4 Do modelowania problemu oraz aplikacji wykorzystano narzędzia do projektowania w języku UML: a) Poseidon for UML 6.0, b) Visual Paradigm for UML 6.4. Modelowanie podzielono na kilka etapów, których realizacją były diagramy poszczególnych problemów. 2.4.1. OGÓLNY MODEL SYSTEMU Na rys.5. przedstawiono prosty diagram obrazujący ogólną strukturę całego systemu. Rys.5. Diagram zaimplementowanego systemu 13

2.4.2. DIAGRAM DZIAŁANIA ALGORYTMU Na rys.6. przedstawiono diagram algorytmu rozwiązującego problem plecakowy. Na diagramie uwzględniono oznaczenia oryginalne z implementacji, opisane w części 2.5. Rys.6. Diagram działania algorytmu 14

2.4.3. DIAGRAM DZIAŁANIA INTERFEJSU Rys.7. Diagram działania interfejsu użytkownika na zasadzie diagramu akcji UML 15

2.4.4. DIAGRAM PODSTAWOWYCH KLAS APLIKACJI Rys.8. Diagram klas utworzonych na potrzeby systemu 16

DOKUMENTACJA KODU 2.5 class KnapsackSolver Klasa zawierająca zaimplementowany algorytm rozwiązania problemu plecakowego, przy użyciu technik programowania dynamicznego. Klasa jest nieskomplikowana, a jej obsługa wyjątkowo prosta. Na potrzeby projektu została ona zamknięta w osobnym pliku, przy czym postanowiono, że nie zostanie utworzona w postaci biblioteki DLL. const int BagLimit = 500; Pole prywatne (stała typu liczba całkowita), przechowuje maksymalną dopuszczalną wartość plecaka, która została poddana weryfikacji: TryAddParameters( ). const int ObjectsLimit = 100000; Pole prywatne (stała typu liczba całkowita), przechowuje maksymalną dopuszczalną liczbę przedmiotów w puli, która została poddana weryfikacji: TryAddParameters( ). private int[] _Weights Pole prywatne (tablica liczb całkowitych), określające wagi wszystkich przedmiotów wczytane do instancji klasy. Indeksacja od 0.._ItemsCount. To pole zostaje przypisane w chwili dodania poprawnych danych metodą TryAddParameters( ), bądź wymuszenia dodania danych przy użyciu ForceAddParameters( ). 17

private int[] _Values Pole prywatne (tablica liczb całkowitych), określające wszystkie wartości (ceny) dodanych przedmiotów. Indeksacja od 0.._ItemsCount. To pole zostaje przypisane w chwili dodania poprawnych danych metodą TryAddParameters( ), bądź wymuszenia dodania danych przy użyciu ForceAddParameters( ). private int _TotalWeight Pole prywatne (liczba całkowita), określające maksymalną dopuszczalną wagę plecaka. To pole zostaje przypisane w chwili dodania poprawnych danych metodą TryAddParameters( ), bądź wymuszenia dodania danych przy użyciu ForceAddParameters( ). private int _ItemsCount Pole prywatne (liczba całkowita), określające ilość przedmiotów, które zostały dodane do puli w instancji klasy. To pole zostaje przypisane w chwili dodania poprawnych danych metodą TryAddParameters( ), bądź wymuszenia dodania danych przy użyciu ForceAddParameters( ). Z zewnątrz klasy pole jest dostępne jako właściwość tylko do odczytu: ItemsCount. private int[,] _Results Pole prywatne (dwuwymiarowa tablica liczb całkowitych), zawierające wyniki w postaci tablicy kroków algorytmu programowania dynamicznego. Pole jest inicjalizowane tylko po wywołaniu metody pierwszej wersji rozwiązywania algorytmu: SolveProblem1(). Z zewnątrz klasy pole jest dostępne jako właściwość tylko do odczytu: Results. Rozwiązanie jest przechowywane naturalnie w prawym dolnym rogu tablicy (jest to rozwiązanie optymalne). 18

public int[,] Results Właściwość publiczna (dwuwymiarowa tablica liczb całkowitych), pozwalająca na dostęp do tablicy rozwiązań cząstkowych algorytmu dynamicznego. Odpowiada prywatnemu polu: _Results. Właściwość tylko-do-odczytu. public int ItemsCount Właściwość publiczna (liczba całkowita), pozwalająca na dostęp do liczby określającej ilość przedmiotów zaakceptowanych w instancji klasy. Odpowiada prywatnemu polu: _ ItemsCount. Właściwość tylko-do-odczytu. public readonly int BagSizePercentage Właściwość publiczna (liczba całkowita), pozwalająca na dostęp do liczby określającej, jaka może być maksymalna suma wag wszystkich dodanych przedmiotów w stosunku do pojemności plecaka. Wartość ta jest określana z góry podczas tworzenia instancji obiektu klasy i wyrażona jest w procentach wielkości plecaka. Domyślnie ustawiana na jest na 70(%) i nie może zostać zmodyfikowana. Pozwala nałożyć pewne wymogi co do parametrów przedmiotów. Ta wartość m.in. jest wykorzystywana podczas weryfikacji danych metodą TryAddParameters( ). Właściwość tylko-do-odczytu. public KnapsackSolver(int BagSizePercent) Publiczny konstruktor parametryzujący. Tworzy instancję obiektu klasy. 19

Parametry: int BagSizePercent - określa procentowy udział wielkości przedmiotów, w wielkości plecaka (patrz BagSizePercentage) Konstruktor ustawia podaną wartość jednorazowo, później właściwość jest już niemodyfikowalna. public KnapsackSolver() Publiczny konstruktor domyślny. Tworzy instancję obiektu klasy, ustawiając od razu domyślna wartość parametru BagSizePercentage na 70(%). Konstruktor ustawia podaną wartość jednorazowo, później właściwość jest już niemodyfikowalna. public Boolean TryAddParameters( int[] ItemsWeights, int[] ItemsValues, int BagMaxWeight ) Publiczna metoda. Pozwala na weryfikację dodawanych danych, które będą przetwarzane przy użyciu metod rozwiązujących w dwóch wersjach: SolveProblem1() oraz SolveProblem2(). Parametry: int[] ItemsWeights - Wektor określający wagi wszystkich przedmiotów dodawanych do puli elementów w klasie (patrz _Weights). int[] ItemsValues - Wektor określający wartości wszystkich przedmiotów dodawanych do puli elementów w klasie (patrz _Values). int BagMaxWeight - Liczba całkowita określająca maksymalny rozmiar plecaka, 20

czyli jego pojemność (patrz _TotalWeight). Wynik: Boolean zwracany wynik to true, gdy dodano poprawnie nowe parametry, lub false, gdy parametry są nie poprawne i nie zostały dodane. Dzięki tej metodzie jest możliwość weryfikacji, czy dany problem jest już rozwiązanym gdyż pula przedmiotów o sumarycznej wadze mniejszej, równej pojemności plecaka może w całości mieścić się w nim. Co za tym idzie nie ma tutaj sensu rozwiązywanie algorytmu. Kolejnym wariantem jest sprawdzenie ilości przedmiotów oraz wielkości plecaka, które są ograniczone zgodnie ze specyfikacją. public void ForceAddParameters( int[] ItemsWeights, int[] ItemsValues, int BagMaxWeight ) Publiczna metoda. Pozwala na dodanie nowych danych o przedmiotach do puli klasy, bez weryfikacji ich poprawności. Dalej dane te są przetwarzane przez jedną z metod rozwiązujących: SolveProblem1() oraz SolveProblem2(). Parametry: int[] ItemsWeights - Wektor określający wagi wszystkich przedmiotów dodawanych do puli elementów w klasie (patrz _Weights). int[] ItemsValues - Wektor określający wartości wszystkich przedmiotów dodawanych do puli elementów w klasie (patrz _Values). int BagMaxWeight - Liczba całkowita określająca maksymalny rozmiar plecaka, czyli jego pojemność (patrz _TotalWeight). 21

Metoda ta zwyczajnie wymusza wykonanie algorytmu dla wszystkich podanych danych. Jest wykorzystywana podczas przetwarzania wsadowego. public int[,] SolveProblem1() Publiczna metoda rozwiązująca. Metoda, która znajduje rozwiązanie optymalne metodą techniki programowania dynamicznego, z wykorzystaniem dwuwymiarowej tablicy cząstkowych rozwiązań. Ma to związek z charakterem programowania dynamicznego. Metoda opracowana na potrzeby prezentacji wyników w postaci tablicy, oraz dla potrzeb interfejsu (mniej wydajna niż SolveProblem2()). Wynik: int[,] zwracany wynik to dwuwymiarowa tablica liczb całkowitych, która jest wspomnianą tablicą cząstkowych rozwiązań problemu. Prawy dolny róg tabeli (komórka) zawiera znalezione optymalne rozwiązanie. Ze względów wydajnościowych nie jest ona raczej stosowana, do przetwarzania wsadowego wykorzystuje się metodę w wersji 2. public int SolveProblem2() Publiczna metoda rozwiązująca. Metoda, która znajduje rozwiązanie optymalne metodą techniki programowania dynamicznego, z wykorzystaniem jednowymiarowej tablicy (wektora) cząstkowych rozwiązań. Ma to związek z charakterem programowania dynamicznego. Metoda stanowi ulepszenie metody w wersji 1 (SolveProblem1()) w sferze wydajnościowej, zajętości pamięci oraz szybkości. Tak naprawdę w metodzie jest wykorzystywany jeszcze jeden wektor pomocniczy, zatem operacje odbywają się na dwóch identycznych wektorach zamienianych miejscami. Wadą tej techniki jest niemożność odzyskania sekwencji dodanych do plecaka przedmiotów, przy sensownej wydajności. Wynik: 22

int zwracany wynik to optymalne upakowanie plecaka, a dokładnie optymalne rozwiązanie, którym jest wartość plecaka tak upakowanego. class FileOperations Klasa pozwalająca na wczytywanie plików z danymi dotyczącymi problemu. Format danych opisano w specyfikacji programu. public static Boolean ReadDataFromFile( String FileName, out int[] w_i, out int[] c_i, out int W ) Publiczna metoda statyczna. Wczytuje parametry algorytmu z pliku tekstowego o określonym formacie (UTF-8), oraz zwraca te parametry w postaci argumentów oznaczonych operatorem out. Parametry: String FileName - Łańcuch znaków opisujący ścieżkę do pliku wraz z jego nazwą, int[] w_i int[] c_i int[] W - Wektor określający wagi poszczególnych przedmiotów. Operator out definiuje, że podany parametr jest zwracany w tym miejscu, zatem pod tą zmienną zostanie podstawiony wynik, - Wektor określający wartości poszczególnych przedmiotów, - Pojemność plecaka. poszczególnych przedmiotów. Operator out definiuje, że podany parametr jest zwracany w tym miejscu, zatem pod tą zmienną 23

zostanie podstawiony wynik. Wynik: Boolean zwracany wynik to true, gdy wczytano poprawnie nowe parametry, lub false, gdy plik FileName nie istniał. Metoda ta jest wykorzystywana podczas przetwarzania wsadowego do wczytywania danych z pliku wejściowego. 24

INTERFEJS (UI) 2.6 Ponieważ interfejs został wykonany w jednej z nowszych technologii firmy Microsoft, postanowiono, że opis tegoż przedsięwzięcia powinien również stanowić część dokumentacji. Platforma.NET pozwala na niezwykle rozwijanie się w sferze nowych technologii. Tego typu technologia jest Windows Presentation Foundation (WPF), która stanowy śmiały krok na przód w dziedzinie projektowania interfejsów. Ten wyjątkowy pakiet dostępny w bibliotekach MS Framework od wersji 3.0, posiada niesamowite możliwości. Teraz okienko staje się przestrzenią dla animacji zarówno 2D jak i 3D, a efekty graficzne są tutaj na bardzo wysokim poziomie. Cała technologia jest oparta o MS DirectX od wersji 9.0c, co powoduje, że wspiera wszelką grafikę wektorową. 2.6.1. SPLASH SCREEN Ponieważ, jak wspomniano w specyfikacji projektu, WPF wymaga załadowania kilku bibliotek DirectX, oraz wygenerowania pewnej liczby elementów graficznych, pierwsze uruchomienie aplikacji zajmuje trochę czasu. Z tego powodu w projekcie po raz pierwszy uwzględniono okno powitalne, które aktualnie stosuje praktycznie w każdej aplikacji. Okienko pojawia się tuż po uruchomieniu aplikacji, i znika w chwili załadowania się okienka głównego z wygenerowaną i przygotowaną grafiką. Wówczas pojawia się główne okienko programu. Jest praktycznie jedyna rola tego elementu graficznego (Rys.9.) aplikacji. Rys.9. Splash Screen aplikacji KnapsackProblem 25

2.6.2. OKNO GŁÓWNE APLIKACJI Interfejs programu stanowi główne okno aplikacji przedstawione na Rys.10. Rys.10. Główne okienko aplikacji Po lewej widzimy zbiór przykładowych przedmiotów w postaci ikon. Każdy przedmiot stanowi osobny komponent UI, który posiada trzy właściwości: Nazwę (String), Wagę (int), Wartość (int). Dzięki nim klikając na konkretny element dwukrotnie jesteśmy w stanie w prosty sposób modyfikować te właściwości, wraz ze zmianą pojemności plecaka (Rys.11.) 26

Rys.11. Modyfikacja parametrów dostępnych przedmiotów Oznaczenia na rysunku dotyczą w tym przypadku obiektu Lornetka. Oczywiście nazwa przedmiotu może również zostać zmodyfikowana w tym okienku. Klikając OK zatwierdzamy wprowadzone zmiany. 2.6.3. ROZWIAZANIE PROBLEMU Gdy skonfigurowaliśmy przedmioty w wymagany sposób możemy przystąpić do obejrzenia wyników. Ponieważ obiektów jest dość mało, obliczenia trwają ułamek sekundy. W tym celu wystarczy kliknąć na plecak (Rys.12.), co powoduje uruchomienia algorytmu. Rys.12. Przycisk rozwiązujący problem 27

Wynik otrzymujemy w postaci wartości plecaka, który został optymalnie upakowany (Rys. 13.). Rys.13. Wynik obliczeń W przypadku domyślnych ustawień przedmiotów wartość ta wynosi 38. Dodatkowo, ponieważ algorytm wykorzystuje drugą wersję metody rozwiązującej, mamy możliwość obejrzenia tablicy cząstkowych wyników, które zostały wykorzystane podczas rozwiązywania. W tym celu klikamy fioletową tabelę w prawym górnym rogu otwartego okienka (Rys. 14.). Rys.14. Przycisk pokazujący tabelę rozwiązań cząstkowych 28

Pojawi się wówczas okienko z tabelą, której kolejno numerowane wiersze odpowiadają kolejnym rozmiarom plecaka, natomiast pierwszy wiersz to indeksy kolumn numery przedmiotów (pula). Rozwiązanie optymalne znajduje się w prawym dolnym rogu tabeli (Rys. 15.). Rys.15. Wynik obliczeń w postaci tabeli rozwiązań cząstkowych Poszczególne okienka zamyka się z reguły klikając na nie, jednakże w przypadku tabeli korzystamy z przycisku w kształcie litery X. 29

3. WYDAJNOŚĆ I WYMAGANIA WYMAGANIA APLIKACJI 3.1 Program został napisany z uwzględnieniem następujących wymogów 1. System Microsoft Windows XP SP3 lub nowszy 2. Zainstalowany MS Framework 3.5 SP1 3. Komputer z procesorem co najmniej 800MHz 4. Z minimalną ilością pamięci operacyjnej 256MB 5. Miejscem na dysku twardym ok. 2MB 6. Dodatkowo system koniecznie musi posiadać Microsoft DirectX 9.0c lub nowszy (Win XP posiada już zainstalowany ten zestaw modułów) WYDAJNOŚĆ 3.2 Aplikacja została przetestowana pod względem realizacji wielokrotnego rozwiązywania problemu dostępnymi dwoma sposobami, w celu wykazania różnicy. Testy wykonano na dość szybkim komputerze Athlon Core 2 Duo 4.2GHz, pamięci operacyjnej 2GB/800MHz, dysku twardym ATA, oraz systemie operacyjnym MS Windows XP spełniającym stawiane wcześniej wymagania. 16 Zależność czasu wykonywania algorytmów od ilości dostępnych przedmiotów Pojemnoość plecaka P = 150 Czas wykonywania algorytmów [ms] 14 12 10 8 6 4 2 Algorytm wykorzystujący tablicę 2D danych cząstkowych Algorytm zoptymalizowany, wykorzystuje jeden wektor danych 0 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 Ilość dostępnych przedmiotów n 30

3 Zależność czasu wykonywania algorytmów od rozmiaru plecaka Ilość przedmiotów n = 100 Czas wykonywania algorytmów [ms] 2,5 2 1,5 1 0,5 0 Algorytm zoptymalizowany, wykorzystuje jeden wektor danych Algorytm wykorzystujący tablicę 2D danych cząstkowych 50 80 110 140 170 200 230 260 290 320 350 380 410 440 470 500 530 560 590 620 Rozmiar plecaka P Przedstawione wykresy ilustrują wpływ poszczególnych parametrów na czasy wykonywania algorytmów. Widzimy, że oba wykresy wyraźnie wraz ze wzrostem rozmiaru plecaka i ilości przedmiotów zaczynają rozbiegać się w stronę nieskończoności. Jest to spowodowane różnicami w algorytmach, które testowano. Przedstawione dwa podejścia wyraźnie ilustrują przewagę drugiej metody nad pierwszą. Optymalizacja zajętości pamięci spowodowała również przyspieszenie algorytmu. W ogólnym rozrachunku są to dość duże zmiany, skąd wniosek, że optymalizacja działa bardzo dobrze. Dla formalności testy realizowano poprzez wczytywanie danych z plików wejściowych. Początkowo wygenerowano odpowiednie zestawy danych, następnie wczytywano je i przetwarzano. Podczas pomiaru czasu uwzględniono tylko czas wykonywania się algorytmu, zaniechano pomiaru całościowego wraz z odczytem danych, gdyż byłoby to nie obiektywne (różne prędkości dysków twardych itp.) 31

4. BIBLIOGRAFIA 1. Sysło M., Deo N., Kowalik J. Algorytmy optymalizacji dyskretnej. PWN Warszawa 1995 2. Wikipedia wolna encyklopedia: http://pl.wikipedia.org/wiki/problem_plecakowy 3. Studia informatyczne beta: http://wazniak.mimuw.edu.pl Opracowania z zakresu programowania dynamicznego i zaawansowanego 4. Wykład z programowania dynamicznego: http://www.home.umk.pl/~bruzdaj/pliki/programowanie_dynam-wyklad.pdf 32