Úvod do knižnice Jenetics

1. Úvod

Cieľom tejto série je vysvetliť myšlienku genetických algoritmov a ukázať najznámejšie implementácie.

V tomto výučbe to urobíme popísať veľmi výkonnú knižnicu Jenetics Java, ktorú je možné použiť na riešenie rôznych optimalizačných problémov.

Ak máte pocit, že sa potrebujete dozvedieť viac informácií o genetických algoritmoch, odporúčame začať týmto článkom.

2. Ako to funguje?

Podľa jej oficiálnych dokumentov je Jenetics knižnica založená na evolučnom algoritme napísanom v jazyku Java. Evolučné algoritmy majú svoje korene v biológii, pretože využívajú mechanizmy inšpirované biologickou evolúciou, ako je reprodukcia, mutácia, rekombinácia a selekcia.

Jenetics sa implementuje pomocou Java Prúd rozhranie, takže funguje dobre so zvyškom Javy Prúd API.

Hlavné vlastnosti sú:

  • bezfrikčná minimalizácia - nie je potrebné meniť alebo vylepšovať funkciu fitness; môžeme len zmeniť konfiguráciu Motor triedy a sme pripravení spustiť našu prvú aplikáciu
  • závislosť zadarmo - na použitie protokolu Jenetics nie sú potrebné žiadne runtime knižnice tretích strán
  • Java 8 pripravená Plná podpora pre Windows Prúd a výrazy lambda
  • viacvláknové - vývojové kroky je možné vykonávať paralelne

Aby sme mohli používať Jenetics, musíme do našej pridať nasledujúcu závislosť pom.xml:

 io.jenetics jenetics 3.7.0 

Najnovšiu verziu nájdete v Maven Central.

3. Používajte puzdrá

Aby sme vyskúšali všetky funkcie Jenetics, pokúsime sa vyriešiť rôzne známe optimalizačné problémy, počnúc jednoduchým binárnym algoritmom a končiac problémom Knapsack.

3.1. Jednoduchý genetický algoritmus

Predpokladajme, že musíme vyriešiť najjednoduchší binárny problém, kde musíme optimalizovať polohy 1 bitov v chromozóme pozostávajúcich z 0 a 1. Najprv musíme definovať továreň vhodnú pre daný problém:

Továreň gtf = Genotype.of (BitChromozome.of (10, 0,5));

Vytvorili sme BitChromozóm s dĺžkou 10 a pravdepodobnosť výskytu 1 v chromozóme rovná 0,5.

Teraz vytvorme prostredie vykonávania:

Engine engine = Engine.builder (SimpleGeneticAlgorithm :: eval, gtf) .build ();

The eval () metóda vráti počet bitov:

private Integer eval (Genotype gt) {return gt.getChromosome (). as (BitChromosome.class) .bitCount (); }

V poslednom kroku začneme vývoj a zhromažďujeme výsledky:

Výsledok genotypu = engine.stream () .limit (500) .collect (EvolutionResult.toBestGenotype ());

Konečný výsledok bude vyzerať podobne ako tento:

Pred evolúciou: [00000010 | 11111100] Po evolúcii: [00000000 | 11111111]

Podarilo sa nám optimalizovať polohu 1 v géne.

3.2. Problém s čiastkovou čiastkou

Ďalším prípadom použitia pre Jenetics je riešenie problému s čiastkovou čiastkou. Stručne povedané, výzvou na optimalizáciu je, že vzhľadom na množinu celých čísel musíme nájsť neprázdnu podmnožinu, ktorej súčet je nulový.

V serveri Jenetics sú preddefinované rozhrania na riešenie týchto problémov:

