Реализация на C++ A-star, определяща дали даден възел вече е в приоритетната опашка на отворените елементи

Една стъпка в алгоритъма за намиране на път A* изисква търсене в списъка с отворени възли за възела, с който в момента взаимодействате, и добавяне на този възел към списъка, ако още не е там, или актуализиране на неговата стойност и родител, ако присъства но с по-високо тегло от текущата версия на възела.

Тези поведения не се поддържат в STL структурата priority_queue. Как трябва да приложа тази стъпка?

Актуализации, тъй като този въпрос получава много гледания:

  • std::priority_queue може да изглежда като добър избор за това, но не е.

  • Внедряването на A* сами е огромен стимул за повишаване на увереността, но след като го направите, трябва да опитате да преминете към използването на този, предоставен от boost. Бях нервен да го инсталирам, когато зададох този въпрос, но инсталирането е много лесно и няма да доведе до никакви усложнения; и A* не е единствената полезна функционалност, която осигурява boost. (По-специално, ако не използвате тяхната функционалност за обработка на низове, в крайна сметка ще напишете свое собствено копие от него; говоря от личен опит...)


person ExOttoyuhr    schedule 11.08.2012    source източник
comment
Така или иначе дори не бих го направил, освен ако пространството не е на първо място. Има ли някаква причина да не можете/не трябва да поддържате отделен набор, който поддържа бързо съдържа заявки?   -  person harold    schedule 11.08.2012
comment
Бихте ли изяснили какво не е необходимо да правите тук? @ybungalobill, по-долу, каза, че опресняването на възела до най-евтината версия е задължително за оптимални резултати.   -  person ExOttoyuhr    schedule 11.08.2012
comment
Така е, но намирането на правилния възел за промяна в опашката с приоритет е бавна операция. Можете да поддържате отделна структура от данни, за да обработвате тази част.   -  person harold    schedule 11.08.2012
comment
Виждам. Благодаря за пояснението.   -  person ExOttoyuhr    schedule 11.08.2012


Отговори (5)


Можете да използвате обикновен вектор или масив за съхраняване на елементите и след това да използвате std::make_heap, std::push_heap, std::pop_heap, std::sort_heap, std::is_heap и std::is_heap_until, за да ги управлявате.

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

person Andrew Tomazos    schedule 11.08.2012
comment
Ако използвам този подход, как да предам различна предикатна функция на make_heap()? Отказва по-голямо‹SearchNode›, казвайки, че името на типа не е разрешено. - person ExOttoyuhr; 11.08.2012
comment
Актуализация: изглежда, че просто трябваше да декларирам екземпляр на large‹SearchNode›. Моля, уведомете ме, ако греша. - person ExOttoyuhr; 11.08.2012
comment
@ExOttoyuhr: Добавих примерен код, показващ как да използвате std::greater. Предполагам, че сте забравили да поставите скоби след него, за да посочите, че искате да създадете анонимен временен. - person Andrew Tomazos; 11.08.2012

Ако сте ограничени до STL, можете да използвате STL Set и постоянно изтриване и повторно вмъкване на елементите (с нов приоритет).

Set< pair<int,int> > s; // < priority,value >
s.insert( make_pair(0,5) );

// Decrease Key operation //
s.erase( s.find( make_pair(0,5) ) );
s.insert( make_pair(1,5) );

Времевата сложност все още е O(log N), но вероятно ще отнеме повече време за големи комплекти.

person kkanellis    schedule 11.08.2012

STL priority_queue не е подходящ за A* реализация. Имате нужда от структура на купчина, която поддържа операцията increase за промяна на приоритета на вече вмъкнати елементи. Използвайте Boost.Heap за внедряване на много класически купчини .

РЕДАКТИРАНЕ: Библиотеката Boost.Graph има имплементация на A* търсене също.

