Разбиране на A* евристика за лабиринт с една цел

Имам лабиринт като следния:

||||||||||||||||||||||||||||||||||||
|                                 P|
| ||||||||||||||||||||||| |||||||| |
| ||   |   |      |||||||   ||     |
| || | | | | |||| ||||||||| || |||||
| || | | | |             || ||     |
| || | | | | | ||||  |||    |||||| |
| |  | | |   |    || ||||||||      |
| || | | |||||||| ||        || |||||
| || |   ||       ||||||||| ||     |
|    |||||| |||||||      || |||||| |
||||||      |       |||| || |      |
|      |||||| ||||| |    || || |||||
| ||||||      |       ||||| ||     |
|        |||||| ||||||||||| ||  || |
||||||||||                  |||||| |
|+         ||||||||||||||||        |
||||||||||||||||||||||||||||||||||||

Целта е P да намери + с подцели на

  • Пътят до + е най-малката цена (1 скок = цена+1)
  • Броят на търсените клетки (разширени възли) е сведен до минимум

Опитвам се да разбера защо моята A* евристика се представя много по-зле от внедряване, което имам за Greedy Best First. Ето двата кода за всеки:

#Greedy Best First -- Manhattan Distance
self.heuristic = abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0])

#A* -- Manhattan Distance + Path Cost from 'startNode' to 'currentNode'
return abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0]) + self.costFromStart

И в двата алгоритъма използвам heapq, приоритизирайки въз основа на евристична стойност. Основният цикъл на търсене е един и същ и за двете:

theFrontier = []
heapq.heappush(theFrontier, (stateNode.heuristic, stateNode)) #populate frontier with 'start copy' as only available Node

#while !goal and frontier !empty
while not GOAL_STATE and theFrontier:
    stateNode = heapq.heappop(theFrontier)[1] #heappop returns tuple of (weighted-idx, data)
    CHECKED_NODES.append(stateNode.xy)
    while stateNode.moves and not GOAL_STATE:
        EXPANDED_NODES += 1
        moveDirection = heapq.heappop(stateNode.moves)[1]

        nextNode = Node()
        nextNode.setParent(stateNode)
        #this makes a call to setHeuristic
        nextNode.setLocation((stateNode.xy[0] + moveDirection[0], stateNode.xy[1] + moveDirection[1]))
        if nextNode.xy not in CHECKED_NODES and not isInFrontier(nextNode):
            if nextNode.checkGoal(): break
            nextNode.populateMoves()
            heapq.heappush(theFrontier, (nextNode.heuristic,nextNode))

Така че сега стигаме до въпроса. Докато A* намира оптималния път, това е доста скъпо. За да намери оптималния път на cost:68, той разширява (навигира и търси) 452 възела, за да направи това. a_star

Докато изпълнението на Greedy Best, което имам, намира неоптимален път (цена: 74) само в 160 разширения.

greedy_best_first

Наистина се опитвам да разбера къде греша тук. Осъзнавам, че алгоритмите на Greedy Best First могат да се държат по този начин естествено, но разликата в разширенията на възлите е толкова голяма, че чувствам, че нещо трябва да е грешно тук.. Всяка помощ ще бъде оценена. Ще се радвам да добавя подробности, ако това, което съм поставил по-горе, е неясно по някакъв начин.


person MrDuk    schedule 23.02.2015    source източник
comment
Без значение за предишните ми коментари. Това е напълно нормално поведение. Отначало мислех, че + е началото. Намирането на оптимални решения на проблеми като този е трудно; затова често не си даваме труда.   -  person user2357112 supports Monica    schedule 23.02.2015
comment
Едно нещо, което открих, което всъщност не реши разликата между двете, но подобри ефективността като цяло, беше добавянето на следното в края на главния цикъл: CHECKED_NODES.append(nextNode.xy) -- това изглежда намали моето разширение наполовина и за двата алгоритъма. ..   -  person MrDuk    schedule 23.02.2015


Отговори (3)


A* осигурява оптималния отговор на проблема, greedy best first search осигурява всяко решение.

Очаква се A* да свърши повече работа.

Ако искате вариант на A*, който вече не е оптимален, но връща решение много по-бързо, можете да погледнете претегления A*. Състои се само от поставяне на тежест на евристиката (тегло > 1). На практика това ви дава огромно увеличение на производителността

Например, можете ли да опитате това:

return 2*(abs(goalNodeXY[1] - self.xy[1]) + abs(goalNodeXY[0] - self.xy[0])) + self.costFromStart
person B. Decoster    schedule 23.02.2015

Търсенето* се опитва да намери възможно най-доброто решение на даден проблем, докато алчното най-добро първо просто се опитва да намери каквото и да е решение. A* има много, много по-трудна задача и трябва да положи много работа, за да проучи всеки един път, който евентуално би могъл да бъде най-добрият, докато алчният най-добър алгоритъм просто отива направо към опцията, която изглежда най-близо до целта .

person user2357112 supports Monica    schedule 23.02.2015

Тъй като това не е решено и въпреки че нещо нередно, зададено от OP, може да бъде разрешено с отговора на Fezvez, чувствам, че трябва да попитам това и може би да отговоря на това какво не е наред и защо отговорът на Fezvez може да се погрижи за това: проверихте ли евристичната стойност на всичките си възли с алгоритъм A* и забелязахте ли нещо странно? Не са ли всички равни? Защото въпреки че вашата евристика е правилна за най-добрия първи алгоритъм, тя не пасва директно за вашия A* алгоритъм. Направих подобен проект в java и имах този проблем, поради което питам тук. Да предположим например, че имате следните точки на интерес:

  • Начало (P) - (0,0)
  • Край(+) - (20,20)
  • P1 - (2,2) -> (Вашата евристика) + (цена на пътя) = ((20-2) + (20-2)) + ((2-0) + (2-0)) = 40
  • P2 - (4,3) -> (Вашата евристика) + (цена на пътя) = ((20-4) + (20-3)) + ((4-0) + (3-0)) = 40

И ако не греша, това ще важи за всичките ви точки в лабиринта. Сега, като се има предвид, че А* алгоритъмът обикновено се прилага точно като алгоритъм в ширина с евристика (и цена на пътища), тъй като вашата евристика винаги ви дава една и съща обща сума (F = h+g) всъщност се превръща в алгоритъм на първо място в ширината, който също ви дава възможно най-доброто решение, но на практика винаги е по-бавен, отколкото A* би направил нормално. Сега, както предложи Fezvez, даването на тежест на вашата евристика може просто да смеси най-доброто от двата свята (първо най-доброто и първо ширината) и ще изглежда така с точките, дадени по-горе:

  • Начало (P) - (0,0)
  • Край(+) - (20,20)
  • P1 - (2,2) -> 2*(Вашата евристика) + (цена на пътя) = 2*((20-2) + (20-2)) + ((2-0) + (2-0)) = 76
  • P2 - (4,3) -> 2*(Вашата евристика) + (цена на пътя) = 2*((20-4) + (20-3)) + ((4-0) + (3-0)) = 73
person pandaman1234    schedule 15.03.2016