Download presentation
Presentation is loading. Please wait.
1
فصل سوم: برنامه نویسی پویا
2
برنامه نویسی پویا، از این لحاظ که نمونه به نمونه های کوچکتر تقسیم می شود ، مشابه روش تقسیم و حل است ولی در این روش ، نخست نمونه های کوچک تر را حل می کنیم ، نتایج را ذخیره می کنیم و بعدا هر گاه به یکی از آن ها نیاز پیدا شد، به جای محاسبه دوباره کافی است آن را بازیابی کنیم.
3
مراحل بسط یک الگوریتم برنامه نویسی پویا به شرح زیر است:
1- ارائه یک ویژگی بازگشتی برای حل نمونه ای از مسئله . 2- حل نمونه ای از مسئله به شیوه جزء به کل با حل نمونه های کوچک تر.
4
ضريب دوجمله اي با استفاده از تقسيم و حل
فرمول مرسوم بدست آوردن ضريب دو جمله اي براي كليه مقادير n ≥ k ≥0 بصورت روبرو است: اما همانطور كه مشاهده مي كنيد، بدليل استفاده از عمل فاكتوريل ، حتي براي مقادير كوچك متغيرهاي k و n ، مقدار k! و يا n! بزرگ (و يا حتي بسيار بزرگ) خواهد بود. پس ما نمي توانيم ضريب دوجمله اي را مستقيماً از اين روش بدست آوريم.
5
ضريب دوجمله اي با استفاده از تقسيم و حل
با استفاده از فرمول زير نياز به محاسبه ي n! و يا k! را به (n-1)! و يا (k-1)! تقليل مي دهيم. 0 < k < n K = 0 يا k = n همانطور كه ملاحظه مي كنيد، براي محاسبه ي اين عبارت، آن را به دو عبارت كوچكتر تبديل مي كنيم و به محاسبه ي آنها مي پردازيم. پس همانطور كه ملاحظه مي كنيد به الگوريتم تقسيم و حل مي رسيم.
6
ضريب دوجمله اي با استفاده از تقسيم و حل
الگوريتم محاسبه ضريب دو جمله اي در اين روش با استفاده از متد بازگشتي بصورت زير مي باشد: int bin (int n, int k) { if (k == 0 || n == k) return 1; else return bin(n-1, k-1) + bin(n-1, k); } ورودي ها: اعداد صحيح و مثبت n و k هستند كه در آن k ≤ n است. خروجي ها: ضريب دو جمله اي n و k بصورت يك عدد صحيح.
7
ضريب دوجمله اي با استفاده از تقسيم و حل
حال با استفاده از يك مثال به توضيح اين الگوريتم مي پردازيم. فرض كنيد قرار است با استفاده از اين الگوريتم ضريب دو جمله اي (2 ،4) را محاسبه كنيم. پس n = 4 و k = 2 خواهد بود، بنابراين تابع ما بصورت زير فراخواني خواهد شد: bin (4, 2) (بدليل استفاده از فراخواني هاي بازگشتي و درك موضوع، درخت اين سلسله فراخواني ها را ترسيم مي كنيم.)
8
ضريب دوجمله اي با استفاده از تقسيم و حل
bin (4, 2) 6 bin (3,1) bin (3, 2) 3 3 bin (2, 0) bin (2, 1) bin (2, 1) bin (2, 2) 1 2 2 1 1 bin (1, 0) bin (1, 1) bin (1, 0) bin (1, 1) 1 1 1 1 1 1 1 1 1
9
ضريب دوجمله اي با استفاده از تقسيم و حل
bin (4, 2) 6 bin (3,1) bin (3, 2) 3 3 bin (2, 0) bin (2, 1) bin (2, 1) bin (2, 2) 1 2 2 1 1 bin (1, 0) bin (1, 1) bin (1, 0) bin (1, 1) 1 1 1 1 1 1 1 1 1 اما همانطور كه مشاهده مي كنيد، در فراخواني هاي بازگشتي، تعدادي از نمونه ها چندين بار حل مي شوند و بطور جداگانه محاسبه مي شوند.
10
ضريب دوجمله اي با استفاده از تقسيم و حل
هماننده آنچه مشاهده كرديد براي محاسبه bin(n-1, k-1) و bin(n-1, k) هر دو نياز به نتيجه ي bin(n-2, k-1) دارند و اين مورد در هر باز فراخواني به طور جداگانه اي محاسبه مي شود. پس در روش تقسيم و حل تا زماني كه نمونه به چند نمونه كوچكتر تقسيم شود كه آن نمونه ها تقريباً به بزرگي نمونه اوليه باشند، اين الگوريتم كارايي ندارد، آنچنانكه براي تعيين نياز به محاسبه ي جمله خواهد بود. حال با استفاده از الگوريتم پويا، الگوريتمي با كارايي بيشتر و بالاتر طراحي ميكنيم.
11
ضريب دوجمله اي با استفاده از برنامه نويسي پويا
با استفاده از همان فرمولي كه در الگوريتم قبلي بيان كرديم: 0 < k < n K = 0 يا k = n اين بار با استفاده از برنامه نويسي پويا پياده سازي مي كنيم. با آرايه اي دو بعدي مثلاً با نام B كه B[i][j] مبين است. 0 < j < i j = i يا j = 0
12
ضريب دوجمله اي با استفاده از برنامه نويسي پويا
مسئله را به شيوه ي جزء به كل حل كرده و به پيش مي رويم. سلولهاي آرايه را به ترتيب از سطر و ستونهاي كوچك شروع كرده و به جلو ميرويم، تا آنجا كه به B[n][k] كه همان است برسيم.
13
الگوریتم 2-3: ضریب دو جمله ای با استفاده از برنامه نویسی پویا
int bin2 ( int n , int k ) { index i , j ; int B [0..n][0..k] for ( i = 0; i ≤ n ; i ++) if ( j == 0 || j == i ) B [i] [j] = 1; else B [i] [j] = B [ i – 1 ] [ j -1 ] + B [ i -1 ] [j]; return B[n] [k]: }
14
ضريب دوجمله اي با استفاده از برنامه نويسي پويا
حال با ذكر يك مثال به توضيح اين الگوريتم نيز مي پردازيم. فرض كنيد بخواهيم ضريب دوجمله اي (4, 2) را اين بار با استفاده از اين الگوريتم بدست آوريم. همانطور كه مي دانيد: n = 4 و k = 2 B k n 1 2 3 4 1 B[0][0] = 1 B[1][0] = 1 B[4][2] = B[3][1]+B[3][2] = = 6 B[2][1] = B[1][0]+B[1][1] = = 2 B[3][0] = 1 B[2][2] = 1 B[2][0] = 1 B[1][1] = 1 B[3][1] = B[2][0]+B[2][1] = = 3 B[3][2] = B[2][1]+B[2][2] = = 3 B[4][0] = 1 B[4][1] = B[3][0]+B[3][1] = = 4 1 1 1 2 1 1 3 3 1 4 6
15
ضريب دوجمله اي با استفاده از برنامه نويسي پويا
همانطور كه مشاهده كرديد با استفاده از آرايه اي دو بعدي و الگوريتم برنامه نويسي پويا به حل مسئله پرداختيم. ديديم كه در اين روش برخلاف روش قبلي سربار اضافي نداشتيم (محاسبه ي مجدد موردهاي محاسبه شده) چرا كه اين الگوريتم جزء به كل بوده و براي محاسبه مقادير جديد مورد نياز، آنها را از مقادير قبلي بدست مي آورد. (نه آنكه مجدداً به محاسبه آنها از نو بپردازد) در نهايت به مقايسه ي كارايي دو الگوريتم مي پردازيم.
16
مقايسه الگوريتم هاي ضريب دوجمله اي
همانطور كه قبلاً بيان شد، در الگوريتم تقسيم و حل، تعداد جملاتي كه الگوريتم براي تعيين محاسبه مي كند است. اين در حالي است كه تعداد كل گذرها در الگوريتم برنامه نويسي پويا با اثبات زير برابر خواهد بود. تعداد گذرها از حلقه j : k+(k+1)+(k+1)+...+(k+1) پس: n-k+1 بار
17
الگوریتم فلوید نمونه سوال: یک مشکل متداول در سفرهای هوایی , هنگامی که پرواز مستقیم وجود نداشته باشد , تعیین کوتاه ترین مسیرپرواز از شهری به شهردیگر است. کوتاهترین مسیر بین جفت نودهای گراف
18
تعاریف حلقه: مسیری از خود راس به خودش
مسیر ساده:اگرمسیری هیچگاه از دو راس نگذرد را مسیر ساده گویند. طول مسیر: در یک گراف وزن دارحاصل جمع وزن ها را گویند و در یک گراف بدون وزن تعداد رئوس موجود مسئله کو تاه ترین مسیر که یک مسئله بهینه سازی می باشد شامل موارد زیر است: از یک نود به نود دیگر از یک نود به تمامی نودها از تمامی نودها به همدیگر
19
روش محاسبه تمامی مسیرها
فرض کنید از هر راس به همه رئوس دیگر یک یال وجود دارد در این صورت , زیر مجموعه ای از همه مسیر ها , عبارت از مجموعه ای خواهد بود که راس نخست شروع می شود و به راسی دیگر ختم می شود واز همه رئوس دیگر عبور می کنند. چون راس دوم در چنین مسیری می تواند هر یک از n-2 راس باشد, راس سوم در چنین مسیری می تواند هر یک ازn-3 راس باشد, ... وراس دومی به آخری روی چنین مسیری فقط می تواند یک راس باشد, تعداد کل مسیرها از یک راس که از همه رئوس دیگر بگذرد (n-2)(n-3)…1=(n-2)!
20
برنامه نویسی پویا و کوتاهترین مسیر
با استفاده از برنامه نویسی پویا, یک الگوریتم زمانی درجه سوم برای این مسئله ایجاد می کنیم.
21
الگوریتم 3-3: الگوریتم فلوید برای یافتن کوتاه ترین مسیر
void floyd ( int n const number W [][], number D [][], { index i , j , k ; D = W ; for ( k = 1 ; k ≤ n ; k ++) for ( i = 1; i ≤ n ; i++) for ( j = 1 ; j ≤ n ; j ++) D [i] [j] = minimum ( D [i][j], D[i][k] + D[k][j]); }
22
T (n) = n × n × n = n³ Є θ (n³) عمل اصلی: دستورهای موجود در حلقه for .
تحلیل پیچیدگی زمانی در بدترین حالت برای ا لگوریتم3-3 (الگوریتم فلوید برای یافتن کوتاهترین مسیر) عمل اصلی: دستورهای موجود در حلقه for . اندازه ورودی:n ، تعداد رئوس گراف. T (n) = n × n × n = n³ Є θ (n³)
23
الگوریتم 4-3:الگوریتم فلوید برای یافتن کوتاهترین مسیر 2
void floyd2 ( int n, const number W [][], number D [][], index P [][]) { index i , j , k ; for ( i = 1 ; i ≤ n ; i ++) for ( j = 1 ; j ≤ n ; j++)
24
P [i] [j] = 0; D = W; for ( k = 1 ; k <= n ; k ++) for ( i = 1 ; i <= n ; i ++) for ( j = 1 ; j <= n ; j ++) if ( D [i] [k] + D [k] [j] < D [i] [j] ) { P [i] [j] = k; D [i] [j] = D [i] [k] + D [k] [j]; }
25
الگوریتم 5-3:چاپ کوتاهترین مسیر
مسئله: چاپ رئوس واسطه روی کوتاه ترین مسیر از راسی به راس دیگر در یک گراف موزون. void path ( index q , r) { if (P [q] [r] != 0 ) { path (q , P [q] [r] ); cout << “v” << P [q] [r]; path (P [q] [r] , r ); }
26
3-3 برنامه نویسی پویا و مسائل بهینه سازی
حل بهینه ، سومین مرحله از بسط یک الگوریتم برنامه نویسی پویا برای مسائل بهینه سازی است. مراحل بسط چنین الگوریتمی به شرح زیر است:
27
1- ارائه یک ویژگی بازگشتی که حل بهینه ی نمونه ای از مسئله را به دست می دهد.
2- محاسبه مقدار حل بهینه به شیوه ی جزء به کل. 3- بنا کردن یک حل نمونه به شیوه ی جزء به کل.
28
هر مسئله بهینه سازی را نمی توان با استفاده از برنامه نویسی پویا حل کرد چرا که باید اصل بهینگی در مسئله صدق کند. اصل بهینگی در یک مسئله صدق می کند اگریک حل بهینه برای نمونه ای از مسئله ، همواره حاوی حل بهینه برای همه ی زیر نمونه ها باشد.
29
ضرب زنجیره ای ماتریس ها ضرب دو ماتريس الگوريتم تحليل پيچيدگي زماني
30
ضرب دو ماتريس 2 1 3 4 5 6 * 7 8 9 4* 3 براي ضرب دو ماتريس فوق بايد هر سطر از ماتريس اول در ستونهاي ماتريس دوم ضرب شود و حاصل هر يك با هم جمع شود. 6*3+2*2+7*1 7*3+3*2+8*1 8*3+4*2+9*1 9*3+5*2+1*1 6*6+2*5+7*4 7*6+3*5+8*4 8*6+4*5+9*4 9*6+5*5+1*4 بنابراين ماتريس حاصلضرب يك ماتريس 4*2 است.
31
تعداد عمل ضرب بنابراين بطور كلي براي ضرب يك ماتريسي كه داراي I سطر و K ستون است در يك ماتريس با K سطر و J ستون تعداد اعمال ضرب به اين صورت است: A i*k * Bk*j I * K * J
32
پرانتزگذاری متفاوت ضرب زنجیره ای ماتریسها
ضرب ماتريسها مانند ضرب اعداد در رياضي داراي خاصيت شركت پذيري است. اگر تعدادي ماتريس داشته باشيم كه بين آنها علامت * است مي توان با استفاده از خاصيت شركت پذيري به شيوه هاي مختلف آنها را در هم ضرب كرد. براي مثال : مي خواهيم 4 ماتريس زير را در هم ضرب كنيم و سپس تعداد اعمال ضرب را در هر حالت به دست آوريم : A * B * C * D 20* * * *8 A (B (CD) تعداد اعمال ضرب: 30*8*12 + 2*8* *8*2 = 3680 (AB)(CD) تعداد اعمال ضرب: 20*30*2 + 30*8* *8*2 = 8880 A ( (BC) D) تعداد اعمال ضرب: 2*12*30 + 2*8* *8*2 = 1232 ((AB)C)D تعداد اعمال ضرب: 20*30*2 + 20*12* *8*12 = 10320 (A(BC))D تعداد اعمال ضرب: 2*12* *12*2 + 20*8*12 = 3120
33
بررسی تمام حالات هدف: الگوريتمي داشته باشيم كه براي Nماتريس ترتيب بهينه را پيدا كند همانطور كه ملاحظه شد ترتيب بهينه فقط به ابعاد ماتريسها بستگي دارد. بنابراين ورودي الگوريتم N (تعداد ماتريسها) وابعاد ماتريسها D است. معايب اين الگوريتم: اين الگوريتم غير هوشمند است زيرا حداقل بصورت نمايي خواهد بود. توضيح:فرض كنيد Tnتعداد ترتيبهاي متفاوت براي ضرب N ماتريس A1,A2,A3,…,An زير مجموعه اي از ترتيب ها A1(A2 A3…An) و (A1A2 A3…)An و .... Tn>=Tn-1 + Tn-1 T2=1
34
برنامه نویسی پویا-مثال
6 ماتريس زير را ميخواهيم در هم ضرب كنيم: A1 * A2 * A3 * A4 * A5 * A6 5* * * * * *8 D= [ ] براي ضرب A4 تا A6 دو ترتيب زير را داريم: 1: (A4 A5) A6 = D3*D4*D5 + D3*D5*D6= 4*6*7+4*7*8 = 392 2: A4 (A5 A6) = D4*D5*D6 + D3*D4*D6= 6*7*8+4*6*8 = 528 M[4][6] = Min(392,528) = 392
35
ضرب زنجیره ای مجموعه ماتريسهاي زير را در نظر بگيريد: A1*A2*…*Ai*…*Aj*…*An منظور از M[i][j] حداقل تعداد ضرب براي ماتريسهاي iام تا jام است بشرطی که i<j يك ماتريس بالا مثلثي است M[i][i]=0 M[i][i+1]=di-1*di*di+1
36
مثال 2 3 4 5 6 7 8 d0 d1 d2 d3 d d d6 d 1 2 3 4 5 6 30 M[2][3]=?
37
مثال در اين صورت دو ترتيب براي ضرب وجود خواهد داشت: 1: (A1 A2)(A3)
همانطور كه در قبل اشاره كرديم حداقل تعداد ضرب دو ماتريس را با M[1][3] بيان مي كنيم. 1: (A1*A2) * (A3) = A1 * A2 * A3 M[1][2] + M[3][3]+ d0*d1*d2 2: (A1) * (A2*A3) = A1 * A2 * A3 M[1][1] + M[2][3] + d0*d1*d2
38
مثال حال در واقع داريم : 1: M[1][2] + M[3][3] + d0*d2*d3 = *3*4 =94 2: M[1][1] + M[2][3] + d0*d1*d3 = *2*4 =64 بايد بين دوترتيب فوق مينيمم بگيريم . مقدار مينيمم در M[1][3] قرار ميگيرد. M[1][3]= 64 در اينصورت براي بدست آوردن درايه M[i][j]از فرمول زير استفاده ميكنيم : M[i][j] = Minimum (M[i][k] + M[k+1][j] + di-1 dk dj) به شرطي كه : و داريم: i<j , i<=k<=j-1 M[i][j]=0
39
مثال 2 3 4 5 6 7 8 d0 d1 d2 d3 d d d6 d 1 2 3 4 5 6 30 64 132 226 348 24 72 156 268 198 366 168 392
40
الگوریتم 6-3: حداقل ضرب ها
int minimult ( int n,const int d [],index P [][] ){ index i , j , k , diagonal; int M [1..n] [1..n]; for ( i = 1 ; i ≤ n ; i ++) M [i] [i] = 0:
41
for (diagonal = 1; diagonal ≤ n -1 ; diagonal ++)
for ( i = 1 ; i ≤ n – diagonal ; i ++) { j = i + diagonal ; M[i][j] = minimum (M[i][k] + M[k +1 ][j] + d [ i – 1] * d [k] * d [j]); i<=k<j P [i][j] = a value of k that gave the minimum; } return M[1][n];
42
تحليل پيچيدگي زماني n-1 [(n-diagonal) * diagonal] diagonal=1
n(n-1)(n+1) 6 N ^ 3
43
تحلیل پیچیدگی زمانی حالت معمول برای ا لگوریتم6-3( حداقل ضرب ها)
تحلیل پیچیدگی زمانی حالت معمول برای ا لگوریتم6-3( حداقل ضرب ها) عمل اصلی: می توان دستورات اجرا شده برای هر مقدار k را عمل اصلی در نظر بگیریم. مقایسه ای را که برای آزمون حداقل انجام می شود، به عنوان عمل اصلی در نظر می گیریم. اندازه ورودی: n ، تعداد ماتریس ها که باید ضرب شوند. N (n -1) (n + 1) / 6 Є θ (n³)
44
الگوریتم 7-3: چاپ ترتیب بهینه
مسئله: چاپ ترتیب بهینه برای ضرب n ماتریس. void order ( index i, index j) { if ( i == j) cout << “A” << i ; else { k = P [i] [j]; cout << “(“; order ( i , k); order ( k +1 , j ); cout << “)”; }
45
5-3 درخت های جست و جوی دودویی بهینه
درخت جست و جوی دودویی یک دودویی از عناصر( که معمولا کلید نامیده می شوند)است که از یک مجموعه مرتب حاصل می شود، به قسمی که: 1- هر گره حاوی یک کلید است.
46
2- کلید های موجود در زیردرخت چپ یک گره مفروض، کوچک تر یا مساوی کلید آن گره هستند.
3- کلیدهای موجود درزیردرخت راست یک گره مفروض، بزرگ تر یا مساوی کلید آن گره هستند.
47
الگوریتم 8-3: درخت جست و جوی دودویی
void search ( node _ pointer tree , keytype keyin, node _ poiner & p) { bool found ; p = tree; found = false; while (! found)
48
if ( p - > key == keyin ) found = true ; else if ( keyin < p - > key ) p = p -> left ; else p = p - > right ; }
49
الگوریتم 9-3 : درخت جست و جوی بهینه
مسئله: تعیین یک درخت جست وجوی بهینه برای مجموعه ای از کلید ها، هر یک با احتمالی مشخص. void optsearchtree ( int n , const float p[]; float & minavg, index R[][]) { index i , j , k , diagonal ;
50
float A [1..n + 1] [0..n]; for ( i = 1 ; i ≤ n ; i ++) { A [i] [i -1] = 0 A [i] [i] = p [i]; R [i] [i] = i ; R [i] [ i – 1 ] = 0; } A[ n + 1 ] [n] = 0 ; R[ n + 1 ] [n] = 0 ;
51
for (diagonal = 1;diagonal ≤ n – 1; diagonal++)
for ( i = 1; i ≤ n – diagonal ; i ++) { j = i + diagonal ; A[i][j] = minimum(A[i][k-1]+A[k+1][j])+∑pm R[i][j] = a value of k that gave the minimum; } minavg = A [1][n];
52
تحلیل پیچیدگی زمانی حالت معمول برای ا لگوریتم درخت جستجوی دودویی بهینه
عمل اصلی: دستورات اجرا شده به ازای هر مقدار از k .این دستورات شامل یک مقایسه برای آزمون حداقل است. اندازه ورودی: n ، تعداد کلید. T(n) = n ( n -1) ( n + 4 ) / 6 Є θ ( n³ )
53
الگوریتم 10 -3: ساخت درخت جست و جوی دودویی بهینه
node _ pointer tree ( index i , j ) { index k ; node _ pointer p ; k = R [i] [j]; if ( k == 0 ) return NULL; else {
54
p = new nodetype ; p - > key = key [k] ; p - > left = tree (i , k – 1); p - > right = tree ( k+1 , j); return P; }
55
الگوریتم11-3:الگوریتم برنامه نویسی پویا برای مسئله فروشنده دوره گرد
void tarvel ( int , n const number W[ ][ ] , index P[ ] [ ] , number & minlength) { index i , j , k ; number D[1..n][subset of V - { v1 } ] ; for ( i = 2 ; i ≤ n ; i ++)
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.