person Yakov Galka    schedule 11.08.2012
comment
Правилно ли е да се каже, че ако намерите версия на конкретен възел с по-ниска обща цена от версията, която имате в момента в опашката, трябва да замените версията в опашката с новата, по-евтина версия? Звучи така, но искам да съм сигурен. - person ExOttoyuhr; 11.08.2012
comment
@ExOttoyuhr: да, иначе няма да получите оптимални решения. Вижте псевдокод на алгоритъма на wikipedia. - person Yakov Galka; 11.08.2012
comment
Благодаря. Но има ли начини да получите подходящ контейнер, без да е необходимо да инсталирате Boost? - person ExOttoyuhr; 11.08.2012
comment
@ExOttoyuhr: можете да използвате multi_set, както предложи @compileGuy, или да използвате функциите на купчина, които @Andrew изброи, и сами да внедрите increase/decrease за купчини, базирани на масиви. Тези подходи обаче ще доведат до O(log N) актуализации, докато повечето от купчините на Boost имат амортизирани O(1) актуализации. - person Yakov Galka; 11.08.2012
comment
ybungalobill: В момента намирам пътя върху карта на света 24x13, за сравнително скромен брой актьори, така че мисля, че O(log N) е поносимо; но ако установя, че интегрирам Boost в проекта в някакъв момент в бъдещето, това със сигурност е област, в която да го използвам. - person ExOttoyuhr; 11.08.2012
comment
не е приоритетна опашка от най-добрите начини за оптимизиране на A* - person Thomas; 04.01.2013
comment
@ThomasVerbeke: приоритетната опашка е най-добрата, но не и STL priority_queue, за който говорех. Проблемът на стандарта priority_queue е, че... обяснено в отговора. - person Yakov Galka; 04.01.2013
comment
comp.dit.ie/bduggan/research/ прочетете тази статия :) - person Thomas; 04.01.2013
comment
@ThomasVerbeke: Боже мой... Това е документ, написан от аматьори. Това, което всъщност има значение, не е хартията (която не обяснява нищо), а изпълнението. И проблемът на внедряването е, че причинява недефинирано поведение чрез обезсилване на priority_queue инвариантите (тоест чрез модифициране на стойностите директно, без да позволява priority_queue да се подреди предварително. вижте ред 90 на PathFinder.cpp). Алгоритъмът им просто е счупен и те са късметлии, че са получили правилни резултати (ако изобщо са проверили коректността, което се съмнявам). - person Yakov Galka; 04.01.2013
comment
съгласен съм; въпреки това все още смятам, че в този случай може да се използва STL приоритетна опашка (дори ако структурата на купчината е очевидно по-добра реализация) и това е, което се опитвах да ви покажа. - person Thomas; 04.01.2013
comment
@ThomasVerbeke: но след това (както в кода, който сте свързали) нарушавате времевата сложност на A*. - person Yakov Galka; 04.01.2013

Ето решението, което използвах за това, ако наистина искате да използвате std::priority_queue:

Когато трябва да актуализирате възел, който вече е в опашката с приоритети, просто вмъкнете нов възел със същото състояние и нова стойност на разходите и родител в опашката. Най-новото актуализирано копие на този възел първо ще излезе от опашката и ще бъде добавено към вашия посетен набор. За да се справите с по-старите дубликати, проверете всеки възел, излизащ от опашката, срещу вашия посетен набор, преди да го обработите. Ако е в посетения набор, тогава пътят с най-ниска цена през този възел вече е видян, така че просто го игнорирайте и обработете следващия възел.

person P_D    schedule 04.10.2015
comment
Това вероятно е най-простият подход и избягва излишните счетоводни разходи свързани с "променливи" приоритетни опашки. - person Rufflewind; 09.03.2017

Има три вероятни решения за това:

  1. Проследявайте списъка с текущо отворени възли, независимо от опашката с приоритети. Опитайте да създадете списък с възли по същия начин, както правите за затворени възли.

  2. Създайте карта на възлите (по координати) до отворено-затворено състояние.

  3. Инсталирайте библиотеката Boost, която включва шаблонна реализация на A* (мисля, че в <graph>).

person ExOttoyuhr    schedule 11.08.2012