סיבוכיות זמן

פונקציות הנפוצות ב[[ניתוח אלגוריתמים המציגות את מספר הפעולות הנדרשות לפונקציה לעומת גודל הקלט]]
בתורת החישוביות, סיבוכיות זמן של אלגוריתם היא הערכה, באמצעות חסמים, על מספר הפעולות שמבצע האלגוריתם כפונקציה של גודל הקלט. 

אין בוחנים את זמן הריצה ביחידות זמן (כגון שניות), משום שמשך הזמן לביצוע פעולה תלוי במודל החישובי ובמחשב שעליו רץ האלגוריתם. למשל, ייתכן שבמודל או בארכיטקטורה מסוימת ניתן לחלק מספר אחד בחברו בצעד אחד, ואילו במודל או ארכיטקטורה אחרת יידרשו לאותה פעולה מספר צעדים. משום כך, בניתוח הסיבוכיות של אלגוריתם מקובל להביא בחשבון רק את סדרי הגודל, ולהתעלם מקבועים. למשל, אלגוריתם המבצע <math>8n+112</math> פעולות על קלט בגודל <math>n</math> הוא בעל "זמן ריצה ליניארי".

הסימון הרווח לזמן הריצה של אלגוריתמים הוא:
{|
| <math>\Theta(f(n))</math>
| זמן ריצה פרופורציוני, עד כדי קבוע, לפונקציה <math>f(n)</math>.
|-
| <math>O(f(n))</math>
| זמן ריצה שאינו עולה על הפונקציה <math>f(n)</math>, עד כדי קבוע.
|-
| <math>\Omega(f(n))</math>
| זמן ריצה שאינו פוחת מהפונקציה <math>f(n)</math>, עד כדי קבוע.
|}

הגדרות פורמליות יותר למושגים אלו ניתן למצוא בקורס בויקיספר.

==סדרי גודל נפוצים==
===זמן ריצה לוגריתמי===
אלגוריתם בעל זמן ריצה לוגריתמי מסתיים תוך זמן פרופורציוני ללוגריתם של גודל הקלט. אין צורך לציין את בסיס הלוגריתם, משום שהחלפת בסיס שקולה לכפל בקבוע, ולכן אם זמן הריצה פרופורציוני לגודל הקלט לפי בסיס לוגריתם כלשהו, הוא פרופורציוני לגודל הקלט גם לפי כל בסיס לוגריתם אחר.

לדוגמה, חיפוש ברשימה ממוינת (בשיטת החצייה) מתבצע בזמן ריצה לוגריתמי. תפקידו של האינדקס בבסיס נתונים הוא לאפשר זמן ריצה לוגריתמי בחיפושים, כאשר ללא אינדקס זמן הריצה הוא ליניארי.

===זמן ריצה ליניארי===
נאמר על אלגוריתם שזמן ריצתו ליניארי אם זמן הריצה שלו חסום על ידי פונקציה ליניארית בגודל הקלט, או – באופן שקול – פרופורציוני לאורך הקלט.

לדוגמה, אלגוריתם המחפש את המספר הגדול ביותר ברשימת מספרים נתונה על ידי מעבר סדרתי על הרשימה הוא בעל זמן ריצה ליניארי, שכן אם גודל הקלט הוא <math>n</math> האלגוריתם יבצע <math>n</math> צעדים.

מאידך, אלגוריתם מיון הממיין רשימת מספרים על ידי בחירת המספר הגדול ביותר והסרתו מהרשימה, בחירת השני בגודלו והסרתו, וכן הלאה, אינו בעל זמן ריצה ליניארי. זאת משום שאם גודל הקלט הוא <math>n</math>, האלגוריתם מבצע <math>n</math> שלבים, ובכל שלב מבצע <math>n-1</math> צעדים (מעבר על כל המספרים שנותרו) ולכן זמן הריצה יהיה ריבועי (פולינום ממעלה שנייה).

זמן ריצה ליניארי הוא בפרט פולינומי ועל כן נחשב חישוב יעיל.

===זמן ריצה פולינומי===
לאלגוריתם נתון ישנו זמן ריצה פולינומי אם קיים פולינום <math>P(n)</math> כך שזמן הריצה של האלגוריתם עבור קלטים בגודל <math>n</math> חסום על ידי ערך הפולינום עבור <math>n</math>. אלגוריתמים אשר זמן ריצתם אינו חסום על ידי פולינום, כגון אלגוריתמים בעלי זמן ריצה מעריכי, נקראים לעיתים "סופר-פולינומיים".

