Úloha 2

Autor: Petr Chytil


Problém kýblů

K dispozici jsou dva kýble (předem daných obecně rozdílných objemů), vodovodní kohoutek a kanál. Na počátku jsou oba kýble prázdné. Vaším úkolem je docílit toho, aby v jednom kýblu byla voda o předem stanoveném objemu, přičemž můžete pouze naplnit plný kýbl z kohoutku, vylít celý kýbl do kanálu a přelít jeden kýbl do druhého tak, aby druhý kýbl nepřetekl.
Problém lze zobecnit tím, že připustíme užití většího počtu kýblů, aby na počátku řešení byla v kýblech nějaká voda, a že předepíšeme koncový objem vody v každém kýblu.

Zadání

Poznámky k implementaci

Zvolil jsem vlastní řešení v jazyce Java a nevyužíval tak nabízené prostředí. Řešení používá objekty třídy Vertex, jimž odpovídají jednotlivé stavy stavového prostoru a objekty třídy Bucket, kterým odpovídají jednotlivé kýble. Objekty kýbl nabízí všechny základní operace specifické pro tento problém (vylij, nalij, přelij), výpočet vzdálenosti od řešení (použito při heuristice) a pár dalších šikovných metod (hash, porovnání). Stav pak obsahuje seznam kýblů, umí zjistit, zda-li je řešením, počítá sumu vzdáleností kýblů od řešení a obsahuje metody pro usnadnění porovnání dvou stavů. V souboru Main je hlavní smyčka programu a zadání jednotlivých instancí. Každá instance je v ní vyřešena dvěma způsoby – BFS a heuristikou.

Řešení pomocí BFS

Na prohledávání do šířky není mnoho k vysvětlování. Z prvního stavu (ten je specifikován v zadání) vygeneruji všechny následníky pomocí operací vylij, nalij a přelij, které aplikuji na všechny kýble, které stav obsahuje. Takto vytvořené stavy přidám do obyčejné FIFO fronty. Před přidáním ještě kontroluji, zda-li již nebyl takovýto stav dříve vygenerován (z každého nově vytvořeného stavu vytvářím hash, která jej jednoznačně popisuje v našem omezeném stavovém prostoru – tuto hash si pak ukládám do hashmapy pro rychlé vyhledávání již vytvořených stavů). Další věcí je kontrola, jestli není stav řešením našeho problému – na to mi odpoví sám pomocí příslušné metody.

Řešení heuristikou

Heuristika kopíruje řešení pomocí BFS. Jediná změna v kódu je použití prioritní fronty. Ta řadí objekty na základě jejich jednoznačného pořadí. To jsem určil přiřazením přirozeného čísla n každému stavu. Číslo n jsem nazval vzdáleností od řešení. Na úrovni stavu je tato vzdálenost sumou vzdáleností jednotlivých kýblů, v tomto stavu obsažených. Kýbl má tuto vzdálenost určenou jako absolutní hodnotu rozdílu aktuálního množství vody v kýblu a požadovaného množství vody v kýblu. Žádné stavy se z prioritní fronty nevyřazují, takže máme jistotu, že nepřicházíme o řešení.

Zdrojový kód řešení

Výstup programu (naměřená data)

Tabulka srovnávající BFS s heuristikou

# instance

uzlů BFS

uzlů heuristika

operací BFS

operací heuristika

uzly %

operace %

1.1

8953

69

10

23

12975,36

43,48

1.2

5947

538

8

19

1105,39

42,11

1.3

5962

10

8

8

59620,00

100,00

1.4

27

5

3

4

540,00

75,00

2.1

49256

5152

16

52

956,06

30,77

2.2

32614

7774

12

65

419,53

18,46

2.3

22886

4979

11

67

459,65

16,42

2.4

214

448

5

12

47,77

41,67

2.5.

1931

613

7

16

315,01

43,75

3.1

59015

727

14

38

8117,61

36,84

3.2

55587

281

12

24

19781,85

50,00

3.3

30746

328

10

33

9373,78

30,30

3.4

252

50

5

12

504,00

41,67

3.5

2826

185

7

10

1527,57

70,00

3.6

15745

1047

9

42

1503,82

21,43



První dva sloupce ukazují počet uzlů, které byly expandovány (closed). V dalších dvou jsou vidět počty operací, které je potřeba provést nad naší množinou kýblů, abychom se dobrali k nalezenému řešení. A konečně poslední dva sloupce srovnávají v procentech BFS a heuristiku (oba jsou počítány jako BFS/heuristika).

Závěr

Vcelku jednoduchá heuristika nám pomohla (až na jednu výjimku potvrzující pravidlo) k výraznému snížení počtu navštívených uzlů stavového prostoru. Počet operací s kyblíky byl dle očekávání větší než když jsme stavový prostor poctivě procházeli pomocí BFS.