واضح آرشیو وب فارسی:سایت رسیک: در علوم رایانه و ریاضیات، برنامهریزی پویا روشی کارآمد برای حل مسائل جستوجو و بهینهسازی با استفاده از دو خصیصهٔ زیرمسئلههای همپوشان و زیرساختهای بهینه است. بر خلاف برنامهریزی خطی، چارچوب استانداردی برای فرموله کردن مسائل برنامهریزی پویا وجود ندارد. در واقع، آنچه برنامهریزی پویا انجام میدهد ارائه روش برخورد کلی جهت حل این نوع مسائل است. در هر مورد، باید معادلات و روابط ریاضی مخصوصی که با شرایط آن مسئله تطبیق دارد نوشته شود.[۱
ویژگیها
برنامهریزی پویا دارای دو ویژگی اصلی است که در زیر بیان میشوند.
[ویرایش] زیرسازه بهینه
استفاده از روش زیرسازه بهینه به این معناست که مسئله را به زیرمسئلههایی کوچکتر بشکانیم و برای هر یک از این زیرمسئلهها پاسخی بهینه بیابیم و پاسخ بهینه مسئلهٔ کلی را از کنار هم قرار دادن این پاسخهای بهینهٔ جزئی به دست آوریم. مثلاً در هنگام حل مسئلهٔ یافتن کوتاهترین مسیر از یک رأس یک گراف به رأسی دیگر، میتوانیم کوتاهترین مسیر تا مقصد از همهٔ رئوس مجاور را به دست آوریم و از آن برای جواب کلی استفاده کنیم. به طور کلی حل مسئله از این روش شامل سه مرحله است.
شکاندن مسئله به بخشهای کوچکتر
حل خود این زیرمسئلهها با شکاندن آنها به صورت بازگشتی
استفاده از پاسخهای جزئی برای یافتن پاسخ کلی
[ویرایش] زیرمسئلههای همپوشان
گفته میشود مسئلهای دارای زیرمسئلههای همپوشان است اگر بتوان مسئله را به زیرمسئلههای کوچکتری شکاند که پاسخ هرکدام چند بار در طول فرایند حل مورد استفاده قرار بگیرد.[۲] برنامهریزی پویا کمک میکند تا هر کدام از این پاسخها فقط یک بار محاسبه شوند فرایند حل از بابت دوبارهکاری هزینهای را متحمل نشود. برای مثال در دنباله فیبوناچی برای محاسبهٔ عدد چهارم دنباله به دانستن عدد سوم نیاز داریم. برای محاسبهٔ عدد پنجم هم باز به عدد سوم نیاز داریم. حال اگر مثلاًَ در شرایطی بخواهیم عدد ششم دنبالهٔ فیبوناچی را حساب کنیم، در این محاسبه هم مقدار عدد پنجم را میخواهیم و هم مقدار عدد چهارم را. اگر تصمیم بگیریم اعداد چهارم و پنجم را به نوبت حساب کنیم در هنگام محاسبهٔ هرکدام به مقدار عدد سوم نیاز پیدا میکنیم و باید دوباره آن را محاسبه کنیم. برای جلوگیری از این محاسبات چندباره، معمولاً الگوریتمهایی که مبتنی بر برنامهریزی پویا هستند از یکی از دو راه زیر استفاده میکنند.
’’’رویکرد بالا به پایین’’’: در این رویکرد مسئله به زیرمسئلههایی شکانده میشود و پاسخ هر زیرمسئله پس از محاسبه در جایی ذخیره میشود. در مراحل بعدی هر وقت به آن پاسخ نیاز بود پاسخ از روی حافظه خوانده میشود. این فرآیند ترکیبی از الگوریتم بازگشتی و ذخیرهسازی در حافظه است.
’’’رویکرد پایین به بالا’’’: در این رویکرد همهٔ زیرمسئلههای مورد نیازر از کوچک به بزرگ حل میشوند و از جوابها بلافاصله برای محاسبهٔ بعدیها استفاده میشود و فرایند محاسبه تا رسیدن به زیرمسئلهٔ مورد نیاز (که در وافع مسئلهٔ اصلی ماست) ادامه مییابد. بدیهیست که در این حالت استفاده از الگوریتم بازگشتی ضروری نیست. مثال زیر این تفاوتها را روشن تر میکند.
[ویرایش] مثال
یک پیادهسازی ساده از یک تابع برای یافتن عدد فیبوناچی nام میتواند به شکل زیر باشد.
function fib(n)
if n = 0
return 0
else if n = 1
return 1
return fib(n − 1) + fib(n − 2)
برای مثال اگر از چنین تابعی (fib(5 را بخواهیم، تابعهایی که صدا میشوند به شکل زیر خواهند بود.
fib(5)
fib(4) + fib(3)
(fib(3) + fib(2)) + (fib(2) + fib(1))
((fib(2) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
(((fib(1) + fib(0)) + fib(1)) + (fib(1) + fib(0))) + ((fib(1) + fib(0)) + fib(1))
مشخص است که چنین فرایندی پر از محاسبات تکراری است.مثلاً عدد فیبوناچی دوم به تنهایی سه بار حساب شدهاست. در محاسبات بزرگتر چنین تکرارهایی برنامه را به شدت کند میکنند. این الگوریتم دارای پیچیدگی زمانی نمایی است. حال فرض کنید ما یک آرایه دوتایی map داریم که عدد n را به مقدار عدد فیبوناچی nام مربوط کرده و ذخیره میکند. پیچیدگی زمانی چنین الگوریتمی n خواهد بود. همچنین میزان فضای اشغالشدهاش هم از مرتبه n خواهد بود.
var m := map(0 → 1, 1 → 1)
function fib(n)
if map m does not contain key n
m[n] := fib(n − 1) + fib(n − 2)
return m[n]
این نمونهای از فرایند بالا به پایین بود. چون ابتدا مسئله را شکاندیم و بعد به محاسبه و ذخیرهٔ پاسخ زیرمسئلهها پرداختیم. در فرایند پایین به بالا برای حل چنین مسئلهای از عدد فیبوناچی یکم شروع میکنیم تا به عدد خواستهشده برسیم. بااین کار باز هم پیچیدگی زمانی از مرتبه n است.
function fib(n)
var previousFib := 0, currentFib := 1
if n = 0
return 0
else if n = 1
return 1
repeat n − 1 times
var newFib := previousFib + currentFib
previousFib := currentFib
currentFib := newFib
return currentFib
برتری این روش به روش قبلی در این است که در این روش حتی به فضای ذخیره از مرتبه n هم نیازی نیست. فضای ذخیره از مرتبه 1 کفایت میکند. علت این که همیشه از رویکرد پایین به بالا استفاده نمیکنیم این است که گاهی از قبل نمیدانیم باید کدام زیرمسئلهها را حل کنیم تا به مرحله اصلی برسیم، یا این که مجبوریم زیرمسئلههایی که استفاده نمیشوند را هم حل کنیم.
این صفحه را در گوگل محبوب کنید
[ارسال شده از: سایت رسیک]
[مشاهده در: www.ri3k.eu]
[تعداد بازديد از اين مطلب: 2081]