Az LPA* vagy Lifelong Planning A* egy heurisztikus keresési algoritmus, amely az A*-on alapul. Először Sven Koenig és Maxim Likhachev írta le 2001-ben.[1]

Leírás szerkesztés

Az LPA* az A* egy növekményes változata, amely alkalmazkodni tud a grafikon változásaihoz anélkül, hogy a teljes grafikont újra kiszámítaná, frissíti a g-értékeket (a kezdetektől való távolságot) az előző keresésből az aktuális keresés során, hogy szükség esetén helyesbítse azokat. Az A*-hoz hasonlóan az LPA* heurisztikát is használ, amely az adott csomóponttól a célig vezető út költségeinek alsó határa. A heurisztika elfogadható, ha garantáltan nem negatív (nulla megengedett), és soha nem haladja meg a cél elérésének legolcsóbb útjának költségét.

Elődök és utódok szerkesztés

A kezdő és a cél csomópont kivételével minden n csomópont rendelkezik elődökkel és utódokkal:

  • Bármely csomópont, amelybe él vezet n egy elődje.
  • Bármelyik csomópont, amelyből él vezet az n utódja.

A következő leírásban ez a két kifejezés csak a közvetlen elődökre és utódokra utal, nem pedig az elődök elődeire vagy az utódok utódaira.

A távolság becslései szerkesztés