verejná trieda SubsetSum implementuje problém {// implementácia}

Ako vidíme, implementujeme Problém, ktorý má tri parametre:

  • - typ argumentu problémovej fitnes funkcie, v našom prípade nemenná, usporiadaná, pevná veľkosť Celé číslo postupnosť ISeq
  • - typ génu, s ktorým evolučný motor pracuje, v tomto prípade spočítateľný Celé číslo gény EnumGene
  • - výsledný typ fitnes funkcie; tu je to Celé číslo

Aby bolo možné použiť Problém musíme prepísať dve metódy:

@ Verejná funkcia override fitness () {return subset -> Math.abs (subset.stream () .mapToInt (Integer :: intValue) .sum ()); } @ Override verejný kodek codec () {return codecs.ofSubSet (basicSet, size); }

V prvej definujeme našu fitnes funkciu, zatiaľ čo druhou je trieda obsahujúca továrenské metódy vytvárania bežných kódovaní problémov, napríklad na vyhľadanie najlepšej podmnožiny pevnej veľkosti z danej základnej množiny, ako je to v našom prípade.

Teraz môžeme prejsť k hlavnej časti. Na začiatku musíme vytvoriť podmnožinu, ktorú použijeme v probléme:

SubsetSum problem = of (500, 15, new LCG64ShiftRandom (101010));

Upozorňujeme, že používame LCG64ShiftRandom generátor poskytovaný spoločnosťou Jenetics. V ďalšom kroku budujeme motor nášho riešenia:

V ďalšom kroku staviame motor nášho riešenia:

Motor engine = Engine.builder (problem) .minimizing () .maximalPhenotypeAge (5) .alterers (new PartiallyMatchedCrossover (0.4), new Mutator (0.3)) .build ();

Snažíme sa minimalizovať výsledok (optimálny bude výsledok 0) nastavením veku fenotypu a alterantov použitých na alteráciu potomstva. V ďalšom kroku môžeme získať výsledok:

Fenotyp výsledok = engine.stream () .limit (limit.bySteadyFitness (55)) .collect (EvolutionResult.toBestPhenotype ());

Upozorňujeme, že používame bySteadyFitness () ktorý vráti predikát, ktorý skráti vývojový prúd, ak po danom počte generácií nenájdete lepší fenotyp a získate najlepší výsledok. Ak budeme mať šťastie a bude tu riešenie náhodne vytvorenej množiny, uvidíme niečo podobné tomuto:

Ak budeme mať šťastie a bude tu riešenie náhodne vytvorenej množiny, uvidíme niečo podobné tomuto:

[85|-76|178|-197|91|-106|-70|-243|-41|-98|94|-213|139|238|219] --> 0

V opačnom prípade bude súčet podmnožiny iný ako 0.

3.3. Problém s prvým nasadením batohu

Knižnica Jenetics nám umožňuje vyriešiť ešte zložitejšie problémy, ako napríklad problém s batohom. Stručne povedané, v tomto probléme máme v batohu obmedzený priestor a musíme sa rozhodnúť, ktoré predmety vložíme dovnútra.

Začnime s definíciou veľkosti tašky a počtu položiek:

int nItems = 15; double ksSize = nItems * 100.0 / 3.0;

V ďalšom kroku vygenerujeme náhodné pole obsahujúce Položka batohu objekty (definované veľkosť a hodnotu polia) a tieto položky náhodne vložíme do batohu pomocou metódy First Fit:

KnapsackFF ff = nový KnapsackFF (Stream.generate (KnapsackItem :: random) .limit (nItems) .toArray (KnapsackItem [] :: new), ksSize);

Ďalej musíme vytvoriť Motor:

Engine engine = Engine.builder (ff, BitChromosome.of (nItems, 0.5)) .populationSize (500) .survivorsSelector (new TournamentSelector (5)) .offspringSelector (new RouletteWheelSelector ()). Alterters (new Mutator (0.115), new SinglePointCrossover (0,16)) .build ();

Tu je potrebné poznamenať niekoľko bodov:

  • počet obyvateľov je 500
  • potomstvo bude vybrané prostredníctvom turnaja a výberu rulety
  • ako sme to urobili v predchádzajúcej podsekcii, musíme tiež definovať alternátory pre novovytvoreného potomka

Jenetics má tiež jednu veľmi dôležitú vlastnosť. Môžeme ľahko zhromaždiť všetky štatistiky a postrehy z celého trvania simulácie. Urobíme to pomocou EvolutionStatistics trieda:

Štatistika EvolutionStatistics = EvolutionStatistics.ofNumber ();

Nakoniec spustíme simulácie:

Najlepší fenotyp = engine.stream () .limit (bySteadyFitness (7)) .limit (100) .peek (štatistika) .collect (toBestPhenotype ());

Upozorňujeme, že po každej generácii aktualizujeme štatistické údaje o hodnotení, ktoré sú obmedzené na 7 ustálených generácií a celkovo maximálne na 100 generácií. Podrobnejšie existujú dva možné scenáre:

  • dosiahneme 7 stabilných generácií, potom sa simulácia zastaví
  • nemôžeme získať 7 stabilných generácií za menej ako 100 generácií, takže simulácia sa zastaví kvôli druhej limit ()

Je dôležité mať obmedzený maximálny počet generácií, inak sa simulácie nemusia zastaviť v rozumnom čase.

Konečný výsledok obsahuje veľa informácií:

+ ------------------------------------------------- -------------------------- + | Časová štatistika + ------------------------------------------------- -------------------------- + | Výber: súčet = 0,039207931000 s; priemer = 0,003267327583 s | | Zmena: suma = 0,065145069000 s; priemer = 0,005428755750 s | | Výpočet zdatnosti: suma = 0,029678433000 s; priemer = 0,002473202750 s | | Celkové prevedenie: suma = 0,111383965000 s; priemer = 0,009281997083 s | + ------------------------------------------------- -------------------------- + | Štatistika vývoja + ------------------------------------------------- -------------------------- + | Generácie: 12 | | Zmenené: suma = 7 664; priemer = 638 6666666667 | | Zabité: suma = 0; priemer = 0,000000000 | | Neplatné: suma = 0; priemer = 0,000000000 | + ------------------------------------------------- -------------------------- + | Štatistika obyvateľstva + ------------------------------------------------- -------------------------- + | Vek: max = 10; priemer = 1,792167; var = 4,657748 | | Fitness: | | min = 0,000000000000 | | max = 716,684883338605 | | priemer = 587,012666759785 | | var = 17309,892287851708 | | std = 131,567063841418 | + ------------------------------------------------- -------------------------- +

Tentokrát sme mohli do najlepšieho scenára umiestniť položky v celkovej hodnote 716,68. Vidíme tiež podrobné štatistiky vývoja a času.

Ako testovať?

Je to pomerne jednoduchý proces - stačí otvoriť hlavný súbor súvisiaci s problémom a najskôr spustiť algoritmus. Keď už máme všeobecnú predstavu, môžeme sa začať hrať s parametrami.

4. Záver

V tomto článku sme sa venovali funkciám knižnice Jenetics na základe skutočných problémov s optimalizáciou.

Kód je k dispozícii ako projekt Maven na GitHub. Upozorňujeme, že sme poskytli príklady kódu pre ďalšie optimalizačné úlohy, ako sú napríklad problémy Springsteen Record (áno, existuje!) A Traveller Salesman.

Všetky články v sérii vrátane ďalších príkladov genetických algoritmov nájdete na nasledujúcich odkazoch:

  • Ako navrhnúť genetický algoritmus v Jave
  • Problém cestovného predajcu v Jave
  • Optimalizácia kolónie mravcov
  • Úvod do knižnice Jenetics (toto)

$config[zx-auto] not found$config[zx-overlay] not found