Útmutató az OptaPlannerhez

1. Bevezetés az OptaPlannerbe

Ebben az oktatóanyagban egy OptaPlanner nevű Java kényszer-elégedettség-megoldót nézünk meg.

Az OptaPlanner minimális beállítású algoritmusok segítségével oldja meg a tervezési problémákat.

Bár az algoritmusok megértése hasznos részleteket szolgáltathat, a keretrendszer elvégzi számunkra a kemény munkát.

2. Maven-függőség

Először hozzáadunk egy Maven-függőséget az OptaPlannerhez:

 org.optaplanner optaplanner-core 7.9.0.Final 

Megtaláljuk az OptaPlanner legújabb verzióját a Maven Central adattárból.

3. Probléma / megoldás osztály

A probléma megoldásához mindenképpen szükségünk van egy konkrét példára.

Az előadások ütemezése megfelelő példa az erőforrások, például a helyiségek, az idő és a tanárok egyensúlyának megteremtése miatt.

3.1. CourseSchedule

CourseSchedule problémaváltozóink és tervezési entitásaink kombinációját tartalmazza, következésképpen ez a megoldási osztály. Ennek eredményeként a konfigurációhoz több kommentárt használunk.

Vizsgáljuk meg közelebbről mindegyiket külön:

@PlanningSolution nyilvános osztály CourseSchedule {private List roomList; private List periodList; privát Lista előadásLista; privát HardSoftScore pontszám;

A PlanningSolution az annotáció azt mondja az OptaPlanner-nek, hogy ez az osztály tartalmazza az adatokat, hogy felöleljék a megoldást.

Az OptaPlanner ezeket a minimális összetevőket várja: a tervező entitást, a problémát és a pontszámot.

3.2. Előadás

Előadás, POJO, úgy néz ki, mint:

@PlanningEntity nyilvános osztály Előadás {public Integer roomNumber; nyilvános egész periódus; nyilvános Vonós tanár; @PlanningVariable (valueRangeProviderRefs = {"availablePeriods"}) public Integer getPeriod () {return period; } @PlanningVariable (valueRangeProviderRefs = {"availableRooms"}) public Integer getRoomNumber () {return roomNumber; }}

Használunk Előadás osztály, mint tervező entitás, ezért hozzáadunk egy újabb jegyzetet a getter in-hez CourseSchedule:

@PlanningEntityCollectionProperty public list getLectureList () {return lectureList; }

Tervezési entitásunk tartalmazza a megkötéseket.

A Tervezés Változó kommentár és a valueRangeProviderRef az annotációk összekapcsolják a megszorításokat a probléma tényeivel.

Ezeket a kényszerértékeket később fogják értékelni az összes tervező entitáson.

3.3. Probléma Tények

A szoba szám és időszak a változók korlátokként működnek egymáshoz hasonlóan.

Az OptaPlanner a logika eredményeként pontozza a megoldásokat ezen változók felhasználásával. Mindkettőhöz jegyzeteket adunk getter mód:

@ValueRangeProvider (id = "availableRooms") @ProblemFactCollectionProperty public list getRoomList () {return roomList; } @ValueRangeProvider (id = "availablePeriods") @ProblemFactCollectionProperty public list getPeriodList () {return periodList; } 

Ezek a listák az összes lehetséges érték, amelyet a Előadás mezők.

Az OptaPlanner feltölti őket a keresési tér minden megoldásában.

Végül ezután meghatározza az egyes megoldások pontszámát, ezért szükségünk van egy mezőre a pontszám tárolásához:

@PlanningScore public HardSoftScore getScore () {return score; }

Pontszám nélkül az OptaPlanner nem találja meg az optimális megoldást, ezért a korábban hangsúlyozott fontosságot.

4. Pontozás

Az eddigiekkel ellentétben a pontozási osztály több egyéni kódot igényel.

A pontszámológép ugyanis a problémára és a tartománymodellre jellemző.

4.1. Egyéni Java

Egy egyszerű pontszámítással oldjuk meg ezt a problémát (bár nem tűnhet annak):

nyilvános osztály ScoreCalculator megvalósítja az EasyScoreCalculator {@Override public Score calcScore (CourseSchedule courseSchedule) {int hardScore = 0; int softScore = 0; Set occupiedRooms = new HashSet (); for (Előadás-előadás: courseSchedule.getLectureList ()) {String roomInUse = lecture.getPeriod () .toString () + ":" + lecture.getRoomNumber (). toString (); if (occupiedRooms.contains (roomInUse)) {hardScore + = -1; } else {occupiedRooms.add (roomInUse); }} return HardSoftScore.valueOf (hardScore, softScore); }}

Ha jobban megnézzük a fenti kódot, akkor a fontos részek világosabbá válnak. Pontot számolunk a ciklusban, mert a Lista a szobák és az időszakok egyedi, nem egyedi kombinációit tartalmazza.

A HashSet egy egyedi kulcs (karakterlánc) mentésére szolgál, hogy megbüntethessük az ismétlődő előadásokat ugyanabban a teremben és időszakban.

Ennek eredményeként egyedi szobakészleteket és időszakokat kapunk.

4.2. Drools

A Drools fájlok segítségével gyorsan módosíthatjuk a fájlokra vonatkozó szabályokat. Míg a szintaxis néha zavaró lehet, a Drools fájl a fordított osztályokon kívül is kezelheti a logikát.

A null bejegyzések megakadályozására vonatkozó szabályunk így néz ki:

globális HardSoftScoreHolder scoreHolder; szabály a "noNullRoomPeriod", amikor előadás (roomNumber == null); Előadás (időszak == null); akkor scoreHolder.addHardConstraintMatch (kcontext, -1); vége

5. Megoldó konfigurálása

Egy másik szükséges konfigurációs fájl, szükségünk van egy XML fájlra a megoldó konfigurálásához.

5.1. XML konfigurációs fájl

    org.baeldung.optaplanner.ScoreCalculator 10 

A jegyzetek miatt a CourseSchedule osztályban használjuk scanAnnotatedClasses elemet az osztályúton lévő fájlok beolvasásához.

A scoreDirectorFactory elem tartalma állítsa be ScoreCalculator osztály tartalmazza a pontozási logikánkat.

Ha Drools fájlt akarunk használni, akkor az elem tartalmát a következőkre cseréljük:

courseScheduleScoreRules.drl

Végső beállításunk a felmondási elem. Ahelyett, hogy végtelenül keresne egy soha nem létező optimalizált megoldást, ez a beállítás egy idő után leállítja a keresést.

Tíz másodperc több mint elég a legtöbb problémára.

6. Tesztelés

Konfiguráltuk megoldási, megoldó és problémakörünket. Teszteljük!

6.1. Tesztünk beállítása

Először elvégezünk néhány beállítást:

SolverFactory solverFactory = SolverFactory .createFromXmlResource ("courseScheduleSolverConfiguration.xml"); megoldó = solverFactory.buildSolver (); unsolvedCourseSchedule = új CourseSchedule ();

Másodszor, az adatokat feltöltjük a tervező entitás gyűjtésébe és a probléma tényébe Lista tárgyakat.

6.2. Teszt végrehajtása és ellenőrzése

Végül telefonálással teszteljük megoldani.

CourseSchedule megoldottCourseSchedule = megoldó.megoldás (megoldatlanCourseSchedule); assertNotNull (megoldottCourseSchedule.getScore ()); assertEquals (-4, megoldottCourseSchedule.getScore (). getHardScore ());

Ellenőrizzük, hogy a solutionCourseSchedule van egy pontszáma, amely azt mondja nekünk, hogy mi az „optimális” megoldás.

Bónuszként létrehozunk egy nyomtatási módszert, amely megjeleníti az optimalizált megoldást:

public void printCourseSchedule () {lectureList.stream () .map (c -> "Előadás a teremben" + c.getRoomNumber (). toString () + "időszak alatt" + c.getPeriod (). toString ()) .forEach (k -> logger.info (k)); }

Ez a módszer a következőket jeleníti meg:

Előadás az 1. teremben az 1. periódusban Előadás a 2. teremben 1. periódusban Előadás az 1. teremben 2. periódus alatt Előadás a 2. teremben 2. periódusban Előadás a 2. teremben 2. periódus alatt Előadás a 1. teremben 3. periódus alatt Előadás a 2. teremben 3. periódus alatt Előadás az 1. teremben 1. periódus alatt Előadás az 1. teremben az 1. időszakban Előadás az 1. teremben az 1. időszakban Előadás az 1. teremben az 1. periódus alatt

Figyelje meg, hogyan ismétlődik az utolsó három bejegyzés. Ez azért történik, mert nincs optimális megoldás a problémánkra. Három időszakot, két tantermet és tíz előadást választottunk.

E rögzített források miatt csak hat lehetséges előadás lehetséges. Legalábbis ez a válasz megmutatja a felhasználónak, hogy nincs elegendő szoba vagy időszak az összes előadás befogadására.

7. Extra jellemzők

Az általunk létrehozott OptaPlanner példánk egyszerű volt, azonban a keretrendszer funkciókkal bővítette a sokféle felhasználási esetet. Érdemes megvalósítanunk vagy módosítanunk az optimalizáláshoz szükséges algoritmusunkat, majd megadhatjuk a keretet annak használatához.

A Java multi-threading képességeinek közelmúltbeli fejlesztéseinek köszönhetően az OptaPlanner lehetőséget ad a fejlesztőknek arra is, hogy a multi-threading több megvalósítását, például villát és csatlakozást, inkrementális megoldást és multitenanciát alkalmazzanak.

További információ a dokumentációban található.

8. Következtetés

Az OptaPlanner keretrendszer hatékony eszközt biztosít a fejlesztőknek a kényszer-elégedettség problémáinak megoldására, mint például az ütemezés és az erőforrás-elosztás.

Az OptaPlanner minimális JVM erőforrás-felhasználást kínál, valamint integrálódik a Jakarta EE-be. A szerző továbbra is támogatja a keretrendszert, és a Red Hat hozzáadta az Business Rules Management Suite részeként.

Mint mindig, a kód megtalálható a Githubon.