Az LPA* a g*(n) kezdési távolság két becslését készíti el minden csomópontnál:

  • g(n), az előzőleg kiszámított g-érték (kezdeti távolság) mint az A* algoritmusban
  • rhs(n), a előrejelzett érték amelyek a csomópont elődjeinek g-értékén alapul (a g(n' ) + d(n' , n) minimuma, ahol n' az n elődje és d(x, y) az x és y csomópontokat összekötő él költsége)

A kezdő csomópont esetében a következő mindig igaz:

 

Ha rhs(n) megegyezik g(n)-nel akkor n-t lokálisan konzisztensnek nevezzük. Ha az összes csomópont lokálisan konzisztens, akkor a legrövidebb utat meg lehet határozni, mint az A*-nál. Ha azonban az élköltségek megváltoznak, a lokális konzisztenciát csak az útvonal szempontjából releváns csomópontokban kell kiszámítani.

Prioritási sor szerkesztés

Amikor egy csomópont lokálisan inkonzisztenssé válik (mivel elődjének költsége vagy az elődjének összekötő élének költsége megváltozott), akkor az algoritmus prioritási sorba helyezi az újraértékeléshez. Az LPA* kétdimenziós kulcsot használ:

 

A bejegyzések először k1 (amely közvetlenül megfelel az A*-ban használt f-értékeknek), majd k2 szerint vannak rendezve.

Csomópont bővítés szerkesztés

A sor felső csomópontja az alábbiak szerint bővül:

  • Ha egy csomópont rhs-értéke megegyezik g-értékével, akkor a csomópont lokálisan konzisztens, és eltávolításra kerül a sorból.
  • Ha egy csomópont rhs-értéke kisebb, mint g-értéke (helyileg túlkonzisztens csomópontnak nevezik), akkor a g-értéket úgy módosítja, hogy megfeleljen az rhs-értéknek, így a csomópont lokálisan konzisztens lesz. Ezután a csomópontot eltávolítják a sorból.
  • Ha egy csomópont rhs-értéke nagyobb, mint g-értéke (helyileg aligkonzisztens csomópontként nevezik), akkor a g-értéket végtelenre állítják (ami a csomópontot lokálisan túlkonzisztensnek vagy lokálisan konzisztensnek tekinti). Ha a csomópont lokálisan konzisztens, akkor azt eltávolítják a sorból, a kulcs megújul.

Mivel egy csomópont g-értékének megváltoztatása megváltoztathatja utódjainak rhs-értékeit (és így lokális konzisztenciájukat), ezeket kiértékeljük, és a sor sorrendjük és kulcsuk szükség esetén frissül.

A csomópontok bővítése a sor tetején lévő következő csomóponttal folytatódik, amíg két feltétel teljesül:

  • A cél lokálisan konzisztens, és
  • A prioritási sor tetején levő csomópontnak olyan kulcs van, amely nagyobb vagy egyenlő a cél kulcsával.

Kezdeti futás szerkesztés

A grafikon inicializálása úgy történik, hogy a kezdő csomópont rhs-értékét 0-ra, g-értékét végtelenre állítja. Az összes többi csomópontnál mind a g-értéket, mind az rhs-értéket végtelennek tekintik, amíg azok nem változnak meg. Ez kezdetben a kezdő csomópontot az egyetlen lokálisan inkonzisztens csomóponttá, és ezáltal a sor egyetlen csomópontjává teszi. Ezután megkezdődik a csomópont-bővítés. Az LPA* első futtatása tehát ugyanúgy viselkedik, mint az A*, azaz ugyanazon csomópontok kibővítése történik ugyanabban a sorrendben.

Költségváltozások szerkesztés

Amikor egy él költsége megváltozik, az LPA* megvizsgálja a változás által érintett összes csomópontot, azaz azokat a csomópontokat, amelyeknél az egyik megváltozott él lezárul (ha egy él mindkét irányban áthaladhat, és a változás mindkét irányba hat, az él mindkét végét megvizsgáljuk):

  • A csomópontok rhs-értékei frissülnek.
  • Azok a csomópontok, amelyek lokálisan konzisztensek lettek, eltávolításra kerülnek a sorból.
  • Azok a csomópontok, amelyek lokálisan inkonzisztenssé váltak, hozzáadódnak a sorhoz.
  • Azok a csomópontok, amelyek lokálisan inkonzisztensek maradnak, a kulcsokat frissítik.

Ezt követően a csomópont kibővítése folytatódik, amíg a végső feltételt elérik.

A legrövidebb út megkeresése szerkesztés

Amint a csomópont kibővítése befejeződött (azaz a kilépési feltételek teljesülnek), a legrövidebb utat kiértékelik. Ha a cél költsége megegyezik a végtelenséggel, akkor nincs elegendő költségút az indulástól a célig. Egyébként a legrövidebb utat visszafelé haladással lehet meghatározni:

  • Kezdje a céltól.
  • Lépjen az aktuális n csomópont n' elődjére, ahol a g(n' ) + d(n' , n) a legalacsonyabb (ha a legalacsonyabb pontszámot több csomópont is eléri, akkor mindegyik érvényes megoldás, és ezek közül bármelyiket lehet választani).
  • Ismételje meg az előző lépést, amíg el nem éri a startot.

Pszeudokód szerkesztés

Ez a kód feltételezi a queue prioritási sort, amely támogatja a következő műveleteket:

  • topKey() visszaadja a sorban lévő bármely csomópont (numerikusan) legalacsonyabb prioritását (vagy végtelennek, ha a sor üres)
  • pop() eltávolítja a sorból a legalacsonyabb prioritású csomópontot, és visszatér
  • insert(node, priority) beszúr egy adott prioritással rendelkező csomópontot a sorba
  • remove(node) eltávolítja a csomópontot a sorból
  • contains(node) igaz értéket ad, ha a sor a megadott csomópontot tartalmazza; hamist, ha nem
void main() {
  initialize();
  while (true) {
    computeShortestPath();
    while (!hasCostChanges())
      sleep;
    for (edge : getChangedEdges()) {
        edge.setCost(getNewCost(edge));
        updateNode(edge.endNode);
    }
  }
}

void initialize() {
  queue = new PriorityQueue();
  for (node : getAllNodes()) {
    node.g = INFINITY;
    node.rhs = INFINITY;
  }
  start.rhs = 0;
  queue.insert(start, calculateKey(start));
}

/** Expands the nodes in the priority queue. */
void computeShortestPath() {
  while ((queue.getTopKey() < calculateKey(goal)) || (goal.rhs != goal.g)) {
    node = queue.pop();
    if (node.g > node.rhs) {
      node.g = node.rhs;
      for (successor : node.getSuccessors())
        updateNode(successor);
    } else {
      node.g = INFINITY;
      updateNode(node);
      for (successor : node.getSuccessors())
        updateNode(successor);
    }
  }
}

/** Recalculates rhs for a node and removes it from the queue.
 * If the node has become locally inconsistent, it is (re-)inserted into the queue with its new key. */
void updateNode(node) {
  if (node != start) {
    node.rhs = INFINITY;
    for (predecessor: node.getPredecessors())
      node.rhs = min(node.rhs, predecessor.g + predecessor.getCostTo(node));
    if (queue.contains(node))
      queue.remove(node);
    if (node.g != node.rhs)
      queue.insert(node, calculateKey(node));
  }
}

int[] calculateKey(node) {
  return {min(node.g, node.rhs) + node.getHeuristic(goal), min(node.g, node.rhs)};
}

[2]

Tulajdonságok szerkesztés

Mivel algoritmikusan hasonló az A*-hoz, az LPA* számos tulajdonságát megosztja.

  • Minden csomópontot az LPA* futtatásakor legfeljebb kétszer kibontunk (látogatunk meg). A lokálisan túlkonzisztens csomópontok LPA* futtatásonként legfeljebb egyszer kibővülnek, így az első futtatás (amelyben minden csomópont túlkonzisztens állapotba lép) hasonló teljesítményű, mint az A*, amely minden csomópontot egyszerre meglátogat.
  • Az egyes futtatásokhoz kibővített csomópontok kulcsai monoton módon nem csökkennek az idő múlásával, mint az A* esetében.
  • Minél informáltabb (és így nagyobb) a heurisztika (miközben továbbra is teljesíti az elfogadhatósági kritériumokat), annál kevesebb csomópontot kell kibővíteni.
  • A prioritási sor végrehajtása jelentős hatást gyakorol a teljesítményre, mint az A* esetében. A Fibonacci-halom használata jelentős teljesítménynövekedést eredményezhet a kevésbé hatékony megvalósításokhoz képest.

Az A* megvalósítás esetén, amely megszakítja a két csomópont közötti egyenlő f-értékek közötti kapcsolatokat a kisebb g-értékű csomópont javára (amelyet az A* nem határoz meg pontosan), a következő állítások is igazak:

  • A lokálisan túlkonzisztens csomópontok kibővítésének sorrendje megegyezik az A*-gal.
  • Az összes lokálisan túlkonzisztens csomópont közül csak azokat - amelyek költsége nem haladja meg a célt - kell kibővíteni, mint az A* esetében.

Az LPA* ezen felül a következő tulajdonságokkal rendelkezik:

  • Amikor az élköltségek megváltoznak, az LPA* felülmúlja az A*-ot (feltételezve, hogy az utóbbi a nulláról indul), mivel csak a csomópontok töredékét kell kibővíteni.

Változatok szerkesztés

Fordítás szerkesztés

  • Ez a szócikk részben vagy egészben a Lifelong Planning A* című angol Wikipédia-szócikk ezen változatának fordításán alapul. Az eredeti cikk szerkesztőit annak laptörténete sorolja fel. Ez a jelzés csupán a megfogalmazás eredetét és a szerzői jogokat jelzi, nem szolgál a cikkben szereplő információk forrásmegjelöléseként.

Irodalom szerkesztés

  1. Koenig, Sven & Likhachev, Maxim (2001), Incremental A*, <https://papers.nips.cc/paper/2003-incremental-a.pdf>
  2. Koenig, Sven & Likhachev, Maxim (2002), D* Lite, <http://idm-lab.org/bib/abstracts/papers/aaai02b.pdf>
  3. S. Koenig and M. Likhachev. Fast Replanning for Navigation in Unknown Terrain[halott link]. Transactions on Robotics, 21, (3), 354-363, 2005.