מקובל לקשר זמן ריצה פולינומי עם חישוב יעיל, כך שאלגוריתם ייחשב יעיל אם זמן הריצה שלו פולינומי. מעשית, לא כל זמן ריצה פולינומי מעיד על יעילות, שכן קיימים פולינומים בעלי חזקות גבוהות – בעת פיתוח אלגוריתם נעדיף זמן ריצה פולינומי מסדר נמוך, כגון זמן ליניארי או ריבועי.

===זמן ריצה מעריכי (אקספוננטי)===
סיבוכיות זמן הריצה של אלגוריתם היא מעריכית אם ורק אם פונקציית זמן הריצה שלו חסומה על ידי פונקציה מעריכית (<math>k^n</math>) כפול קבוע, כאשר <math>k>1</math>.

על פי רוב, אלגוריתמים בעלי זמן ריצה מעריכי אינם נחשבים ליעילים, וזאת משום שהפונקציה המעריכית "גדלה מהר מאוד" ביחס לקלט; לכן, מועדף השימוש באלגוריתמים מזמני ריצה טובים יותר. דוגמה לאלגוריתם שכזה: אלגוריתם לפתרון בעיית מגדלי האנוי.

===זמן ריצה תת-מעריכי===
סיבוכיות זמן ריצה תת-מעריכית או תת-אקספוננטית, מוערכת על ידי <math>L_n[\alpha,c]</math>, כאשר:
:<math>L_n[\alpha,c]=\exp\bigl(c\log(n)^{\alpha}\log(\log(n))^{1-\alpha}\bigr)</math>
<math>\alpha</math> הוא משקל החלק הלוגריתמי במעריך, בעוד שהמשלים ל-<math>\alpha</math> הוא משקל החלק הלוג-לוגריתמי, הקטן יותר. לכל ערך של <math>\alpha</math>, הפונקציה מייצגת סיבוכיות גדולה יותר ככל ש-<math>c</math> גדול יותר. לשם השוואה, <math>L_n[1,c]=n^c</math> היא סיבוכיות מעריכית, בעוד ש-<math>L_n[0,c]=\log(n)^c</math> היא סיבוכיות פולינומית. על סולם זה, קל להעריך את מידת היעילות של האלגוריתם מתוך התבוננות בערך <math>\alpha</math> למשל <math>L_n[0.5,c]</math> נמצאת ב"אמצע הדרך" בין סיבוכיות מעריכית לפולינומית.

===זמן ריצה עצרתי===
מונח זה מתאר אלגוריתמים שזמן הריצה שלהם חסום על ידי פונקציית עצרת. בדומה לאלגוריתמים בעלי זמן ריצה מעריכי, אלגוריתם שפועל בזמן ריצה עצרתי נחשב ללא-יעיל במיוחד כאשר מדובר בקלטים גדולים. האלגוריתם המדויק הידוע לפתרון בעיית הסוכן הנוסע נחשב לאלגוריתם בעל זמן ריצה שכזה.

==זמן הריצה של פתרון בעיית הפירוק לגורמים==
בעיית הפירוק לגורמים של מספר שלם נחשבה במשך שנים לבעיה מעריכית והיא עמדה על סיבוכיות משוערת מסדר גודל <math>L_n[0.5,c]</math> והושגו רק שיפורים מינוריים בערכו של הקבוע <math>c</math>. שיפור משמעותי הושג ב-1988, כאשר המתמטיקאים ג'ון פולארד, מארק מאנאס, הנדריק לנסטרה וארג'ן לנסטרה פרסמו את שיטת נפת שדה מספרים המיוחדת לפירוק מספרים בעלי מבנה מיוחד כמספרי פרמה ומספרי קנינגהם. לשיטה זו סיבוכיות מהצורה <math>L_n[1/3,c]</math> כשהקבוע <math>c=(32/9)^{1/3}\approx1.526</math>. כמספר שנים לאחר מכן, פותח האלגוריתם הכללי – "נפת שדה המספרים הכללית" המתאים לפירוק כל מספר שלם ויעילותו מוערכת ב-<math>L_n[1/3,c]</math>, כשהקבוע <math>c=(64/9)^{1/3}\approx1.923</math>. בכל המקרים נוסחאות הסיבוכיות מנומקות, מוסכמות על כל המומחים, מגובות בתוצאות אמפיריות – אך אינן מוכחות.

מומחים הראו שעל ידי אלגוריתם קוונטי המבוצע במחשב קוונטי, כגון אלגוריתם שור, ניתן להפחית את זמן הריצה של בעיית הפירוק לגורמים לזמן ריצה של <math>O(\log(n)^3)</math> הנמוך משמעותית.

==ראו גם==
*סיבוכיות מקום
*סימון אסימפטוטי
*בסיס גרייבר

 

קטגוריה:סיבוכיות
קטגוריה:מונחים בתוכנה