דף זה עוסק בסווג פונקציות (מתמטיות) ל"משפחות". כבר ראינו שלפעמים אפשר לקבוע חד-משמעית שאלגוריתם אחד פועל יותר טוב מאחר (עבור קלטים מספיק גדולים), על ידי סווגם למשפחות.
אנו נתעניין בהגדרות מדוייקות לסיווגים גמישים יחסית, כמו "משפחת הפונקציות שגדלה כמו פונקציה לינארית", או "משפחת הפונקציות שגדלה יותר מהר מפונקציה לוגריתמית".
כדאי לדעת:
בספר הקורס , הפרקים "Growth of Functions" ו"Summations" דנים בנושאים אלה.
הפונקציות בהן נעסוק
עריכה
הקבוצה
O
{\displaystyle \displaystyle O}
עריכה
הקבוצה
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
הנה קבוצת כל הפונקציות
f
(
n
)
{\displaystyle \displaystyle f(n)}
כך ש
f
(
n
)
{\displaystyle \displaystyle f(n)}
, עבור
n
{\displaystyle \displaystyle n}
גדול מספיק, חסומה מלמעלה ע"י
c
⋅
g
(
n
)
{\displaystyle \displaystyle c\cdot g(n)}
עבור
c
>
0
{\displaystyle \displaystyle c>0}
כלשהו.
הגדרה:
O
(
g
(
n
)
)
=
{
f
(
n
)
|
∃
c
>
0
,
n
0
>
0
∀
n
≥
n
0
f
(
n
)
≤
c
⋅
g
(
n
)
}
{\displaystyle \displaystyle O(g(n))=\{f(n)|\exists _{c>0,n_{0}>0}\forall _{n\geq n_{0}}f(n)\leq c\cdot g(n)\}}
אינטואיציה לאו.
למרבה הצער, הסימון
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
משמש לשני דברים שונים:
הקבוצה
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
, או איבר מהקבוצה
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
. שלוש הדוגמאות שראינו מסומנות בד"כ כ
n
=
O
(
n
)
{\displaystyle \displaystyle n=O(n)}
,
n
+
3
=
O
(
n
/
2
)
{\displaystyle \displaystyle n+3=O(n/2)}
, ו
n
2
≠
O
(
n
)
{\displaystyle \displaystyle n^{2}\neq O(n)}
.
שימו לב:
בראותך את הסימון
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
, עליך להבין מההקשר האם מדובר אכן בקבוצה או באיבר מתוך הקבוצה.
הקבוצה
Ω
{\displaystyle \displaystyle \Omega }
עריכה
הקבוצה
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
הנה קבוצת כל הפונקציות
f
(
n
)
{\displaystyle \displaystyle f(n)}
כך שכל
פונקציה
f
(
n
)
{\displaystyle \displaystyle f(n)}
, עבור
n
{\displaystyle \displaystyle n}
גדול מספיק, חסומה מלמטה ע"י
c
⋅
g
(
n
)
{\displaystyle \displaystyle c\cdot g(n)}
, עבור
c
>
0
{\displaystyle \displaystyle c>0}
כלשהו.
הגדרה:
Ω
(
g
(
n
)
)
=
{
f
(
n
)
|
∃
n
0
>
0
,
c
>
0
∀
n
≥
n
0
f
(
n
)
≥
c
⋅
g
(
n
)
}
{\displaystyle \displaystyle \Omega (g(n))=\{f(n)|\exists _{n_{0}>0,c>0}\forall _{n\geq n_{0}}f(n)\geq c\cdot g(n)\}}
אינטואיציה לאומגה.
בדיוק כמו ב
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
, גם
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
משמשת לשני דברים: הקבוצה
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
, או איבר הקבוצה
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
.
שלוש הדוגמאות הקודמות נכתבות בד"כ כ
n
=
Ω
(
n
)
{\displaystyle \displaystyle n=\Omega (n)}
,
n
+
3
=
Ω
(
n
/
2
)
{\displaystyle \displaystyle n+3=\Omega (n/2)}
, ו
n
2
=
Ω
(
n
)
{\displaystyle \displaystyle n^{2}=\Omega (n)}
.
שימו לב:
בראותך
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
, עליך להסיק מההקשר האם מדובר בקבוצה או באיבר מהקבוצה.
דוגמה:
האם חיפוש ליניארי רץ במקרה הגרוע בזמן
Ω
(
n
)
{\displaystyle \displaystyle \Omega (n)}
? כן, וודא שהנך מסוגל לשנות את ההוכחה שנתנה לעיל לגבי
O
(
n
)
{\displaystyle \displaystyle O(n)}
כדי להראות זאת.
דוגמה:
האם ישנו אלגוריתם (או חלקו) בקורס שאינו
Ω
(
1
)
{\displaystyle \displaystyle \Omega (1)}
?
לא משום שנדרש זמן
k
>
0
{\displaystyle \displaystyle k>0}
עבור קלט בגודל 1, והפונקציות שנטפל בהן הנן מונוטוניות לא יורדות.
הקבוצה
Θ
{\displaystyle \displaystyle \Theta }
עריכה
הקבוצה
Θ
(
g
(
n
)
)
{\displaystyle \displaystyle \Theta (g(n))}
הנה החיתוך של
O
(
g
(
n
)
)
{\displaystyle \displaystyle O(g(n))}
ו
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Omega (g(n))}
.
הגדרה:
Θ
(
g
(
n
)
)
=
O
(
g
(
n
)
)
⋂
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle \Theta (g(n))=O(g(n))\bigcap \Omega (g(n))}
אינטואיציה לתטא.
שימו לב:
בראותך
Θ
(
g
(
n
)
)
{\displaystyle \displaystyle \Theta (g(n))}
, עליך להסיק מההקשר האם מדובר בקבוצה או באיבר.
(כבר הוכחנו זאת.)
הוכחה: (1)
עפ"י ההגדרה:
f
1
(
n
)
=
O
(
f
(
n
)
)
{\displaystyle \displaystyle f_{1}(n)=O(f(n))}
משמעו שלכל
n
≥
n
0
,
f
{\displaystyle \displaystyle n\geq n_{0,f}}
,
f
1
(
n
)
≤
c
f
⋅
f
(
n
)
{\displaystyle \displaystyle f_{1}(n)\leq c_{f}\cdot f(n)}
, עבור
c
f
,
n
0
,
f
>
0
{\displaystyle \displaystyle c_{f},n_{0,f}>0}
כלשהם.
g
1
(
n
)
=
O
(
g
(
n
)
)
{\displaystyle \displaystyle g_{1}(n)=O(g(n))}
משמעו שלכל
n
≥
n
0
,
g
{\displaystyle \displaystyle n\geq n_{0,g}}
,
g
1
(
n
)
≤
c
g
⋅
g
(
n
)
{\displaystyle \displaystyle g_{1}(n)\leq c_{g}\cdot g(n)}
, עבור
c
g
,
n
0
,
g
>
0
{\displaystyle \displaystyle c_{g},n_{0,g}>0}
כלשהם.
לכן:
לכל
n
≥
max
{
n
0
,
f
,
n
0
,
g
}
{\displaystyle \displaystyle n\geq \max\{n_{0,f},n_{0,g}\}}
,
f
1
(
n
)
≤
max
{
c
f
,
c
g
}
⋅
f
(
n
)
{\displaystyle \displaystyle f_{1}(n)\leq \max\{c_{f},c_{g}\}\cdot f(n)}
.
לכל
n
≥
max
{
n
0
,
f
,
n
0
,
g
}
{\displaystyle \displaystyle n\geq \max\{n_{0,f},n_{0,g}\}}
,
g
1
(
n
)
≤
max
{
c
f
,
c
g
}
⋅
g
(
n
)
{\displaystyle \displaystyle g_{1}(n)\leq \max\{c_{f},c_{g}\}\cdot g(n)}
.
מקבלים את התוצאה ע"י חיבור שני האי-שוויונים האחרונים.
משפט:
לכל
f
(
n
)
,
g
(
n
)
{\displaystyle \displaystyle f(n),g(n)}
,
f
(
n
)
=
O
(
g
(
n
)
)
⇔
g
(
n
)
=
Ω
(
f
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))\Leftrightarrow g(n)=\Omega (f(n))}
.
הוכחה:
(
⇒
)
{\displaystyle \displaystyle (\Rightarrow )}
f
(
n
)
=
O
(
g
(
n
)
)
⇒
{\displaystyle \displaystyle f(n)=O(g(n))\Rightarrow }
∃
c
>
0
,
n
0
>
0
∀
n
≥
n
0
f
(
n
)
≤
c
⋅
g
(
n
)
⇒
{\displaystyle \displaystyle \exists _{c>0,n_{0}>0}\forall _{n\geq n_{0}}f(n)\leq c\cdot g(n)\Rightarrow }
∃
c
>
0
,
n
0
>
0
∀
n
≥
n
0
(
1
/
c
)
⋅
f
(
n
)
≤
g
(
n
)
⇒
{\displaystyle \displaystyle \exists _{c>0,n_{0}>0}\forall _{n\geq n_{0}}(1/c)\cdot f(n)\leq g(n)\Rightarrow }
∃
c
>
0
,
n
0
>
0
∀
n
≥
n
0
g
(
n
)
≥
(
1
/
c
)
⋅
f
(
n
)
⇒
{\displaystyle \displaystyle \exists _{c>0,n_{0}>0}\forall _{n\geq n_{0}}g(n)\geq (1/c)\cdot f(n)\Rightarrow }
g
(
n
)
=
Ω
(
f
(
n
)
)
{\displaystyle \displaystyle g(n)=\Omega (f(n))}
הוכחה: דומה למה שראינו באדיטיביות .
הוכחה: (1)
lim
n
→
∞
[
f
(
n
)
/
g
(
n
)
]
=
0
⇒
{\displaystyle \displaystyle \lim _{n\rightarrow \infty }[f(n)/g(n)]=0\Rightarrow }
∀
ϵ
>
0
∃
n
0
>
0
∀
n
≥
n
0
f
(
n
)
/
g
(
n
)
<
ϵ
⇒
{\displaystyle \displaystyle \forall _{\epsilon >0}\exists _{n_{0}>0}\forall _{n\geq n_{0}}f(n)/g(n)<\epsilon \Rightarrow }
∃
n
0
>
0
∀
n
≥
n
0
f
(
n
)
/
g
(
n
)
<
1
⇒
{\displaystyle \displaystyle \exists _{n_{0}>0}\forall _{n\geq n_{0}}f(n)/g(n)<1\Rightarrow }
∃
n
0
>
0
∀
n
≥
n
0
f
(
n
)
<
1
⋅
g
(
n
)
⇒
{\displaystyle \displaystyle \exists _{n_{0}>0}\forall _{n\geq n_{0}}f(n)<1\cdot g(n)\Rightarrow }
f
(
n
)
=
O
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))}
דמיון לאופרטורי השוואה
עריכה
בדף זה ראינו דרכים לסווג פונקציות לפי סדרי הגדילה שלהן. יש דמיון כלשהו בין הקבוצות שראינו לבין אופרטורי השוואה אלגבריים.
הקבוצה
O
{\displaystyle \displaystyle O}
והייחס
≤
{\displaystyle \displaystyle \leq }
עריכה
הקביעה
f
(
n
)
≤
g
(
n
)
{\displaystyle \displaystyle f(n)\leq g(n)}
אומרת ש
f
(
n
)
{\displaystyle \displaystyle f(n)}
חסומה מלמעלה על ידי
g
(
n
)
{\displaystyle \displaystyle g(n)}
. הקביעה
f
(
n
)
=
O
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))}
אומרת שקצב הגדילה של
f
(
n
)
{\displaystyle \displaystyle f(n)}
חסום מלמעלה על ידי קצב הגידול של
g
(
n
)
{\displaystyle \displaystyle g(n)}
. יש דמיון כלשהו בין שתי הקביעות.
ראשית, נשים לב שמתקיים
f
(
n
)
≤
g
(
n
)
⇒
f
(
n
)
=
O
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)\leq g(n)\Rightarrow f(n)=O(g(n))}
(למה?).
יש עוד נקודות דמיון. בין היתר:
רפלקסיביות : לכל פונקציה
f
(
n
)
{\displaystyle \displaystyle f(n)}
מתקיים
f
(
n
)
≤
f
(
n
)
{\displaystyle \displaystyle f(n)\leq f(n)}
, ובאותו אופן, מתקיים
f
(
n
)
=
O
(
f
(
n
)
)
{\displaystyle \displaystyle f(n)=O(f(n))}
.
טרנזיטיביות : לכל
f
(
n
)
,
g
(
n
)
,
h
(
n
)
{\displaystyle \displaystyle f(n),g(n),h(n)}
מתקיים
f
(
n
)
≤
g
(
n
)
⋀
g
(
n
)
≤
h
(
n
)
⇒
f
(
n
)
≤
h
(
n
)
{\displaystyle \displaystyle f(n)\leq g(n)\bigwedge g(n)\leq h(n)\Rightarrow f(n)\leq h(n)}
,ובאותו אופן,
f
(
n
)
=
O
(
g
(
n
)
)
⋀
g
(
n
)
=
O
(
h
(
n
)
)
⇒
f
(
n
)
=
O
(
h
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))\bigwedge g(n)=O(h(n))\Rightarrow f(n)=O(h(n))}
הקבוצה
Ω
{\displaystyle \displaystyle \Omega }
והייחס
≥
{\displaystyle \displaystyle \geq }
עריכה
הקביעה
f
(
n
)
≥
g
(
n
)
{\displaystyle \displaystyle f(n)\geq g(n)}
אומרת ש
f
(
n
)
{\displaystyle \displaystyle f(n)}
חסומה מלמטה על ידי
g
(
n
)
{\displaystyle \displaystyle g(n)}
. הקביעה
f
(
n
)
=
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=\Omega (g(n))}
אומרת שקצב הגדילה של
f
(
n
)
{\displaystyle \displaystyle f(n)}
חסום מלמטה על ידי קצב הגידול של
g
(
n
)
{\displaystyle \displaystyle g(n)}
. יש דמיון כלשהו בין שתי הקביעות.
ראשית, נשים לב שמתקיים
f
(
n
)
≥
g
(
n
)
⇒
f
(
n
)
=
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)\geq g(n)\Rightarrow f(n)=\Omega (g(n))}
(למה?).
יש עוד נקודות דמיון. בין היתר:
רפלקסיביות : לכל פונקציה
f
(
n
)
{\displaystyle \displaystyle f(n)}
מתקיים
f
(
n
)
≥
f
(
n
)
{\displaystyle \displaystyle f(n)\geq f(n)}
, ובאותו אופן, מתקיים
f
(
n
)
=
Ω
(
f
(
n
)
)
{\displaystyle \displaystyle f(n)=\Omega (f(n))}
.
טרנזיטיביות : לכל
f
(
n
)
,
g
(
n
)
,
h
(
n
)
{\displaystyle \displaystyle f(n),g(n),h(n)}
מתקיים
f
(
n
)
≥
g
(
n
)
⋀
g
(
n
)
≥
h
(
n
)
⇒
f
(
n
)
≥
h
(
n
)
{\displaystyle \displaystyle f(n)\geq g(n)\bigwedge g(n)\geq h(n)\Rightarrow f(n)\geq h(n)}
,ובאותו אופן,
f
(
n
)
=
Ω
(
g
(
n
)
)
⋀
g
(
n
)
=
Ω
(
h
(
n
)
)
⇒
f
(
n
)
=
Ω
(
h
(
n
)
)
{\displaystyle \displaystyle f(n)=\Omega (g(n))\bigwedge g(n)=\Omega (h(n))\Rightarrow f(n)=\Omega (h(n))}
הקבוצה
Θ
{\displaystyle \displaystyle \Theta }
והייחס
=
{\displaystyle \displaystyle =}
עריכה
הקביעה
f
(
n
)
=
g
(
n
)
{\displaystyle \displaystyle f(n)=g(n)}
אומרת ש
f
(
n
)
{\displaystyle \displaystyle f(n)}
שווה ל
g
(
n
)
{\displaystyle \displaystyle g(n)}
.
הקביעה
f
(
n
)
=
Θ
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=\Theta (g(n))}
אומרת שקצב הגדילה של
f
(
n
)
{\displaystyle \displaystyle f(n)}
שווה לקצב הגידול של
g
(
n
)
{\displaystyle \displaystyle g(n)}
. יש דמיון כלשהו בין שתי הקביעות.
ראשית, נשים לב שמתקיים
f
(
n
)
=
g
(
n
)
⇒
f
(
n
)
=
Θ
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=g(n)\Rightarrow f(n)=\Theta (g(n))}
(למה?).
יש עוד נקודות דמיון. בין היתר:
רפלקסיביות : לכל פונקציה
f
(
n
)
{\displaystyle \displaystyle f(n)}
מתקיים
f
(
n
)
=
f
(
n
)
{\displaystyle \displaystyle f(n)=f(n)}
, ובאותו אופן, מתקיים
f
(
n
)
=
Θ
(
f
(
n
)
)
{\displaystyle \displaystyle f(n)=\Theta (f(n))}
.
טרנזיטיביות : לכל
f
(
n
)
,
g
(
n
)
,
h
(
n
)
{\displaystyle \displaystyle f(n),g(n),h(n)}
מתקיים
f
(
n
)
=
g
(
n
)
⋀
g
(
n
)
=
h
(
n
)
⇒
f
(
n
)
=
h
(
n
)
{\displaystyle \displaystyle f(n)=g(n)\bigwedge g(n)=h(n)\Rightarrow f(n)=h(n)}
,ובאותו אופן,
f
(
n
)
=
Θ
(
g
(
n
)
)
⋀
g
(
n
)
=
Θ
(
h
(
n
)
)
⇒
f
(
n
)
=
Θ
(
h
(
n
)
)
{\displaystyle \displaystyle f(n)=\Theta (g(n))\bigwedge g(n)=\Theta (h(n))\Rightarrow f(n)=\Theta (h(n))}
.
יש עוד נקודות דמיון, לדוגמה:
סימטריה הופכית : לכל שתי פונקציות
f
(
n
)
,
g
(
n
)
{\displaystyle \displaystyle f(n),g(n)}
,
f
(
n
)
≤
g
(
n
)
⇔
g
(
n
)
≥
f
(
n
)
{\displaystyle \displaystyle f(n)\leq g(n)\Leftrightarrow g(n)\geq f(n)}
, ובאותו אופן,
f
(
n
)
=
O
(
g
(
n
)
)
⇔
g
(
n
)
=
Ω
(
f
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))\Leftrightarrow g(n)=\Omega (f(n))}
.
לכל שתי פונקציות
f
(
n
)
,
g
(
n
)
{\displaystyle \displaystyle f(n),g(n)}
,
f
(
n
)
=
g
(
n
)
⇔
f
(
n
)
≤
g
(
n
)
⋀
f
(
n
)
≥
g
(
n
)
{\displaystyle \displaystyle f(n)=g(n)\Leftrightarrow f(n)\leq g(n)\bigwedge f(n)\geq g(n)}
, ובאותו אופן,
f
(
n
)
=
Θ
(
g
(
n
)
)
⇔
f
(
n
)
=
O
(
g
(
n
)
)
⋀
f
(
n
)
=
Ω
(
g
(
n
)
)
{\displaystyle \displaystyle f(n)=\Theta (g(n))\Leftrightarrow f(n)=O(g(n))\bigwedge f(n)=\Omega (g(n))}
.
לדמיון בין הקבוצות לאופרטורים שראינו יש גם מגבלות. בין היתר:
לא תמיד נכון ש
2
⋅
f
(
n
)
≤
f
(
n
)
{\displaystyle \displaystyle 2\cdot f(n)\leq f(n)}
, אך תמיד נכון ש
2
⋅
f
(
n
)
=
O
(
f
(
n
)
)
{\displaystyle \displaystyle 2\cdot f(n)=O(f(n))}
.
תמיד מתקיים ש
f
(
n
)
≤
g
(
n
)
⇒
2
f
(
n
)
≤
2
g
(
n
)
{\displaystyle \displaystyle f(n)\leq g(n)\Rightarrow 2^{f(n)}\leq 2^{g(n)}}
, אך לא תמיד מתקיים
f
(
n
)
=
O
(
g
(
n
)
)
⇒
2
f
(
n
)
=
O
(
2
g
(
n
)
)
{\displaystyle \displaystyle f(n)=O(g(n))\Rightarrow 2^{f(n)}=O(2^{g(n)})}
.
עכשיו תורכם:
נמק נקודות אלו.
דוגמה לשילוב מספר כללים
עריכה
בחיפוש לינארי ובינרי ראינו דוגמות ראשונות של ניתוח אלגוריתמים. דף זה עסק בסדרי גדילה של פונקציות.
בהנתן אלגוריתם כלשהו, נוכל לשאול לגביו מספר שאלות, לדוגמה:
מהו זמן הריצה של האלגוריתם במקרה הטוב ביותר?
מהו זמן הריצה של האלגוריתם במקרה הגרוע ביותר?
מהו זמן הריצה של האלגוריתם במקרה הממוצע? (לא נעסוק בכך בקורס זה, עם זאת).
לעתים נוכל לשאול שאלות מפורטות יותר אפילו:
מהו זמן הריצה הטוב ביותר של חיפוש בינרי בהנחה שהאיבר נמצא במערך?
מהו זמן הריצה הגרוע ביותר של חיפוש לינארי בהנחה שהאיבר אינו נמצא במערך?
בכל פעם שאנו מקבלים אלגוריתם ושאלה מסוג זה, התשובה הינה פונקציה (מתמטית) של
n
{\displaystyle \displaystyle n}
, גודל הקלט. נוכל להשתמש בקבוצות סדרי הגדילה שראינו כאן כדי לסווג את הפונקציה (המתמטית). כך לדוגמה, נוכל להגיד על אלגוריתם כלשהו "הוא פועל במקרה הגרוע ביותר בערך כמו פונקציה לינארית", או "הוא פועל במקרה הטוב ביותר פחות מאיזושהי פונקציה שורשית".
כדאי לדעת:
במציאות, השאלות המעניינות ביותר לגבי אלגוריתמים הן לרוב זמן הריצה הגרוע והממוצע שלהם (זמן הריצה הטוב ביותר שלהם כמעט אף פעם לא מעניין). בקורס זה (שאינו מניח ידע בהסתברות), ננתח כמעט תמיד את זמן הריצה הגרוע של אלגוריתמים.