Transcript O(n)

‫ניתוח זמן ריצה‬
‫(על קצה המזלג)‬
‫קרן כליף‬
‫ביחידה זו נלמד‪:‬‬
‫‪‬‬
‫מהו ניתוח זמן ריצה‬
‫‪‬‬
‫‪‬‬
‫מוטיבציה‬
‫הגדרה‬
‫‪ ‬ניתוחי זמן ריצה בסיסיים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪2‬‬
‫‪© Keren Kalif‬‬
‫קבוע‬
‫לינארי‬
‫ריבועי‬
‫לוגריתמי‬
‫משפחות של פונקציות‬
‫‪‬‬
‫לינאריות‪:‬‬
‫כאשר ‪ a‬ו‪ b -‬קבועים‬
‫‪ ‬ריבועיות‪:‬‬
‫כאשר ‪ a,b‬ו‪ c -‬קבועים‬
‫‪‬‬
‫‪f)n( = a∙n + b‬‬
‫‪f)n( = a∙n2 + b∙n + c‬‬
‫מעריכיות (אקספוננציליות)‪:‬‬
‫‪f(n) = 2n‬‬
‫‪3‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה ‪ -‬מוטיבציה‬
‫‪‬‬
‫כאשר אנחנו מריצים את התוכניות שלנו‪ ,‬נראה לנו שהן‬
‫רצות מאוד מהר‪ ,‬ואנחנו לא עוצרים לחשוב כמה עבודה‬
‫באמת מבוצעת ע"י המחשב‬
‫‪ ‬ב"עולם האמיתי" לעיתים יש חשיבות לכל מיקרו שנייה‬
‫שהתוכנית רצה‬
‫‪‬‬
‫נרצה לדעת להעריך האם התוכנית שכתבנו יעילה‪,‬‬
‫כלומר כמה עבודה המחשב באמת מבצע‬
‫‪ ‬נרצה לבחון האם יש דרך לכתוב את התוכנית בצורה‬
‫יותר יעילה‬
‫‪4‬‬
‫‪© Keren Kalif‬‬
‫זמן ריצה ‪ -‬הגדרה‬
‫‪‬‬
‫זמן ריצה של תוכנית הוא סדר גודל של מספר הפעולות‬
‫שהתוכנית מבצעת ביחס לגודל הקלט‬
‫‪‬‬
‫סדר גודל אינו מספר מדויק אלא הערכה של מספר‬
‫הפעולות המבוצעות בתוכנית‬
‫‪5‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה – זמן ריצה קבוע לעומת‬
‫זמן ריצה תלוי משתנה‬
‫‪‬‬
‫כמה זמן לוקח למחשב לחשב את‪:‬‬
‫;‪x = x + y‬‬
‫‪‬‬
‫‪‬‬
‫כמה זמן לוקח למחשב לחשב את‪:‬‬
‫)‪for (i=1 ; i <= n ; i++‬‬
‫;‪x += i‬‬
‫‪‬‬
‫‪6‬‬
‫‪© Keren Kalif‬‬
‫התשובה תלויה במהירות המחשב‪ ,‬אך הנחה סבירה היא‬
‫שפעולה זו על אותו מחשב בזמנים שונים תיקח זמן זהה‬
‫במקרה זה זמן הריצה תלוי בגודלו של ‪ n‬שאינו ידוע מראש‪,‬‬
‫ולכן צריך לקחתו בחשבון בזמן הניתוח התיאורטי‪ ,‬שכן יהיה‬
‫הבדל משמעותי אם ‪ n=10‬או ‪...n=100,000,000‬‬
‫ניתוח זמן ריצה – זמן ריצה קבוע )‪O(1‬‬
‫‪‬‬
‫כאשר מספר הפעולות של התוכנית קבוע בכל הרצה‪,‬‬
‫נאמר כי זמן הריצה הוא קבוע‪ ,‬ונסמנו ב‪O(1) -‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num‬‬
‫;“ ‪cout << “Please enter a number:‬‬
‫;‪cin >> num‬‬
‫;‪cout << “The number is ” << num << endl‬‬
‫}‬
‫‪‬‬
‫‪7‬‬
‫‪© Keren Kalif‬‬
‫בתוכנית זו מספר הפעולות תמיד יהיה זהה‪ ,‬לא משנה מה ערכו‬
‫של ‪num‬‬
‫ניתוח זמן ריצה – זמן ריצה קבוע )‪O(1‬‬
‫מציאת מי המספר המינימלי ומי המקסימלי‪:‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num1, num2, min, max‬‬
‫;“ ‪cout << “Please enter 2 numbers:‬‬
‫;‪cin >> num1 >> num2‬‬
‫;‪min = num1‬‬
‫;‪max = num2‬‬
‫)‪if (min > num2‬‬
‫{‬
‫בעת חישוב ניתוח זמן ריצה נסתכל תמיד על המקרה‬
‫;‪min = num2‬‬
‫הגרוע ביותר‪ ,‬כלומר המקרה בו יהיו הכי הרבה‬
‫;‪max = num1‬‬
‫פעולות ולכן בדוגמא זו נניח כי ה‪ if -‬מבוצע‬
‫}‬
‫}‬
‫‪‬‬
‫‪8‬‬
‫‪© Keren Kalif‬‬
‫עדיין במקרה זה מספר הפעולות קבוע בכל הרצה‪ ,‬אפילו אם‬
‫ערכם של ‪ num1‬ו‪ num2 -‬מאוד גדול‪ ,‬ולכן זמן הריצה של‬
‫תוכנית זו הוא )‪O(1‬‬
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
‫(לינארי)‬
‫חישוב עצרת‪:‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num, fact=1‬‬
‫מספר הפעולות בתוכנית זו תלוי בערכו של ‪:num‬‬
‫התוכנית תבצע תמיד את הפעולות הקבועות של‬
‫קבלת הקלט והדפסת התוצאה‪ ,‬אבל מספר‬
‫;“ ‪cout << "Enter a number:‬‬
‫הפעמים שהלולאה תרוץ תלוי בקלט‪.‬‬
‫;‪cin >> num‬‬
‫)‪for (int i=1 ; i <= num ; i++‬‬
‫;‪fact *= i‬‬
‫;‪cout << num << “! = “ << fact << endl‬‬
‫}‬
‫‪‬‬
‫‪9‬‬
‫‪© Keren Kalif‬‬
‫מספר הפעולות בתוכנית זו הוא )‪ O(1‬עבור הפעולות הקבועות ועוד )‪ O(1‬פעולות בכל‬
‫איטרציה של הלולאה‪ ,‬ומאחר ויש ‪ num‬איטרציות‪:‬‬
‫)‪O(main) = O(1) + num*O(1) = O(1) + O(num*1) = O(1) + O(num‬‬
‫כאשר ‪ num‬מאוד גדול זמן הריצה הקבוע זניח ולכן נאמר כי‪:‬‬
‫)‪O(main) = O(1) + O(num) = O(num‬‬
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
‫(לינארי) (‪)2‬‬
‫הדפסת המספרים הזוגיים עד מספר מסוים‪:‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num‬‬
‫;“ ‪cout << "Please enter a number:‬‬
‫;‪cin >> num‬‬
‫;“ ‪cout << "All even numbers from 1 to “ << num << “:‬‬
‫) ‪for (int i=0 ; i < num ; i+=2‬‬
‫; “ “ << ‪cout << i‬‬
‫;‪cout << endl‬‬
‫}‬
‫‪ ‬מספר הפעולות בתוכנית זו הוא )‪ O(1‬עבור הפעולות הקבועות ועוד )‪ O(1‬פעולות בכל‬
‫איטרציה של הלולאה‪ ,‬ומאחר ויש ‪ num/2‬איטרציות‪:‬‬
‫)‪O(main) = O(1) + (num/2)*O(1) = O(1) + O((num/2)*1) = O(1) + O(num/2) =O (num/2‬‬
‫‪ ‬כאשר ‪ num‬מאוד גדול זמן הריצה הקבוע זניח ולכן נאמר כי‪:‬‬
‫)‪O(main) = O(num/2) = O(num‬‬
‫‪10‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
)‫(ריבועי‬
void main()
{
int width, height;
:‫הדפסת מלבן‬
cout << "Enter a rectangle's width and height: ";
O(1)
cin >> width >> height;
for (int i=1 ; i <= height ; i++)
{
for (int j=1 ; j <= width ; j++)
width*O(1) = O(width)
cout << “*”;
cout << endl;
}
heigth*O(width) =
O(height*width)
}
O(height *width) :‫סה"כ זמן הריצה של התוכנית‬
width == height == n ‫אם‬
O(n2) ‫סדר גודל זמן הריצה היה‬

11
© Keren Kalif
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
‫(ריבועי)‬
‫)(‪void main‬‬
‫{‬
‫;‪int num‬‬
‫הדפסת משולש‪:‬‬
‫;" ‪cout << "Please enter the base of the triangle:‬‬
‫;‪cin >> num‬‬
‫בדוגמא זו מספר הפעולות בלולאה‬
‫הפנימית שונה בכל איטרציה‪ ,‬ולכן נבדוק‬
‫ביתר קפידה כמה פעולות באמת‬
‫מתבצעות‪ :‬באיטרציה הראשונה של‬
‫הלולאה הראשית לולאה הפנימית תרוץ‬
‫פעם אחת‪ ,‬באיטרציה השנייה פעמיים וכו'‪..‬‬
‫)‪for (int i=1 ; i <= num ; i++‬‬
‫{‬
‫)‪for (int j=1 ; j <= i ; j++‬‬
‫;”*“ << ‪cout‬‬
‫;‪cout << endl‬‬
‫}‬
‫}‬
‫‪ ‬סדר גודל זמן הריצה של התוכנית הוא מספר הפעולות בסה"כ של הלולאה הראשית‪:‬‬
‫‪ ,1+2+…+num‬וזוהי סדרה חשבונית שסכומה‪:‬‬
‫)‪O(main) = O(num*(num+1)/2) = O((num2+num)/2) = O(num2 + num)= O(num2‬‬
‫‪12‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
‫(לוגריתמי)‬
‫הדפסת חזקה‪:‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num‬‬
‫;“ ‪cout << "Please enter a number:‬‬
‫;‪cin >> num‬‬
‫;“ ‪cout << "All 2 powers till “ << num << “:‬‬
‫)‪for (int i=1 ; i <= num ; i*=2‬‬
‫;“ “ << ‪cout << i‬‬
‫;‪cout << endl‬‬
‫{‬
‫‪‬‬
‫מספר הפעולות בתוכנית זו הוא )‪ O(1‬עבור הפעולות הקבועות ועוד )‪ O(1‬פעולות בכל‬
‫איטרציה של הלולאה‪ ,‬אך מהי כמות האיטרציות של הלולאה?‬
‫‪13‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה – תלות בגודל הקלט‬
‫(לוגריתמי)‬
‫‪‬‬
‫למשל עבור ‪:n=128‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫כמות האיטרציות היא כמות הפעמים שנכפיל ב‪ 2 -‬עד שנגיע‬
‫ל‪: n -‬‬
‫‪1248163264128‬‬
‫אפשר לשאול כמה פעמים הכפלנו את ‪ 2‬בעצמו עד קבלת‬
‫‪:128‬‬
‫‪2? = 128‬‬
‫וזוהי בדיוק פעולת ה‪:log -‬‬
‫‪log2128‬‬
‫‪ ‬לכן‪ ,‬כמות הפעולות שתרוץ עבור לולאה שקצב‬
‫ההתקדמות שלה הוא כפל‪/‬חילוק הוא ‪log‬‬
‫‪14‬‬
‫‪© Keren Kalif‬‬
‫ניתוח זמן ריצה לוגריתמי‬
‫כלומר‪ ,‬ככל שבסיס הלוג יותר גדול‪,‬‬
‫כך כמות הפעולות קטנה (יותר יעיל)‬
‫)‪for (int i=1 ; i <= 240 ; i*=2‬‬
‫‪…..‬‬
‫‪ ‬כמות האיטרציות‪:‬‬
‫‪256128643216842 1‬‬
‫‪log2256 = 8‬‬
‫‪‬‬
‫‪15‬‬
‫‪© Keren Kalif‬‬
‫)‪for (int i=1 ; i <= 240 ; i*=3‬‬
‫‪…..‬‬
‫כמות האיטרציות‪24381931 :‬‬
‫‪log3243 = 5‬‬
‫השוואת סדרי גודל‬
‫‪‬‬
‫כאשר אנו כותבים תוכנית נשאף שזמן הריצה יהיה נמוך‬
‫ככל שניתן‬
‫‪ ‬נזכור כי‪:‬‬
‫)‪O(1) < O(log3n) < O(log2n) < O(n) < O(n2) < O(n3‬‬
‫‪16‬‬
‫‪© Keren Kalif‬‬
‫חיבור סדרי גודל‬
void foo(int n, int m)
{
for (int i=1 ; i <= n; i++)
cout << i;
cout << endl;
for (int i=1 ; i <= m; i++)
cout << i;
}
,m-‫ ל‬n ‫מאחר ולא ידוע לנו היחס בין‬
O(n+m) ‫נאמר שסדר הגודל הוא‬
17
© Keren Kalif
‫דוגמא ‪ :2‬קבל מספר והחזר מהי הספרה‬
‫המופיעה הכי הרבה פעמים‬
‫‪‬‬
‫אופציה ‪ :1‬לספור כמה פעמים מופיעה הספרה ‪ 1‬ע"י‬
‫לולאה על כל המספר‪ ,‬הספרה ‪ 2‬וכו'‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫אופציה ‪ :2‬מיון דליים‬
‫‪‬‬
‫‪18‬‬
‫‪© Keren Kalif‬‬
‫)‪ ,10*O(n‬כאשר ‪ n‬הוא מספר הספרות במספר‬
‫)‪10*O(n) = O(n‬‬
‫)‪ ,O(n‬כאשר ‪ n‬הוא מספר הספרות במספר‬
int countDigits1(int num)
O(n)
{
int max=0,
maxCount=0;
for (int i = 0; i < 10;
i++)
{
int temp =
num;
int count = 0;
while (temp >
0)
{
if
(temp % 10 == i)
count++;
temp /= 10;
}
if (count >
maxCount)
{
‫הקוד‬
int countDigits2(int num)
{
int counters[10];
O(n)
while (num > 0)
{
counters[num %
10]++;
num /= 10;
}
int max = 0;
for (int i = 1; i < 10; i++)
if (counters[i] >
counters[max])
max = i;
return max;
}
19
© Keren Kalif
‫ביחידה זו למדנו‪:‬‬
‫‪‬‬
‫מהו ניתוח זמן ריצה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ניתוחי זמן ריצה בסיסיים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪20‬‬
‫‪© Keren Kalif‬‬
‫מוטיבציה‬
‫הגדרה‬
‫קבוע‬
‫לינארי‬
‫ריבועי‬
‫לוגריתמי‬