WebZdarma.cz

Řešení problému batohu a problému obchodního cestujícího pomocí genetických algoritmů

Problém batohu

Zadání: Máme dáno n předmětů. Pro každý předmět i = 1 ... n máme dánu hmotnost W[i] a cenu P[i]. Je dána kapacita C. Máme najít takový binární vektor x = (x[1] ... x[n]) takový, že
(1)   x[i] * W[i] C
a zároveň je celková cena batohu
(2)  P(x) =  x[i] * P[i]
je co největší.
 

Nabízí se hned několik možností, jak tento problém řešit pomocí genetických algoritmů. Ukažme si některé z nich.
 

Algoritmy Ap[i] - "Penalizační"

V těchto algoritmech reprezentujeme vektory pomocí binárních řetězců délky n. (Tzn. předmět je v batohu právě když x[i] = 1). Kvalitu (fitness) eval(x) spočteme takto:
eval(x) =  x[i] * P[i] - Pen(x),
kde Pen(x) je penalizační funkce.
V našem vyhledávacím prostoru se nacházejí i řetězce, které nesplňují podmínku (1) zadání. Pro tyto řetězce potřebujeme určit nějaké znehodnocení (potřebujeme zmenšit jejich fitness). Toto znehodnocení je reprezentováno penalizační funkcí. Penalizační funkce má hodnotu 0 pro řetězce splňující podmínku (1) zadání. Pro řetězce, které tuto podmínku nesplňují, je kladná. Je mnoho způsobů, jak vypočítávat penalizační funkci. Ukažme si některé možnosti: Ve všech třech případech je r = maxi = 1 .. n{P[i]/W[i]}.
 

Algoritmy Ar[i] - "Opravné"

