סיבוכיות זמן

בתורת החישוביות, סיבוכיות זמן של אלגוריתם היא הערכה, באמצעות חסמים, על מספר הפעולות שמבצע האלגוריתם כפונקציה של גודל הקלט. אין בוחנים את זמן הריצה ביחידות זמן (כגון שניות), משום שמשך הזמן לביצוע פעולה תלוי במודל החישובי ובמחשב שעליו רץ האלגוריתם. למשל, ייתכן שבמודל או בארכיטקטורה מסוימת ניתן לחלק מספר אחד בחברו בצעד אחד, ואילו במודל או ארכיטקטורה אחרת יידרשו לאותה פעולה מספר צעדים. משום כך, בניתוח הסיבוכיות של אלגוריתם מקובל להביא בחשבון רק את סדרי הגודל, ולהתעלם מקבועים. למשל, אלגוריתם המבצע 8 n + 112 {\displaystyle 8n+112} פעולות על קלט בגודל n {\displaystyle n} הוא בעל "זמן ריצה ליניארי". הסימון הרווח לזמן הריצה של אלגוריתמים הוא: הגדרות פורמליות יותר למושגים אלו ניתן למצוא בקורס בויקיספר. אלגוריתם בעל זמן ריצה לוגריתמי מסתיים תוך זמן פרופורציוני ללוגריתם של גודל הקלט. אין צורך לציין את בסיס הלוגריתם, משום שהחלפת בסיס שקולה לכפל בקבוע, ולכן אם זמן הריצה פרופורציוני לגודל הקלט לפי בסיס לוגריתם כלשהו, הוא פרופורציוני לגודל הקלט גם לפי כל בסיס לוגריתם אחר. לדוגמה, חיפוש ברשימה ממוינת (בשיטת החצייה) מתבצע בזמן ריצה לוגריתמי. תפקידו של האינדקס בבסיס נתונים הוא לאפשר זמן ריצה לוגריתמי בחיפושים, כאשר ללא אינדקס זמן הריצה הוא ליניארי. נאמר על אלגוריתם שזמן ריצתו ליניארי אם זמן הריצה שלו חסום על ידי פונקציה ליניארית בגודל הקלט, או – באופן שקול – פרופורציוני לאורך הקלט. לדוגמה, אלגוריתם המחפש את המספר הגדול ביותר ברשימת מספרים נתונה על ידי מעבר סדרתי על הרשימה הוא בעל זמן ריצה ליניארי, שכן אם גודל הקלט הוא n {\displaystyle n} האלגוריתם יבצע n {\displaystyle n} צעדים. מאידך, אלגוריתם מיון הממיין רשימת מספרים על ידי בחירת המספר הגדול ביותר והסרתו מהרשימה, בחירת השני בגודלו והסרתו, וכן הלאה, אינו בעל זמן ריצה ליניארי. זאת משום שאם גודל הקלט הוא n {\displaystyle n} , האלגוריתם מבצע n {\displaystyle n} שלבים, ובכל שלב מבצע n − 1 {\displaystyle n-1} צעדים (מעבר על כל המספרים שנותרו) ולכן זמן הריצה יהיה ריבועי (פולינום ממעלה שנייה). זמן ריצה ליניארי הוא בפרט פולינומי ועל כן נחשב חישוב יעיל. לאלגוריתם נתון ישנו זמן ריצה פולינומי אם קיים פולינום P ( n ) {\displaystyle P(n)} כך שזמן הריצה של האלגוריתם עבור קלטים בגודל n {\displaystyle n} חסום על ידי ערך הפולינום עבור n {\displaystyle n} . אלגוריתמים אשר זמן ריצתם אינו חסום על ידי פולינום, כגון אלגוריתמים בעלי זמן ריצה מעריכי, נקראים לעיתים "סופר-פולינומיים". מקובל לקשר זמן ריצה פולינומי עם חישוב יעיל, כך שאלגוריתם ייחשב יעיל אם זמן הריצה שלו פולינומי. מעשית, לא כל זמן ריצה פולינומי מעיד על יעילות, שכן קיימים פולינומים בעלי חזקות גבוהות – בעת פיתוח אלגוריתם נעדיף זמן ריצה פולינומי מסדר נמוך, כגון זמן ליניארי או ריבועי. סיבוכיות זמן הריצה של אלגוריתם היא מעריכית אם ורק אם פונקציית זמן הריצה שלו חסומה על ידי פונקציה מעריכית ( k n {\displaystyle k^{n}} ) כפול קבוע, כאשר k > 1 {\displaystyle k>1} . על פי רוב, אלגוריתמים בעלי זמן ריצה מעריכי אינם נחשבים ליעילים, וזאת משום שהפונקציה המעריכית "גדלה מהר מאוד" ביחס לקלט; לכן, מועדף השימוש באלגוריתמים מזמני ריצה טובים יותר. דוגמה לאלגוריתם שכזה: אלגוריתם לפתרון בעיית מגדלי האנוי. סיבוכיות זמן ריצה תת-מעריכית או תת-אקספוננטית, מוערכת על ידי L n [ α , c ] {\displaystyle L_{n}[\alpha ,c]} , כאשר: L n [ α , c ] = exp ⁡ ( c log ⁡ ( n ) α log ⁡ ( log ⁡ ( n ) ) 1 − α ) {\displaystyle L_{n}[\alpha ,c]=\exp {\bigl (}c\log(n)^{\alpha }\log(\log(n))^{1-\alpha }{\bigr )}} α {\displaystyle \alpha } הוא משקל החלק הלוגריתמי במעריך, בעוד שהמשלים ל- α {\displaystyle \alpha } הוא משקל החלק הלוג-לוגריתמי, הקטן יותר. לכל ערך של α {\displaystyle \alpha } , הפונקציה מייצגת סיבוכיות גדולה יותר ככל ש- c {\displaystyle c} גדול יותר. לשם השוואה, L n [ 1 , c ] = n c {\displaystyle L_{n}[1,c]=n^{c}} היא סיבוכיות מעריכית, בעוד ש- L n [ 0 , c ] = log ⁡ ( n ) c {\displaystyle L_{n}[0,c]=\log(n)^{c}} היא סיבוכיות פולינומית. על סולם זה, קל להעריך את מידת היעילות של האלגוריתם מתוך התבוננות בערך α {\displaystyle \alpha } למשל L n [ 0.5 , c ] {\displaystyle L_{n}[0.5,c]} נמצאת ב"אמצע הדרך" בין סיבוכיות מעריכית לפולינומית. מונח זה מתאר אלגוריתמים שזמן הריצה שלהם חסום על ידי פונקציית עצרת. בדומה לאלגוריתמים בעלי זמן ריצה מעריכי, אלגוריתם שפועל בזמן ריצה עצרתי נחשב ללא-יעיל במיוחד כאשר מדובר בקלטים גדולים. האלגוריתם המדויק הידוע לפתרון בעיית הסוכן הנוסע נחשב לאלגוריתם בעל זמן ריצה שכזה. בעיית הפירוק לגורמים של מספר שלם נחשבה במשך שנים לבעיה מעריכית והיא עמדה על סיבוכיות משוערת מסדר גודל L n [ 0.5 , c ] {\displaystyle L_{n}[0.5,c]} והושגו רק שיפורים מינוריים בערכו של הקבוע c {\displaystyle c} . שיפור משמעותי הושג ב-1988, כאשר המתמטיקאים ג'ון פולארד, מארק מאנאס, הנדריק לנסטרה וארג'ן לנסטרה פרסמו את שיטת נפת שדה מספרים המיוחדת לפירוק מספרים בעלי מבנה מיוחד כמספרי פרמה ומספרי קנינגהם. לשיטה זו סיבוכיות מהצורה L n [ 1 / 3 , c ] {\displaystyle L_{n}[1/3,c]} כשהקבוע c = ( 32 / 9 ) 1 / 3 ≈ 1.526 {\displaystyle c=(32/9)^{1/3}\approx 1.526} . כמספר שנים לאחר מכן, פותח האלגוריתם הכללי – "נפת שדה המספרים הכללית" המתאים לפירוק כל מספר שלם ויעילותו מוערכת ב- L n [ 1 / 3 , c ] {\displaystyle L_{n}[1/3,c]} , כשהקבוע c = ( 64 / 9 ) 1 / 3 ≈ 1.923 {\displaystyle c=(64/9)^{1/3}\approx 1.923} . בכל המקרים נוסחאות הסיבוכיות מנומקות, מוסכמות על כל המומחים, מגובות בתוצאות אמפיריות – אך אינן מוכחות. מומחים הראו שעל ידי אלגוריתם קוונטי המבוצע במחשב קוונטי, כגון אלגוריתם שור, ניתן להפחית את זמן הריצה של בעיית הפירוק לגורמים לזמן ריצה של O ( log ⁡ ( n ) 3 ) {\displaystyle O(\log(n)^{3})} הנמוך משמעותית. סיבוכיות מקום סימון אסימפטוטי בסיס גרייבר