550 likes | 676 Views
Algorithmic Complexity. Algorithmic Complexity: Two Key Metrics. پيچيدگي مکاني: حداکثر مقدار حافظه مورد نياز براي انجام محاسبات مورد نظر و ارتباط آن با اندازه ورودي. پيچيدگي زماني: حداکثر تعداد محاسبات مورد نياز براي انجام محاسبات مورد نظر و ارتباط آن با اندازه ورودي.
E N D
Algorithmic Complexity:Two Key Metrics • پيچيدگي مکاني: • حداکثر مقدار حافظه مورد نياز براي انجام محاسبات مورد نظر و ارتباط آن با اندازه ورودي. • پيچيدگي زماني: • حداکثر تعداد محاسبات مورد نياز براي انجام محاسبات مورد نظر و ارتباط آن با اندازه ورودي. • ما ابتدا اين پارامترها را در مورد بازگشت بررسي مي کنيم.
Determining Time Efficiency • راه حلها: • تجربي: اضافه کردن شمارنده ها جهت اندازه گيري تعداد عمليات انجام شده • تئوري: استفاده از مدل رياضي براي مدل کردن توابع محاسباتي مورد نياز • بازگشت؟ بعضي مسائل از روابط رياضي مناسب بهره مي برند – لذا از اين مسائل شروع مي کنيم.
Recursion Time Efficiency: Recurrence Relations • محاسبات مورد نياز: سه موضوع • مقدار کار مورد نياز در تکرار فعلي • هزينه مورد نياز براي آماده کردن داده ها قبل از استفاده از بازگشت و بعد از آن • تعداد زير مسائل بازگشتي • بازگشت خطي يا بازگشت درختي • اندازه ورودي زير مسائل بازگشتي • زير مسئله بازگشتي چقدر کوچکتر است.
Recursive Binary Search • محاسبات مورد نياز در جستجوي بازگشتي • مقدار کار مورد نياز در تکرار فعلي • 1 مقايسه (مقايسه ورودي با عنصر وسطي) • 1 تنظيم ( تغيير پارامترهاي چپ يا راست) • تعداد زير مسائل بازگشتي • 1 زير مسئله (سمت چپ يا راست آرايه) • اندازه ورودي زير مسائل بازگشتي • سايز زير مسئله نصف مسئله اصلي است.
Recurrence Relation • رابطه بازگشتي عمومي: T(n) = aT(n/b) + cnk a = تعداد زير مسائل b = 1/اندازه زير مسائل f(n) = کار تکرار فعلي = constant * nk
Recurrence: Master Theorem T(n) = aT(n/b) + f (n) where f (n) ≈nk • a < bk T(n) ~ nk • a = bk T(n) ~ nk lg n • a > bk T(n) ~ nlog b a
Recurrence: Master Theorem T1(n) = 7 T1(n/7)+n a = 7, b = 7, k = 1 a ? bk 7 == 7 • nklgn=> n lgn T2(n) = 7 T2(n/2)+n2 a = 7, b = 2, k = 2 a ? bk 7 > 22 => nlogba => nlog27 => n2.81 T3(n) = 7 T3(n/3)+n2 a = 7, b = 3, k = 2 a ? bk 7 < 32 => nk => n2
Recursive Binary Search T(n) = aT(n/b) + f(n) a = number of sub problems = 1 b = 1/size of subproblems = 1/(1/2) => 2 f(n) = current iteration work = 2n0 so k = 0 Compare a to bk:1 vs 2^0 = 1 vs 1 If they are equal, computational cost is: nk log n = 1 * log n => log n [Formula can be looked up for >, <, and ==]
Space Complexity • چرا ما به پيچيدگي مکاني علاقه منديم؟ • اکثر مردم نگران پيچيدگي زماني هستند. • به طور عمومي، مسائلي که داراي پيچيدگي زماني زيادي هستند را مي شود با صبر کردن بيشتر حل نمود. ( اگر چه ممکن است به عمر ما کفاف ندهد!) • اما ممکن است نتوانيم مسائلي که داراي پيچيدگي مکاني زيادي هستند را اجرا کنيم. در اينجا، محدوديت ما کمبود حافظه است و محدوديت حافظه از محدوديت زماني مشهود تر است.
Space Complexity • به طور کلي داراي دو جزء است: • جزء ثابت: C • مقدار کد برنامه • ثابتها • متغيير هاي ساده • جزء موردي: Sp • متغيير هايي که سايز آنها به مسئله ربط دارد. • فضاي پشته مورد نياز • S(P) = C + Sp • معمولا از C در مقابل Spصرفنظر مي کنيم.
Space Complexity float abc(float a, float b, float c) { return a + b + b*c + (a+b-c) / (a+b) + 4.0; } • فرض کنيد فضاي مورد نياز براي هر float برابر 1 کلمه باشد. • بدون توجه به مقادير a، b و c فضاي مورد نياز براي اجراي اين برنامه ثابت است. • لذا پيچيدگي مکاني اين تابع مستقل از ورودي است يا Sabc = 0
Space Complexity float sum(float *a, const int n) { float s = 0; for (int i =0; i < n; i++) s += a[i]; return s; } • اين مسئله عناصر يک آرايه را با هم جمع مي کند. • سايز ورودي برابر با سايز آرايه است. • به 4 کلمه براي a, n, s, i نياز داريم. • دوباره فضاي مورد نياز براي اجراي اين برنامه مستقل از ورودي و ثابت است. لذا Ssum = 0
Space Complexity float rsum(float *a, const int n) { if (n <= 0) return 0; else return (rsum(a, n-1) + a[n-1]); } • اين مسئله عناصر يک آرايه را با هم جمع مي کند. • سايز ورودي برابر با سايز آرايه است. • به 4 کلمه براي a, n, a[n-1] و آدرس بازگشت نياز داريم. • هر بار اجراي بازگشت به چهار کلمه نياز دارد. • عمق بازگشت برابر n+1 است. • فضاي مورد نياز براي اجراي اين برنامه وابسته به ورودي است و Srsum = 4(n+1)
Time Complexity • زمان کامپايل + زمان اجرا T(P)= • زمان کامپايل : • وابسته به ورودي نيست. • در مقايسه با زمان اجرا قابل صرفنظر کردن است. • زمان اجرا: • به دقت قابل اندازه گيري نيست. • وابسته به سخت افزار، سيستم عامل ، بار کامپيوتر و ... است. • به صورت قدمهاي اجرايي مورد نياز تخمين زده مي شود.
Time Complexity in C++ Context • توضيحات: • اجرا نمي شوند. • Step count = 0 • اعلانها (int b; ): • يا اجرا نمي شوند • يا در قسمت سربار صدا زدن توابع مورد ملاحظه قرار مي گيرند. • Step count = 0
C++ Time Complexity • عبارات (for example: a == true) : • معمولا step count = 1 • اگر عبارت شامل صدا زدن يک تابع باشد. هزينه صدا زدن تابع نيز بايد لحاظ گردد. • انتساب : <variable> = <expression> • معمولا تعداد قدمها معادل عبارت سمت راست است. • اما اگر سايز متغيير سمت راست وابسته به ورودي (يا متغيير) باشد: • step count = size of variable + expression step count • مثال:در a = (2+b); اگر a و b هر دو ليست باشند: • هزینه = هز ینه ی ارزیابی عبارت (2+b) به علاوه ی هزینه ی کپی کردن لیست b در لیست a.
C++ Time Complexity • تکرار: • مي خواهيم عناصر کنترلي خود حلقه را بررسي کنيم نه دستورات درون حلقه را • While <expr> do • Do… while <expr> • For (<init_stmt>; <expr1>; <expr2>) • در while, doهر تکرار به اندازه <expr>هزينه دارد. • در for • اگر دستورات <init_stmt>, <expr1>, <expr2> به سايز ورودي وابسته نباشند هزينه هر تکرار را برابر 1 در نظر مي گيريم. • لذا کل هزينه for برابر تعداد تکرارها بعلاوه 1 (براي آخرين چک کردن ناموفق حلقه) است. • در غير اين صورت: • اولین اجرای حلقه: <init_stmt> steps + <expr1> steps • بقیه ی اجراهای حلقه: <expr1> steps + <expr2> steps
C++ Time Complexity • دستور switch : • switch <expr> { case cond1: <statement1> case cond2: <statement2> default: <statement>} • عبارت مورد ارزیابی: : <expr> steps • شرایط:هزينه چک کردن شرط فعلي بعلاوه تمام شروط قبلي • دستورIf/else : • If (<expr>) <statements1>; else <statements2> • <expr> = true: <expr> +<statements1> cost • <expr> = false: <expr>+ <statements2> cost
C++ Time Complexity • صدا زدن توابع: • معمولا step count = 1 • فرستادن آرگومانها با مقدار: اگر سايز متغيير وابسته به ورودي باشد هزينه کپي کردن آرگومانها را لحاظ کنيد. • اگر از روش بازگشتي استفاده مي کنيد، تمام متغييرهاي محلي وابسته به سايز را شناسايي و هزينه آنها را در نظر بگيريد زيرا بايد هنگام صدا زدن بازگشت توليد گردند.
C++ Time Complexity • مديريت حافظه: new/delete • معمولا step count = 1 • اگر شي ايجاد شده جزء سازنده و مخرب داشته باشد، هزينه اين جزء ها بايد همانند توابع حساب شود. • دستورات شرطي:continue/ break/ goto/ return / return<expr> : • براي continue/break/goto/return برابر 1 • براي return <expr> برابر هزينه <expr>
Measuring Complexity • راه حل اول: • برنامه را طوري تعيير دهيد که شامل عبارات مربوط step count گردد. • يک متغيير عمومي به اسم countتعريف کنيد. • هرجايي که يک عبارت اجرا مي شود، count را به طور مناسب افزايش دهيد.
Measuring Complexity float sum(float *a, const int n) { float s = 0; for (int i =0; i < n; i++) s += a[i]; return s; } float sum(float *a, const int n) { float s = 0; count++; // assignment for (int i =0; i < n; i++) { count++; s += a[i]; count++;} // 1 for for , 1 for += count ++; // last time checking for return s; count ++; // return statement }
Measuring Complexity • هر چيزي غير از عبارات مربوط به count را حذف کنيد. float sum(float *a, const int n) { count++; // assignment for (int i =0; i < n; i++) { count = count + 2;} // 1 for for, 1 for += // last time checking for and return statement count = count + 2; }
Measuring Complexity: • عبارات مربوط به count را با هم جمع کنيد. 1 at beginning + (N iterations of loop) * (2 within a loop) + 2 at end => 2N + 3
Measuring Complexity float rsum(float *a, int n) { if (n <= 0) return 0; else return (rsum(a, n-1) + a[n-1]); } float rsum(float *a, int n) { count ++; // if conditional if (n <= 0) { count++; return 0;} // return else { count++; return (rsum(a,n-1) + a[n-1]); // return statement } با بازگشت چکار کنيم؟
Measuring Complexity: Recursion float rsum(float *a, cont int n) { count ++; // if conditional if (n <= 0) { count++;} // return else { count++; return (rsum(a,n-1) + a[n-1]); } If (n <= 0) count = 2 Otherwise count = 2 + count(n-1)
Measuring Complexity: Recursion • 2 + count(n-1) رابطه بازگشتي => 2+ count(n-1) = 2 + 2 + count(n-2) = 2 + 2 + 2 + count(n – 3) … = 2 * n + count(0) => 2n + 2
Time Complexity • جمع تکراري: 2n + 3 • جمع بازگشتي: 2n + 2 • آيا جمع بازگشتي از جمع تکراري سريع تر است؟ • ضرورتا نه – هر قدم جمع بازگشتي ممکن است خيلي از قدمهاي جمع تکراري هزينه بر تر باشد. • اما ايرادي ندارد – چون ما به تعداد قدمها در مقايسه با سايز ورودي علاقه داريم که در اين مورد مثل هم هستند.
Time Complexity • 2n + 2 زمان اجرا خطي است • يعني اگر ورودي 10 برابر شود، زمان اجرا نيز 10 برابر مي شود. • اگر ورودي 10000 برابر شود، زمان اجرا نيز 10000 برابر خواهد شد. • در رابطه Xn + C هميشه Xn قسمت ثابت را تحت تاثير قرار مي دهد و ما از C صرفنظر مي کنيم.
Time Complexity • جمع ماتريسها: void add(matrix a, matrix b, matrix c, int m, int n) { for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { c[i][j] = a[i][j] + b[i][j] } } }
Time Complexity void add(matrix a, matrix b, matrix c, int m, int n) { for (int i = 0; i < m; i++) { count++; // for loop for (int j = 0; j < n; j++) { count++; // for loop c[i][j] = a[i][j] + b[i][j] count++; // assignment } count ++; // last time through for loop on j } count++; // last time through for loop on i }
Time Complexity void add(matrix a, matrix b, matrix c, int m, int n) { for (int i = 0; i < m; i++) { count = count + 2; // for loop start, last time on j [case 1] for (int j = 0; j < n; j++) { count = count + 2; } // for loop start, assignment [case 2] } } count++; // last time through for loop on i [case 3] } => 2m [case 1]+ 2mn [case 2] + 1 [case 3]
Time Complexity جمع ماتريسها: 2mn + 2m + 1 عبارت فوق چه چيزي را به ما ياد مي دهد؟ اگر m>>n است جاي حلقه ها را عوض کنيد. تا بجاي 2m از 2n عمل استفاده کنيد.
Measuring Complexity • راه حل اول: جمع count ها • راه حل دوم: متد جدولي • قدمهاي مورد نياز براي اجراي هر دستور • فرکانس دستور
Tabular Method for Complexity float sum(float *a, const int n) 1 { 2 float s = 0; • for (int i =0; i < n; i++) • s += a[i]; 5 return s; 6} • Line S/E Freq Total • 0 1 0 • 1 1 1 • 1 n+1 n+1 • 4 1 n n • 5 1 1 1 • 6 0 1 0 • Total: 2n+3
Tabular Method for complexity float rsum(float *a, const int n) 1 { 2 if (n <= 0) return 0; 3 else return (rsum(a, n-1) + a[n-1]); 4} • Line S/E Freq Total • =0 >0 =0 >0 • 0 1 1 0 0 • 1 1 1 1 1 • 2b 1 1 0 1 0 • 3 1 + tn-1 0 1 0 1+tn-1 • 4 0 1 1 0 0 • Total: n = 0 => 2 • n > 0 => 2+ tn-1
Tabular Method for Complexity void add(matrix a, matrix b, matrix c, int m, int n) { 1 for (int i = 0; i < m; i++) { 2 for (int j = 0; j < n; j++) { 3 c[i][j] = a[i][j] + b[i][j] } } } • Line S/E Freq Total • 1 m+1 m+1 • 1 m(n+1) mn+m • 1 mn mn • Total: 2mn + 2m + 1
Time Complexity • بهترين حالت: کمترين تعداد قدمهاي اجرايي مورد نياز براي پارامترهاي داده شده • بدترين حالت:بيشترين تعداد قدمهاي اجرايي مورد نياز براي پارامترهاي داده شده • حالت ميانگين: تعداد متوسط قدمهاي اجرايي مورد نياز براي پارامترهاي داده شده
Time Complexity T(A) = c1n2 + c2n T(B) = c3n فرض کنيد: c1 = 5, c2 = 3, c3 = 100 چه موقع هزينه الگوريتم A< هزينه الگوريتم B 5n2 + 3n > 100n => 5n2 > 97n => 5n > 97 => n > 19.4 19.4 is the breakpoint
Big “Oh” Notation • روابط بين توابع را تعريف مي کنيم. • مي گوييم F(n) از درجه O(g(n)) است اگر: • وجود داشته باشد c و n0 > 0 بطوري که f(n) <= cg(n) براي تمام n >= n0 • 3n + 2 از درجه O(n)است زيرا: 3n+2 <= 4n براي تمام n >= 2 (c = 4, n0 = 2)
Big “Oh” notation • 10n2 + 4n + 2 از درجه O(n2)استزيرا : 10n2 + 4n + 2 < 11n2 براي تمام n >= 5 (c = 11, n0 = 5) • 10n2 + 4n + 2 از درجه O(n) نيست زيرا: وجود ندارد c و n0 که: 10n2 + 4n + 2 is < cn براي همه n >= n0
Big “Oh” notation C = 100? 10n2+4n+2 < 100n 10n2 < 96n – 2 ~ 10n2 < 95n = 10n < 95 => n < 9.5 فقط براي n هاي بين 1 و 10 برقرار است. • C = 1000? 10n2+4n+2 < 1000n 10n2 < 996n – 2 ~ 10n2 < 995n = 10n < 995 => n < 99.5 فقط براي n هاي بين 1 و 100 برقرار است. نمي توانيم n0 پيدا کنيم که رابطه n >= n0هميشه برقرار باشد.
Big “Oh” Notation • رابطه f(n) = O(g(n)) يک حد بالا روي مقدار f(n) قرار مي دهد که لزوما بهترين حد بالا نيست. • ما دنبال کوچکترين حد بالا هستيم. F(n) = 4n + 2 از درجه O(n) و O(n2) و O(n3).. است اما ازO(n) استفاده مي کنيم.
Big “Oh” notation • O(1) = ثابت • O(n) = خطي • O(n2) = مربعي • O(n3) = مکعبي • O(log n) = لگاريتمي • O(2n) = نمايي • الگوريتمهاي با درجه پيچيدگي O(n log n)نيز مرسوم هستند.
Big “Oh” Notation • اگر تابعي داراي چند جمله باشد جمله بزرگتر لحاظ مي گردد. • f(n) = 4n5 + 6n3 + 2n2 + n + 8 => O(n5)
Omega Notation • f(n) = Ω(g(n)) اگر: • وجود داشته باشد c و n0 > 0 بطوري که f(n) >= cg(n) براي تمام n >= n0 • 3n + 2 = Ω(n) چون: 3n + 2 >= 3n براي n >= 1 (c = 3, n = 1) • 10n2 + 4n + 2 = Ω(n2) چون: 10n2 + 4n + 2 >= n2براي n >= 1 (c = 1, n = 1) همچنين Ω(n)و Ω(1) نيز هست.
Omega Notation • رابطه f(n) = Ω(g(n)) يک حد پايين روي مقدار f(n) قرار مي دهد که لزوما بهترين حد پايين ممکن نيست. • ما دنبال بزرگترين حد پايين هستيم.
Theta Notation • f(n) = Θ(g(n)) اگر: • وجود داشته باشد c1، c2و n0 > 0 بطوري که c1g(n) <= f(n) <= c2g(n) براي تمام n >= n0 • 3n + 2 از مرتبه Θ(n) است زيرا: 3n + 2 <= 4n براي تمام n >= 2 O(n) 3n + 2 >= 3n براي تمام n >= 2 Ω(n) • Θ(g(n)) يک حد بالا و پايين سفت وسخت روي f(n)مي گذارد.
cg(n) f(n) is O(g(n)) f(n) is (g(n)) f(n) f(n) cg(n) N N c1g(n) f(n) is (g(n)) f(n) c2g(n) N Graphs • دقت کنيد که: • در شروع (n < N) هر اتفاقي که مي افتد مهم نيست. • در شکل سوم، c1g(n) و c2g(n) مثل هم هستند. (چرا؟) http://www.cis.upenn.edu/~matuszek/cit594-2004/Lectures/49-analysis-2.ppt