Reprezentace generace bude v tomto typu algoritmu stejná, jako v Ap[i]. Odlišný bude výpočet kvality řetězce. Stejně jako v předchozím algoritmu nastává situace, kdy řetězec reprezentuje "přeplněný" batoh. Na tyto řetězce voláme opravnou funkci, která některé přebytečné věci z batohu vyhází. Nebo-li bude odebírat předměty z batohu (měnit 1 na 0 na určitých místech řetězce) tak dlouho, dokud nebude splněna podmínka (1) ze zadání (neboli dokud se zbytek do batohu nevejde). Tímto způsobem dostaneme z původního řetězce x řetězec x', který bude reprezentovat platné řešení. Fitness pro řetězec x budeme pak počítat vzorcem
eval(x) =  x'[i] * P[i]
kde x'[i] je opravený řetězec.
Nastává zde otázka, jestli by se neměli opravované řetězce nahrazovat v generaci opravenými řetězci (má-li x' nahradit x). Orvosh a Davis vyzkoumali, že nejlepší je tuto záměnu dělat s pravděpodobností 5%.
Poslední otázkou, kterou bychom si měli zodpovědět je, v jakém pořadí budou "vyhazovány" předměty z batohu opravným algoritmem. Ukažme si opět více způsobů, jak volit pořadí odebíraných předmětů.  

Algoritmy Ad[i] - "Dekódovací"

V těchto algoritmech kódujeme problém tak, abychom prohledávali pouze tu část prostoru, která splňuje podmínku (1) ze zadání. Vzdáváme se přitom reprezentace batohu jako binárního řetězce.
Jak tedy postupujeme:
Očíslujeme předměty a vytvoříme si jejich uspořádaný seznam. Mějme tedy třeba 6 různých předmětů a uspořádejme je do následujícího seznamu: (3,4,6,2,1,5). Mějme navíc vektor (4,3,4,1,1,1), který nám kóduje jednu z možností, jak naplnit batoh.
Tento vektor dekódujeme takto: Vybereme položku ve vektoru. Ta nám udává, kterou položku se seznamu máme vybrat. Příslušnou položku v seznamu zapíšeme do výsledku a vyškrtneme ji z kopie seznamu. Pokračujeme další položkou seznamu. Obsah kopie seznamu a výsledku se bude při dekódování našeho vektoru měnit takto:
(3,4,6,2,1,5)
( )

(3,4,6,1,5)
(2)

(3,4,1,5)
(2,6)

(3,4,1)
(2,6,5)

(4,1)
(2,6,5,3)

(1)
(2,6,5,3,4)

( )
(2,6,5,3,4,1) toto je dekódovaný řetězec.
Získané vektor nám udává pořadí, v jakém budou předměty dávány do batohu, dokud bude stačit kapacita batohu.
Při generování nové generace je potřeba ohlídat, abychom generovali pouze vektory které bude možno dekódovat. K tomu je třeba zajistit, aby číslo ve vektoru nebylo nikdy větší než délka zbývajícího seznamu. Tzn. na j-tém místě kódu může být pouze číslo n - j + 1, kde n je délka kódu. Díky této vlastnosti získáme při křížení dvou kódů vždy dva nové vektory, které lze dekódovat. Při mutaci musíme příslušné místo kódu měnit tak, aby platila výše zmíněná nerovnost.
Poslední otázkou, kterou musíme při programování vyřešit je, jakým způsobem získáme počáteční seznam pro dekódování. Naznačme si dva různé způsoby:

Výsledky pokusů:

Všechny víše popsané algoritmy byly testovány na těchto datech:
neprovázaná:
    W[i] = náhodné číslo od 1 do v
    P[i] =  náhodné číslo od 1 do v

slabě provázaná:
    W[i] = náhodné číslo od 1 do v
    P[i] =  W[i] + náhodné číslo od -r do r

silně provázaná:
    W[i] = náhodné číslo od 1 do v
    P[i] =  W[i] + r

Dvě různé velikosti batohu C1 = 2*v, C2 = 0.5 *  W[i]

Ap[i] nedávají dobré výsledky pro batoh kapacity C1 pro všechny typy dat.
Nejlepší výsledek pro kapacitu C2 dal algoritmus Ap[1] pro všechny typy dat.
"Vítězem kategorie  C1" se jednoznačně stal algoritmus Ar[2].
 

Problém obchodního cestujícího (TSP)

Zadání: Obchodní cestující má každé město navštívit právě jednou a pak se vrátit do města, ze kterého vycestoval. Naším úkolem je při znalosti ceny cesty mezi jednotlivými městy najít co nejlevnější způsob (pořadí měst), jak tuto cestu provést.

Nejdůležitější při použití genetického algoritmu bude problém, jak cestu zakódovat a jak provést křížení a mutaci. Ukažme si opět několik možností, jak lze úlohu zakódovat.
 

Sousedská reprezentace (Adjacency Representation)

Města jsou očíslovaná. Cestu budeme kódovat do vektorů délky rovné počtu měst. Číslice na j-tém místě vektoru udává, kam se má vydat obchodník z j-tého města. Cestu přitom vždy začínáme z města číslo 1. Čili například pro 9 měst reprezentuje vektor (2, 4, 8, 3, 9, 7, 1, 5, 6) reprezentuje následující cestu 1-2-4-3-8-5-9-6-7. Ne každý vektor ovšem reprezentuje cestu přes všechna města. Například cesta (3, 1, 2, 4, 5, 6, 7, 8, 9) skončí poměrně brzy. Tyto nepříjemné cykly by se vytvářeli při klasickém použití křížení. Proto je nutné nadefinovat jiné křížení, které obsahuje opravný algoritmus. Možnosti jsou takovéto: Výhoda sousedské reprezentace spočívá v tom, že nám umožňuje standartní zkoumání pomocí teorie o schematech. Například schema (***3*7***) určuje všechny cesty obsahující hrany (4, 3) a (6, 7).
Tento způsob kódování se nejeví jako nejlepší. Jak křížení střídáním hran tak křížení střídáním podcest má za následek poničení dobrých schemat.

Ordinální reprezentace (Ordinal representation)

Reprezentace podobná jako u algoritmu Ad[i]. Vytvoříme uspořádáný seznam měst. Mějme seznam 6 měst (4, 6, 3, 2, 5, 1) pak vektor (2, 5, 1, 3, 2, 1) bude reprezentovat cestu 6-1-4-5-2-3-6 (konstrukce viz algoritmy Ad[i] v problému batohu). Výhodou této reprezentace je, že funguje běžné křížení a mutace.
Ani tato reprezentace však v TSP nedává dobré výsledky.
 

Reprezentace cestou (Path representation)

Pravděpodobně nejpřirozenější způsob reprezentace. Nastává zde otázka, jakým způsobem má být definováno křížení, protože klasický způsob křížení zde nefunguje.
Ukažme si některé z možností: V algoritmech používajících Path Representation se používají některé další operátory, které jej vylepší. Algoritmy tuto reprezentaci používající bývají ve vyhledávání řešení TSP vcelku úspěšné.



Všechny informace uvedené na této stránce jsem čerpal z knihy Genetic Algorithms + Data Structures = Evolution Programs od Zbygniewa Michalewicze vydané v roce 1994 nakladatelstvím Springer - Verlag. V této knize najdete mnoho dalších zajímavých informací. 
Budete-li mít k tomuto textu jakoukoli připomínku či bude něco nejasného, napište mi