مسیریابی (الگوریتم)مسیر یابی یک الگوریتم برای برنامههای کامپیوتری است که هدف آن یافتن (غالبا) کوتاهترین مسیر بین دو نقطه است. مسیر یابی یک راه کاربردی برای حل هزارتوها است. مسیر یابی به مقدار زیادی به مسئلهٔ کوتاهترین مسیر در نظریهٔ گرافها ارتباط دارد؛ که در واقع این مسئله به این موضوع میپردازد که چگونه سریعترین، ارزانترین (از لحاظ تعداد راسها) و کوتاهترین مسیر را بین دو نقطه در یک شبکهٔ بزرگ بیابیم.[۱] تاریخچهردیابی تاریخچه مسئله مسیریابی و کوتاهترین مسیر دشوار است. میتوان تصور کرد که حتی در جوامع بسیار بدوی (حتی حیوانات) نیز این کار بسیار ضروری بودهاست (به عنوان مثال برای یافتن غذا). تحقیقات ریاضی دربارهٔ این مسئله نسبت به مسئلههای معروف دیگر دیر شروع شد. از اولین الگوریتمهایی که برای این مسئله ارائه شد میتوان به الگوریتم جستجوی اول سطح اشاره کرد.[۲] با توجه به پیشرفت کامپیوترها و ظهور مسائل جدید، این مسئله به تدریج از اواسط قرن بیستم مورد توجه قرار گرفت و با ایجاد شبکهٔ جهانی اینترنت، بیش از پیش به آن پرداخته شد. بازیهای کامپیوتری و نقشههای آنلاین، امروزه از پیشرفتهترین الگوریتمهای مسیریابی استفاده میکنند. تعریف مسئلهمسیریابی عبارت است از پیدا کردن یک مسیر بین دو نقطه از فضا که ممکن است مابین آنها موانعی وجود داشته باشد. مسئلهٔ مسیریابی به دو زیرمسالهٔ زیر قابل تقسیم است:[۳]
در مرحلهٔ اول سعی بر این است که فضای پیوسته به فضایی گسسته نگاشته شود. به این ترتیب یک گراف به وجود میآید که در آن (مجموعهٔ رئوس گراف) نمایندهٔ نقاطی از فضای پیوسته و (مجموعهٔ یالهای گراف) نمایندهٔ وجود یا عدم وجود ارتباط بدون واسطه بین دو نقطه از فضای پیوستهاست. این گراف در مرحلهٔ دوم به عنوان ورودی به یک الگوریتم مسیریابی داده میشود. خروجی این الگوریتم یک آرایهی مرتب از راسها است که برای رسیدن به راس هدف باید آنها را به ترتیب و بدون واسطه طی کرد. الگوریتمهای مسیریابی معمولاً علاوه بر این که میخواهند یک مسیر پیدا کنند، در پی مسیری هستند که بر اساس معیار خاصی (مثلا طول مسیر) بهینه باشد.[۴] الگوریتممتد مسیریابی با شروع از یک راس و جستجو در راسهای مجاور آن تا زمان رسیدن به راس مقصد، یک گراف را جستجو میکند؛ که معمولاً هدف آن یافتن سریعترین مسیر است. در حقیقت هدف الگوریتم مسیر یابی عموماً یافتن مسیری با کمترین تعداد راس استفاده شده (در گرافهای فاقد وزن)، یا کمترین جمع اوزان مشاهده شده (در گرافهای وزندار) است. به عنوان مثال میتوان گفت که الگوریتم مد نظر، همانند شخصی است که قصد دارد از نقطهای به نقطهٔ دیگری برسد؛ به جای این که این شخص تمام مسیرهای موجود را بررسی کند، در یک مسیر حرکت میکند و فقط زمانی از مسیر خود منحرف میشود که مانعی بر سر راه وی وجود داشته باشد. دو نکته مهم در مسیر یابی که باید به آن توجه شود عبارتاند از:
الگوریتمهای پایه ای مانند جستجوی اول سطح و جستجوی اول عمق مشکل اول را با بررسی تمام حالتهای موجود بررسی میکنند. به این صورت که با گرفتن یک راس تمام مسیرهای موجود را جستجو میکنند تا به راس هدف برسند. از لحاظ مرتبه زمانی چنین الگوریتمی در زمان طول میکشد که تعداد راسها و تعداد یالها است. اما مسئلهٔ پیچیدهتر یافتن مسیر بهینه است. راهحل کلی در این موارد استفاده از الگوریتم بلمن-فورد است که پیچیدگی زمانی آن میباشد. الگوریتمهایی مانند Dijkstra و به صورت استراتژیک مسیرها را خرد میکنند. الگوریتم جستجوی اول عمق![]() مقالهٔ اصلی: الگوریتم جستجوی اول عمق الگوریتم جستجوی اول عمق یک روش ساده و در عین حال کارا برای مسیریابی است که در سال ۱۸۸۲ توسط ریاضیدان فرانسوی، چارلز پیر ترما به منظور حل هزارتوها پیشنهاد شد.[۵] این الگوریتم بر پایهٔ روش پسگرد استوار است.[۶][۷] در این روش یکی از همسایههای گره شروع به صورت دلخواه انتخاب میشود. اگر این گره همسایهای داشته باشد که تا آن لحظه بازدید نشده باشد، الگوریتم به آن همسایه میرود (اگر چند همسایه با این شرایط وجود داشته باشد یکی به دلخواه انتخاب میشود). این روند آنقدر ادامه پیدا میکند تا یکی از دو اتفاق زیر بیفتد:
الگوریتم جستجوی اول عمق با وجود سادگی، پایهٔ بسیاری از الگوریتمهای مهم در نظریهٔ گراف است. این الگوریتم را میتوان به صورت تابع بازگشتی زیر در پایتون پیادهسازی کرد: def depth_first_search(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
for next in graph[start] - visited:
depth_first_search(graph, next, visited)
return visited
الگوریتم جستجوی اول سطحمقالهٔ اصلی: الگوریتم جستجوی اول سطح ![]() این روش نخستین بار در سال ۱۹۵۹ توسط ادوارد مور برای یافتن کوتاهترین مسیر در یک هزارتو معرفی شد.[۹] با استفاده از این الگوریتم میتوان کوتاهترین مسیر بین دو راس از یک گراف غیر وزن دار را پیدا کرد. در این روش که با استفاده از داده ساختار صف پیادهسازی میشود، قسمتی از گراف به عنوان جلودار (frontier) در نظر گرفته میشود. این قسمت از نقطهٔ شروع آغاز به گسترش مینماید و بسته به کاربرد میتواند تا دربرگرفتن تمام گراف یا تا حدی که نقطهٔ پایان مسیر را شامل شود گسترش یابد. در صورت پیمایش تمام گراف، میتوان مسیر بهینه را از نقطهٔ شروع تا هر نقطهٔ دلخواه دیگر را پیدا کرد. این الگوریتم از یک حلقهٔ تکرار با دو گام زیر تشکیل میشود و تا خالی شدن جلودار ادامه مییابد:
با اجرای این حلقه مولفهٔ همبند گراف که شامل راس شروع است پیمایش میشود. برای ذخیرهٔ مسیر از راس شروع تا هر راس دلخواه، باید حین اضافه کردن همسایهها به جلودار، در جدولی نام راسی که همسایهها از آن منشأ گرفتهاند را نوشت. به این ترتیب پس از رسیدن به راس دلخواه (پایانی) میتوان به صورت بازگشتی مسیری به سمت راس شروع پیدا کرد. شبه کد الگوریتم به قرار زیر است. frontier = Queue()
frontier.put(start )
visited = {}
visited[start] = True
while not frontier.empty():
current = frontier.get()
for next in graph.neighbors(current):
if next not in visited:
frontier.put(next)
visited[next] = True
الگوریتم Dijkstraمقالهٔ اصلی: الگوریتم دایکسترا الگوریتم Dijkstra برای یافتن کوتاهترین مسیر بین گرهها در یک گراف مورد استفاده قرار میگیرد. این الگوریتم به عنوان مثال، در شبکههای جادهای کاربرد زیادی دارد.[۱۰] این الگوریتم توسط ادسخر دایکسترا در سال ۱۹۵۶ پیشنهاد شد.[۱۱] این الگوریتم انواع مختلفی دارد. الگوریتم اصلی دایکسترا کوتاهترین مسیر را بین دو گره معین پیدا میکرد،[۱۲] اما یک نوع رایجتر این الگوریتم یک گره منفرد را به عنوان گره "مبدأ" مشخص میکند و کوتاهترین مسیر را از مبدأ مشخص شده تا همهٔ گرههای دیگر در گراف پیدا میکند که به آن "درخت کوتاهترین مسیر (Shortest path tree)" گفته میشود. برای یک مبدأ مشخص شده در گراف، این الگوریتم کوتاهترین مسیر را بین آن گره و سایر نقاط پیدا میکند.[۱۳] همچنین از این الگوریتم میتوان برای یافتن کوتاهترین مسیر بین دو گره مشخص استفاده کرد، به این صورت که الگوریتم از گره مبدأ آغاز میشود و در زمانی که به گره مقصد رسید متوقف خواهد شد. این الگوریتم کوتاهترین مسیر به شکل گسترده در پروتکلهای مسیریابی در شبکهها کامپیوتری، بهطور مثال در IS-IS یا در OSPF مورد استفاده است. الگوریتم دایکسترا از برچسبهایی استفاده میکند که اعداد صحیح مثبت یا اعداد حقیقی هستند و کاملاً مرتب شدهاند. اما میتوان آن را به اعدادی که به صورت جزئی (محلی) مرتب هستند نیز تعمیم داد. به این عمومی سازی، الگوریتم کوتاهترین مسیر عمومی دایکسترا گفته میشود.[۱۴] الگوریتم دایکسترا برای ذخیرهسازی دسترسی به اطلاعات ذخیرهشده، از یک داده ساختار استفاده میکند. الگوریتم اصلی از صف اولویتدار استفاده میکند و پیچیدگی آن (جایی که تعداد گرهها است) میباشد. ایده این الگوریتم در مقالهٔ (Leyzorek و دیگران 1957) نیز ارائه شدهاست. (Fredman و Tarjan 1984) با استفاده از یک صف اولویتدار در هرم فیبوناچی پیچیدگی زمان اجرا را به (که تعداد لبهها است) رساندند. این الگوریتم به صورت مجانبی سریعترین الگوریتم یافتن کوتاهترین مسیر از یک مبدأ برای یک گراف جهت دار با وزنهای غیر منفی بدون محدودیت شناخته شدهاست. الگوریتمگرهی که الگوریتم از آن آغاز میشود گره اولیه نامیده میشود. طول مسیر گره Y را فاصلهٔ گره Y تا گره اولیه (مبدأ) قرار میدهیم. الگوریتم دایکسترا مقادیر اولیهای (معمولا صفر برای گره اولیه و بینهایت برای سایر گرهها) را به گرهها نسبت میدهد و در هر مرحله آن را بهبود میبخشد.
هنگام پیدا کردن یک مسیر، لازم نیست صبر کنیم تا گره مقصد مانند «فوق» بازدید شود: الگوریتم میتواند هنگامی که گره مقصد کمترین فاصله را بین همهٔ گرههای «بازدیدنشده» داشته باشد متوقف شود (و بنابراین این گره میتواند به عنوان گره «فعلی» بعد از گره مبدأ مورد استفاده قرار گیرد). شرح اجرا![]() فرض کنید میخواهیم کوتاهترین مسیر بین دو نقطه روی نقشه را پیدا کنیم، یکی از این دو نقطه را نقطهٔ شروع و دیگری را به عنوان مقصد در نظر میگیریم. الگوریتم دایکسترا در ابتدا فاصلهٔ هر نقطهٔ تقاطع تا مبدأ روی نقشه را با بینهایت نشانه گذاری میکند. این کار به این معنی نیست که فاصلهها بینهایت هستند، بلکه به این معنا است که این نقاط تقاطع هنوز بازدید نشدهاند (روشهایی نیز وجود دارند که تقاطعهای بازدید نشده را در ابتدا نشانه گذاری نمیکنند). اکنون در هر تکرار یکی از این نقاط تقاطع را انتخاب میکنیم. در اولین مرحله، تقاطع فعلی، نقطهٔ شروع بوده و فاصله تا آن صفر خواهد بود (پس این نقطه را با صفر نشانهگذاری میکنیم). در مراحل بعد، نقطهای که روی آن قرار داریم نزدیکترین نقطه به مبدأ خواهد بود (پیدا کردن آن نیز کاری آسان خواهد بود). از تقاطع فعلی، فاصلهٔ تمام نقاط تقاطعی که به آن بهطور مستقیم متصل هستند و بازدید نشدهاند را بهروزرسانی میکنیم. این کار را به این صورت انجام میدهیم که مجموع فاصلهٔ نقطهٔ تقاطع مورد نظر با تقاطع فعلی را با نشانهای که برای تقاطع فعلی در نظر گرفتهایم حساب میکنیم، پس از آن اگر مجموع حسابشده از نشانهٔ آن تقاطع بازدیدنشده کمتر بود آن را با مقدار جدید نشانهگذاری میکنیم. بعد از اینکه مسافتها را برای هر تقاطع همسایه بهروزرسانی کردیم، تقاطع فعلی را به عنوان بازدیدشده علامتگذاری میکنیم و یک تقاطع بازدید نشده با حداقل فاصله (از نقطهٔ شروع) -یا کمترین عدد نشانهگذاری شده روی آن- را به عنوان تقاطع فعلی انتخاب میکنیم. تقاطعهایی که به عنوان بازدیدشده هستند با کوتاهترین مسیر از نقطهٔ مبدأ تا آن برچسب خوردهاند و قابل بازنگری یا بازگشت به آن نخواهند بود. این روند یعنی به روز رسانی تقاطعهای همسایه با کمترین فاصلهٔ آنها تا مبدأ، نشانهگذاری تقاطعی که روی آن بودیم به عنوان بازدیدشده و سپس انتخاب نزدیکترین تقاطع همسایه به عنوان تقاطع بعدی را ادامه میدهیم تا زمانی که نقطهٔ مقصد مورد نظر را به عنوان بازدیدشده مشخص کنیم. هنگامی که مقصد را به عنوان بازدیدشده مشخص کردیم کوتاهترین مسیر از نقطهٔ مبدأ تا آن را تعیین کردهایم و میتوانیم مسیر خود را به عقب با دنبال کردن جهت معکوس پیکانها ردیابی کنیم. در اجرای الگوریتم، این کار معمولاً بدین صورت انجام میشود که از نقطهٔ مقصد شروع به دنبال کردن والدین آن و پس از آن والدین نقطهٔ بعدی میکنیم تا به نقطهٔ مبدأ برسیم. به همین دلیل والدین هر گره را نیز در هر مرحله باید داشته باشیم و بهروز کنیم. این الگوریتم همانطور که انتظار میرود هیچ تلاشی برای «کاوش» مستقیم به سمت نقطهٔ مقصد انجام نمیدهد. در عوض، تنها نکتهٔ مورد نظر برای تعیین نقطهٔ تقاطع بعدی، فاصله آن از نقطهٔ شروع است. این نکته نیز ممکن است یکی از نقاط ضعف الگوریتم را نشان دهد، کندی نسبی آن در برخی توپولوژیها. شبه کددر الگوریتم زیر، کد u ← vertex در Q با حداقل فاصله [u]، گره u را در مجموعه گرههای Q جست و جو میکند که حداقل فاصلهٔ آن [dist[u است. (length(u, v فاصلهٔ بین دو گره همسایه u و v را برمیگرداند. متغیر alt در خط 18 طول مسیر از گره ریشه (نقطهٔ مبدأ) تا گره همسایه v است به طوری که این مسیر از گره u عبور کند. اگر این مسیر محاسبه شده کوتاهتر از کوتاهترین مسیری باشد که برای v ثبت شدهاست، مسیر محاسبهشده در متغیر alt با مسیر قبلی جایگزین میشود. آرایه prev شامل اشارهگرها به گره بعدی است تا کوتاهترین مسیر به مبدأ قابل بازیابی باشد. 1 function Dijkstra(Graph, source):
2
3 create vertex set Q
4
5 for each vertex v in Graph:
6 dist[v] ← INFINITY
7 prev[v] ← UNDEFINED
8 add v to Q
9 dist[source] ← 0
10
11 while Q is not empty:
12 u ← vertex in Q with min dist[u]
13
14 remove u from Q
15
16 for each neighbor v of u: // only v that are still in Q
17 alt ← dist[u] + length(u, v)
18 if alt < dist[v]:
19 dist[v] ← alt
20 prev[v] ← u
21
22 return dist[], prev[] در صورتی که فقط به کوتاهترین مسیر بین گره مبدأ و مقصد علاقهمند باشیم، میتوانیم جستجو را پس از خط 15 با این شرط که u = target خاتمه دهیم. اکنون میتوانیم کوتاهترین مسیر از مبدأ به مقصد را با یک تکرار معکوس به شکل زیر بخوانیم: 1 S ← empty sequence
2 u ← target
3 if prev[u] is defined or u = source: // Do something only if the vertex is reachable
4 while u is defined: // Construct the shortest path with a stack S
5 insert u at the beginning of S // Push the vertex onto the stack
6 u ← prev[u] // Traverse from target to source حالا دنبالهٔ S لیستی از رئوسی است که یکی از کوتاهترین مسیرها از مبدأ به مقصد را تشکیل میدهند. البته اگر هیچ مسیری وجود نداشته باشد، دنباله خالی خواهد بود.[۱۶] یک مسئلهٔ کلی تر این است که همهٔ کوتاهترین مسیرها را بین مبدأ و مقصد پیدا کنیم (ممکن است چندین مسیر مختلف با طول یکسان وجود داشته باشد). در این صورت به جای این که فقط یک گره را هر بار در آرایه []prev ذخیره کنیم، تمام گرههایی که شرایط مورد نظر در الگوریتم را برآورده میسازد در آن ذخیره میکنیم. به عنوان مثال، اگر هم گره r و هم مبدأ، متصل به گره مقصد باشند و هر دوی آنها شامل کوتاهترین مسیرهای مختلف به گره مقصد باشند، هر دو را به آرایه [prev[target اضافه میکنیم. وقتی که الگوریتم به پایان رسید، []prev در واقع یک ساختار داده خواهد بود که زیرمجموعهای از گراف اصلی است به طوری که برخی از یالهای آن برداشته شدهاست. ویژگی اصلی این ساختار داده این خواهد بود که اگر الگوریتم برای همهٔ گرهها به عنوان گره مبدأ اجرا شود، آنگاه هر مسیری از آن گره به هر گره دیگر در گراف جدید، کوتاهترین مسیر بین آن گرهها در گراف اصلی خواهد بود، و تمام مسیرهایی از آن طول در گراف اصلی در گراف جدید نیز موجود است. سپس برای یافتن همه این کوتاهترین مسیرها بین دو گره داده شده، از الگوریتمی برای یافتن مسیر در گراف جدید مانند جستجوی اول عمق استفاده خواهیم کرد. الگوریتم بلمن-فوردمقالهٔ اصلی: الگوریتم بلمن-فورد مقدمهالگوریتم Bellman-Ford الگوریتمی برای پیدا کردن کوتاهترین مسیر از یک راس گراف به عنوان منبع به کلیه راسهای دیگر در یک گراف وزندار است.[۱۷] این الگوریتم در حل یک مسئله مشخص از الگوریتم دکسترا کندتر است، اما برای حل مسائل بیشتری کاربرد دارد، زیرا این قابلیت را دارد که برای گرافهایی با وزن منفی نیز استفاده شود. این الگوریتم برای اولین بار توسط آلفونسو شیمبل ارائه شدهاست، اما به نام ریچارد بلمن و لستر فورد جونیور نامگذاری شدهاست که به ترتیب در سال ۱۹۵۸ و ۱۹۵۶ این الگوریتم را منتشر کردند.[۱۸] ادوارد اف. مور نیز در سال ۱۹۵۷ همین الگوریتم را منتشر کرد و به همین دلیل گاهی اوقات الگوریتم بلمن-فورد-مور نیز نامیده میشود.[۱۷] یالهای منفی گرافها در بسیاری از مسائل کاربرد دارد، از این رو این الگوریتم میتواند از جهت کمک به حل این نوع از مسائل بسیار سودمند باشد. [۱۹] اگر یک گراف شامل یک «دور منفی» (یعنی دوری که مجموع وزن یالهای آن به یک مقدار منفی برسد) که از مبدأ قابل دسترسی باشد، کوتاهترین مسیر وجود ندارد: هر مسیری که دارای یک راس در دور منفی باشد میتواند با هر بار پیمایش دور منفی موجود در گراف به مسیر کوتاه تری تبدیل شود. در چنین حالتی، الگوریتم بلمن-فورد میتواند دور منفی را شناسایی و گزارش کند.[۲۰] [۲۱] الگوریتممانند الگوریتم دایکسترا، بلمن-فورد با استفاده از روش «ریلکس» کردن (relaxation) پیش میرود، که در آن تقریب فاصلهٔ صحیح با فواصل بهتر در طول الگوریتم جایگزین میشود تا اینکه در نهایت به بهترین جواب برسد. در هر دو الگوریتم، فاصلهٔ تقریبی مبدأ با هر راس در گراف همیشه فراتر از فاصلهٔ واقعی است و در طول اجرا با حداقل مقدار از بین مقدار قدیمی آن و مسیر تازه پیدا شده جایگزین میشود. با این حال، الگوریتم دایکسترا از صف اولویت دار برای انتخاب حریصانهی نزدیکترین راسی که هنوز پردازش نشدهاست استفاده میکند و همین روند را برای تمامی یالهای خروجی راسها انجام میدهد. در مقابل، الگوریتم بلمن-فورد به راحتی تمام یالها را «ریلکس» میکند و ریلکس کردن را بار انجام میدهد، که در آن تعداد راسهای موجود در گراف است. در هر یک از این تکرارها تعداد راسها با فاصلهٔ درست از مبدأ رشد میکند، که در نهایت منجر به این میشود که فاصله همه راسها از مبدأ بهطور صحیح محاسبه شود. این روش اجازه میدهد تا الگوریتم بلمن-فورد برای طیف گستردهتری از گرافها به عنوان ورودی اجرا شود. پیچیدگی زمانی اجرا ی الگوریتم بلمن - فورد است، که در آن و به ترتیب تعداد راسها و یالهای گراف هستند. 1function BellmanFord(list vertices, list edges, vertex source) is
2 ::distance[], predecessor[]
3
4 // This implementation takes in a graph, represented as
5 // lists of vertices and edges, and fills two arrays
6 // (distance and predecessor) about the shortest path
7 // from the source to each vertex
8 // Step 1: initialize graph
9 for each vertex v in vertices do
10 distance[v] := inf // Initialize the distance to all vertices to infinity
11 predecessor[v] := null // And having a null predecessor
12 distance[source] := 0 // The distance from the source to itself is, of course, zero
13
14
15 // Step 2: relax edges repeatedly
16
17 for i from 1 to size(vertices)−1 do //just |V|−1 repetitions; i is never referenced
18 for each edge (u, v) with weight w in edges do
19 if distance[u] + w < distance[v] then
20 distance[v] := distance[u] + w
21 predecessor[v] := u
22
23
24 // Step 3: check for negative-weight cycles
25
26 for each edge (u, v) with weight w in edges do
27 if distance[u] + w < distance[v] then
28 error "Graph contains a negative-weight cycle"
29
30
31 return distance[], predecessor[] ![]() به عبارت ساده، این الگوریتم در ابتدای کار فاصلهٔ مبدأ را برابر صفر و همه راسهای دیگر گراف را بینهایت قرار میدهد. سپس برای همه یالها، اگر با در نظر گرفتن آن یال فاصلهٔ مقصد کاهش پیدا کند، فاصله به مقدار کمتر جدید به روز میشود. در iاُمین بار تکرار الگوریتم که یالها مورد بررسی قرار میگیرند، این الگوریتم تمام مسیرهای با طول i یال (و احتمالاً برخی از مسیرهای طولانیتر از i یال) پیدا میکند. از آنجایی که طولانیترین مسیر ممکن بدون دور میتواند شامل یال باشد، یالها باید بار بررسی شوند تا مطمئن شویم که کوتاهترین مسیر برای همه راسها پیدا شدهاست. بررسی نهایی برای تمامی یالها انجام میشود و اگر فاصلهای به روز شود، مسیری به طول یال پیدا شدهاست که تنها در صورت وجود حداقل یک دور با وزن منفی در گراف میتواند رخ دهد. الگوریتم *Aمقاله اصلی: الگوریتم *A این الگوریتم در ابتدا توسط پیتر ای هارت (Peter E. Hart)، نیلز جان نیلسون (Nils John Nilsson) و برترام رافائل (Bertram Raphael) در سال ۱۹۶۸ میلادی ارائه و شرح داده شد. در واقع این الگوریتم ترکیبی از الگوریتم Dijkstra و الگوریتم جستجوی ابتدا-بهترینِ حریصانه است و در عین حال که کوتاهترین مسیر را مییابد (البته در مواردی که خانههای بسته -خانههایی که غیرقابل عبورند- وجود دارد این ویژگی تضمین شده نیست)، از هر دو سریعتر کار میکند.[۲۲] در این الگوریتم، ابتدا دو لیست تعریف میکنیم؛ لیست باز (open list) و لیست بسته (closed list). سپس نقطه مبدأ را به لیست بسته اضافه میکنیم و در هر مرحله راسهای قابل تردد همسایهٔ راس فعلی را به لیست باز اضافه میکنیم. حال باید دو تابع تعریف کنیم:[۲۳]
حال اگر تعریف کنیم: (F(node) = G(node) + H(node، آنگاه این تابع برآورد کمهزینهترین مسیر از node به مقصد است. برای محاسبهٔ G مقدار G والد (آخرین راس قبل از راس فعلی که از آن گذشتهایم) آن راس را یک واحد بیشتر میکنیم. برای حساب کردن H راههای مختلفی وجود دارد مانند روش فاصلهٔ منهتن (Manhattan distance method)، روش فاصلهٔ قطری (Diagonal distance method)، روش فاصلهٔ اقلیدسی (Euclidean distance method) و …[۲۴] این الگوریتم این گونه کار میکند که در هر مرحله به کمهزینهترین راس موجود در لیست باز میرود و هر همسایه راس فعلی مانند T سه حالت دارد:
شبه کد الگوریتم *A به صورت زیر است: function A*(start, goal)
closed = empty set
q = makequeue(path(start))
while q is not empty do
p = removefirst(q)
x = lastnode(p)
if x in closed then
end if
if x = goal then
return p
end if
add x to closed
for y := successor(x) do
enqueue(p,q,y)
end for
return Failure
end while
end function
الگوریتم نمونهحال برای درک بهتر الگوریتمهای مسیریابی میتوان به مثال زیر توجه کرد. در نقشهٔ زیر x نماد مکانهای بسته یا دیوار، s محل شروع، o نقطهٔ پایان و _ مکانهای باز نقشه را نشان میدهند. 1 2 3 4 5 6 7 8
X X X X X X X X X X
X _ _ _ X X _ X _ X 1
X _ X _ _ X _ _ _ X 2
X S X X _ _ _ X _ X 3
X _ X _ _ X _ _ _ X 4
X _ _ _ X X _ X _ X 5
X _ X _ _ X _ X _ X 6
X _ X X _ _ _ X _ X 7
X _ _ O _ X _ _ _ X 8
X X X X X X X X X X
برای شروع به یک لیست از مختصاتها نیاز است که در این مورد از یک صف استفاده میشود. این صف ابتدا به وسیلهٔ مختصات نقطهٔ نهایی مقدار دهی اولیه میشود. همچنین به هر کدام از اعضای صف یک شمارنده تخصیص داده میشود. در مثال مورد بررسی، صف با ((۳٬۸٬۰)) شروع میشود. سپس بر روی تمام اعضای صف پیمایش انجام میشود و اعمال زیر برای هر عضو انجام میشود.
این کار را تا زمانی انجام میپذیرد که نقطهٔ شروع مشاهده شود. هنگامی که به نقطه شروع برسیم مشاهده میشود که نقاطی که در صف هستند، یک مقدار شمارنده به آنها تخصیص داده شدهاست. در نقشه زیر این موضوع مشخص است. 1 2 3 4 5 6 7 8
X X X X X X X X X X
X _ _ _ X X _ X _ X 1
X _ X _ _ X _ _ _ X 2
X S X X _ _ _ X _ X 3
X 6 X 6 _ X _ _ _ X 4
X 5 6 5 X X 6 X _ X 5
X 4 X 4 3 X 5 X _ X 6
X 3 X X 2 3 4 X _ X 7
X 2 1 0 1 X 5 6 _ X 8
X X X X X X X X X X
حال از نقطهٔ S پیمایش شروع میشود و در هر مرحله از بین مختصاتهای مجاور هر نقطه، به نقطه ای با کمترین مقدار شمارنده حرکت میکنیم. مقایسهٔ الگوریتمهادر جدول زیر، الگوریتمهای شاخص مسیریابی از جهت پیچیدگی زمانی و مورد کاربرد با یکدیگر مقایسه شدهاند.
کاربردهای مسیریابی
جستارهای وابستهمنابع
Information related to مسیریابی (الگوریتم) |
Portal di Ensiklopedia Dunia