Transcript Quicksort
10 תרגול
Huffman code
QuickSort
Huffman code
• קוד הופמן – אלגוריתם קידוד לכיווץ נתונים שמשתמש
בתור עדיפות לקביעת הקידוד.
• תכונות האלגוריתם – בהינתן מידע המורכב מקבוצת
סמלים ( Cלדוגמה Cיכולה להיות קבוצת האותיות בשפה
כלשהי +סימני פיסוק) האלגוריתם משתמש בתור
עדיפות כדי לקבוע אילוי קידודים לתת לסמלים השונים
בקבוצה.
– האלגוריתם בונה עץ בינארי (עץ הופמן) שבו הסמלים
משויכים לעלים ,ואורך הקידוד של סמל הוא עומק העלה אליו
הוא משויך.
– כדי למצוא את הקידוד של סמל כלשהו ,נתחיל בשורש ונלך
במסלול עד לעלה אליו הוא משויך .נוסיף 0כל פעם שעברנו
לבן השמאלי ו 1כל פעם שעברנו לבן הימני
– הקידוד שנוצר הוא אופטימלי (מבין הקידודים המקודדים כל
סמל בנפרד)
קוד לבניית העץ-פסאודו
• Huffman (C)
n ← |C|
Q ← { new priority queue for the letters in C }
for i ← 1 to n-1
z ← allocate new node
x ← Extract_Min(Q)
y ← Extract_Min(Q)
z.left ← x
z.right ← y
frequency (z) ← frequency (x) + frequency (y)
Insert(Q, z)
דוגמה
• Example Huffman tree with 4 symbols (C={e,s,x,y})
• Frequencies:
x=1
y=1
s=7
e=20
• Encoding:
e: 1
s: 01
x: 000
y: 001
שאלה 1
.aמהו קוד הופמן האופטימלי עבור רצף התדירויות
הבא ,המבוסס על 8מספרי פיבונאצ'י הראשונים
a:1 b:1 c:2 d:3 e:5 f:8 g:13 h:21
.bהכלילו את תשובתכם לכדי מציאת קוד הופמן
האופטימלי כאשר התדירויות הנתונות הן עבור n
מספרי פיבונאצ'י הראשונים
שאלה 1
.aמהו קוד הופמן האופטימלי עבור רצף התדירויות
הבא ,המבוסס על 8מספרי פיבונאצ'י הראשונים
a:1 b:1 c:2 d:3 e:5 f:8 g:13 h:21
תשובה:
מאחר וישנן 8תדירויות ( 8אותיות ב-א"ב) ,גודל התור
ההתחלתי יהיה ,n=8ויידרשו 7מיזוגים לשם בניית
העץ.
העץ הסופי ייצג את הקידוד האופטימלי.
מילת-הקוד עבור כל אות היא רצף התוויות שעל הצלעות
(צלע לבן שמאלי – ,0צלע לבן ימני – )1במסלול
שמוביל מהשורש לעלה שמייצג את האות.
שאלה 1
.aמהו קוד הופמן האופטימלי עבור רצף התדירויות
הבא ,המבוסס על 8מספרי פיבונאצ'י הראשונים
a:1 b:1 c:2 d:3 e:5 f:8 g:13 h:21
תשובה:
0
1
0
1
1
0
1
1
1
0
1
1
1
1
0
1
1
1
1
1
0
1
1
1
1
1
1
0
1
1
1
1
1
1
1
h:
g:
f :
e:
d:
c:
b:
a:
שאלה 1
.bהכלילו את תשובתכם לכדי מציאת קוד הופמן
האופטימלי כאשר התדירויות הנתונות הן עבור nמספרי
פיבונאצ'י הראשונים
תשובה:
ניתן לראות שבסעיף הקודם העץ שהתקבל הוא למעשה
ענף אחד ארוך שעליו תלויים הרבה עלים.
זה נכון למספרי פיבונאצ'י באופן כללי ,שכן מתכונת
הרקורסיה של פיבונאצ'י נובע ש:
𝑛
= 𝐹𝑛+2
𝐹𝑖 + 1
𝑖=0
שאלה 1
.bהכלילו את תשובתכם לכדי מציאת קוד הופמן האופטימלי כאשר
התדירויות הנתונות הן עבור nמספרי פיבונאצ'י הראשונים
תשובה:
𝑘
ניתן להוכיח זאת באמצעות אינדוקציה:
= 𝐹𝑘+2
𝐹𝑖 + 1
𝑖=0
המספרים 1,1,2,3מהווים את הבסיס
נניח את נכונות הטענה לכל מספרי פיבונאצ'י שקטנים מ𝐹𝑛+2 -
צעד :הוכחת נכונות עבור 𝐹𝑛+2
𝑛
𝐹𝑖 + 1
𝑛−1
= 𝑛𝐹 𝐹𝑖 + 1 +
𝑖=0
𝑖𝐹 ,𝐹𝑛+2 > 𝑛𝑖=0
= 𝑛𝐹 𝐹𝑛+2 = 𝐹𝑛+1 +
𝑖=0
וברור שמתקיים 𝐹𝑛+2 > 𝐹𝑛+1לכן 𝐹𝑛+2
לכן מתקיים
נבחר אחרי כל מספרי פיבונאצ'י שקטנים ממנו כבר אוחדו לעץ אחד.
שאלה 2
.aבהינתן סדרת התדירויות הבאה עבור קוד הופמן𝑓𝑖 :
4
𝑖=1
𝑖 =
𝑛2 𝑖 ∈ 2..
ציירו את המבנה של עץ הופמן שמתאר את הסדרה.
פתרון:
הסבר:
• באופן דומה לסדרת פיבונאצ'י ,ניתן לראות שמתקיים 𝑗𝑓
= 𝑓1 + 𝑓2 + ⋯ + 𝑓𝑗−1
• לכן ,בכל שלב בבניית העץ נבחר את השורש של תת-העץ
𝑓1 −− −𝑓𝑖−1שכבר נוצר ,ונקבל את העץ בדיאגרמה.
שאלה 2
.bמצאו רשימת תדירויות כך שעץ הופמן עבור
רשימה זו יבנה בצורה דטרמיניסטית את המבנה
הבא:
22
10
12
7
4
5
3
תשובה (דוגמה):
2,2,3,5,5,5
2
2
5
5
שאלה 2
.c
מצאו נוסחת תדירויות כך שקוד הופמן עבור תדירויות אלו יבנה
בצורה דטרמיניסטית את המבנה הבא:
תשובה:
בכדי ליצור את המבנה הזה ,נרצה ששני האלמנטים הבאים בסדרה
ייבחרו לפני האיחוד עם תת העץ שכבר קיים.
התבנית של הסדרה מבוססת על העקרון שבכל רמה ,התדירות של שני
האלמנטים הבאים קטנה מסכום התדירויות עד לאותו רגע.
שאלה 2
.c
מצאו נוסחת תדירויות כך שקוד הופמן עבור תדירויות אלו יבנה
בצורה דטרמיניסטית את המבנה הבא:
לדוגמה:
𝑠𝑜𝑚𝑒 𝑐𝑜𝑛𝑠𝑡𝑎𝑛𝑡 𝑐 ≥ 1
𝑖≤4
𝑓𝑖−1
𝑛𝑒𝑣𝑒 𝑠𝑖 𝑖
= 𝑖𝑓
𝑒𝑠𝑙𝑒
𝑖−3
32
הפונקציה הנ"ל יוצרת ,למשל ,את הסדרה
…1,1,1,1,3,3,9,9,27,27,81,81,
Quicksort
- Quicksortהרעיון
• שיטת הפרד ומשול
• בוחרים איבר במערך שיהיה ה ,pivot-ומחלקים
את האיברים במערך לשתי קבוצות:
• )(< pivot) and (> pivot
)(> pivot
RIGHT group
)(<pivot
LEFT group
האלגוריתם
int partition( A, low, high )
pivot_value ← A[low]
left ← low
pivot ← left
right ← high
while ( left < right )
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
// Move right while item > pivot
while( A[right] > pivot_value)
right—
// Make sure right has not passed left
if( left < right )
SWAP(A, left, right)
// right is final position for the pivot
A[low] ← A[right]
A[right] ← pivot_item
return right
quickSort( A, low, high )
if(high > low)
pivot ← partition(A, low, high)
quickSort(A, low, pivot-1 )
quickSort(A, pivot+1, high)
ביצוע מיון לכל המערך יתבצע ע"י
הקריאה
quickSort(A, 0, length(A)-1)
דוגמה
4
1
7
3
8
6
2
Left
Pivot
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
5
9 10
Right
דוגמה
4
1
7
3
8
6
2
Pivot Left
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
5
9 10
Right
דוגמה
4
Pivot
1
7
3
8
6
2
Left
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
5
9 10
Right
דוגמה
4
Pivot
1
7
3
8
6
Left
// Move right while item > pivot
while( A[right] > pivot_value)
right--
2
5
9 10
Right
דוגמה
4
Pivot
1
7
3
8
6
Left
// Move right while item > pivot
while( A[right] > pivot_value)
right--
2
5
Right
9 10
דוגמה
4
Pivot
1
7
3
8
6
Left
// Move right while item > pivot
while( A[right] > pivot_value)
right--
2
Right
5
9 10
דוגמה
4
Pivot
1 7
Left
3
8
6 2
5
9 10
Right
if( left < right ) \\Make sure right has not passed left
SWAP(A, left, right)
דוגמה
4
Pivot
1
2
3
Left
8
6
7
Right
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
5
9 10
דוגמה
4
Pivot
1
2
3
8
Left
6
7
Right
// Move left while item < pivot
while( left < high && A[left] ≤ pivot_value)
left++
5
9 10
דוגמה
4
Pivot
1
2
3
8
6
Left Right
// Move right while item > pivot
while( A[right] > pivot_value)
right--
7
5
9 10
דוגמה
4
Pivot
1
2
3
8
6
Left
Right
// Move right while item > pivot
while( A[right] > pivot_value)
right--
7
5
9 10
דוגמה
4
Pivot
1
2
3
8
6
Right Left
// Move right while item > pivot
while( A[right] > pivot_value)
right--
7
5
9 10
דוגמה
4
Pivot
1
2
3 8 6 7 5 9 10
Right Left
// right is final position for the pivot
A[low] ← A[right]
A[right] ← pivot_value
return right
דוגמה
4
9 10
5
7
6
8
9 10
5
7
6
8
2
9 10
8
7
6
5
...
3
2
9 10
8
7
6
5
3
2
4
2
1
3
1
3
1
1
?Why Quicksort
• זמן ריצה:
במקרה הגרוע – )O(n^2
במקרה הממוצע O(nlogn) -
למה משתמשים בו בפועל ולא ב merge-sortשמבטיח ריצה
בזמן )?O(nlogn
ראיתם בכיתה ,כי בפועל זמן הריצה קרוב לזה של המקרה
הטוב .הקבועים ב Quicksort-קטנים מאלו שבmerge-sort-
למשל ,ולכן ברוב המקרים ירוץ מהר יותר.
היתרון הגדול של Quicksortשזהו אלגוריתם in placeוהוא לא
דורש זיכרון נוסף ,לעומת merge-sortהמקצה מערכים
נוספים.
in place algorithms: need small, constant amount of
extra storage space, beyond the items being sorted.
Quicksort
• stable sorting algorithms: maintain the
relative order of records with equal keys
• Is the QuickSort version above stable?
– No
שאלה 3
•
•
•
•
•
נתונה קבוצה של nאיברים ,Sבה אותו איבר יכול
להופיע יותר מפעם אחת ,ואינדקס .(1 ≤ k ≤ n( k
האיבר ה k-בגודלו הוא האיבר שיימצא במקום הk-
אם נמיין את האיברים.
הציעו אלגוריתם שמוצא את האיבר ה k-בגודלו ב-
) O(nזמן בממוצע.
לדוגמה{6, 3, 2, 4, 1, 1, 2, 6} :
האיבר ה 4-בגודלו הוא 2מכיוון שהוא נמצא
במיקום 4בקבוצה הממוינת:
}.{1, 1, 2, 2, 3, 4, 6, 6
פתרון- 3 שאלה
Select(k, S) // returns k-th element in S.
pick x in S
partition S into: // Slightly different variant of partition()
max(L) < x, E = {x}, x < min(G)
if k ≤ length(L) // Searching for item ≤ x.
return Select(k, L)
else if k ≤ length(L) + length(E) // Found
return x
else // Searching for item ≥ x.
return Select(k - length(L) - length(E), G)
pivot , בגודלוk- – האיבר הx
: קבוצות3- לS בכל שלב מחלקים את
x- כל האיברים הגדולים מ-G
x- כל האיברים הקטנים מ-L
x- כל האיברים השווים ל-E
G- או בL- ממשיכים לחפש אותו רקורסיבית ב,x אם לא מצאנו את
שאלה - 3פתרון
•
•
•
•
•
•
במקרה הגרוע:
ה pivot-הנבחר xהוא האיבר המקסימלי במערך הנוכחי ,ויש
רק אחד כזה.
– Gקבוצה ריקה|L|=n-1 ,|E|=1 ,
𝑛 𝑂𝑇 𝑛−1 +
𝑘>𝑛
= 𝑛 𝑇
𝑘
𝑘=𝑛
פתרון נוסחת הנסיגה𝑇 𝑛 = 𝑂(𝑛2 ) :
במקרה הממוצע:
בדומה ל ,Quicksort-חצי מהאיברים ב S-הם טובים כ ,pivot-לכן
3
הגודל של הממוצע Gו L-יהיה קטן מ 𝑛
• )𝑛(𝑂 = 𝑛 𝑂 +
3
𝑛
4
4
𝑇 ≤ 𝑛 𝑇 )שיטת המאסטר(
שאלה 4
• במערך בגודל ,nהציעו אלגוריתם בזמן צפוי של
) O(nשקובע האם קיים מספר שמופיע יותר מ n/2
פעמים
שאלה 4
• במערך בגודל ,nהציעו אלגוריתם בזמן צפוי של
) O(nשקובע האם קיים מספר שמופיע יותר מ n/2
פעמים
• אם xמופיע יותר מ ,n/2אז הוא המספר ה
𝑛/2 + 1בגודלו במערך
שאלה 4
שאלה 5
• הציעו אלגוריתם למיון בזמן ) O(nבמקרה הגרוע
במקרה הבא (בלי תוספת זכרון):
.1כל המפתחות הם 0או 1
נבצע Partitionעם pivot=0
שאלה 5
• הציעו אלגוריתם למיון בזמן ) O(nבמקרה הגרוע
במקרה הבא (בלי תוספת זכרון):
.2כל המפתחות הם בטווח ] k( [1..kקבוע)
נבצע Partitionעם pivot=1
נבצע Partitionעם pivot=2
...
נבצע Partitionעם pivot=k-1
שאלה 6
• נתון אלגוריתם מיון חדש:
– )]Sort recursively the first 2/3 of A (A[1..2n/3
– )]Sort recursively the last 2/3 of A (A[n/3..n
– )]Sort recursively the first 2/3 of A (A[1..2n/3
2
3
• אם ∗ nהוא לא מספר שלם אז נעגל אותו
למעלה.
• הוכיחו את האלגוריתם הנ"ל (כלומר ,להסביר
שהוא עובד) והראו את זמן הריצה.
שאלה 6
– )]Sort recursively the first 2/3 of A (A[1...2n/3
– )]Sort recursively the last 2/3 of A (A[n/3...n
– )]Sort recursively the first 2/3 of A (A[1...2n/3
•
•
•
•
•
הוכחה:
לאחר הצעד הראשון 1/3האיברים הגדולים ביותר במערך
נמצאים ב 2/3המקומות האחרונים במערך.
לכן ,לאחר הצעד השני 1/3האיברים האחרונים גדולים
מיתר האיברים במערך ,והם ממוינים (בינם לבין עצמם)
כלומר 1/3 ,האיברים האחרונים הם הגדולים ביותר ויהיו
ממוינים במקומם.
לאחר מכן אנחנו ממיינים את 2/3האיברים הנותרים ,ולכן
אנחנו מקבלים מערך ממוין.
6 שאלה
Sort recursively the first 2/3 of A (A[1..2n/3]) –
Sort recursively the last 2/3 of A (A[n/3+1..n]) –
Sort recursively the first 2/3 of A (A[1..2n/3]) –
2𝑛
𝑇 𝑛 = 3𝑇
:זמן ריצה
3
:נפתח בשיטת האיטרציה
• 𝑇 𝑛 = 3𝑇
2𝑛
3
= 3 3𝑇
8𝑛
3 3 3𝑇
27
4𝑛
9
•
•
=
2
3
𝑖
= … = 3𝑇
𝑖
𝑛
: = 𝑖 צעדיםlog 2 𝑛 אחרי
•
3
log3 𝑛
𝑇 𝑛 = 3
2
·𝑇 1 =⋯=𝑛
)𝑛 𝑂 = 𝑛 𝑇 (ניתן להגיע לזה גם בעזרת שיטת המאסטר
log3 3
log3 3
2
2
•
לכן
•
שאלה 7
•
•
•
•
נתון מערך Aעם M+Nאיברים N .האיברים
הראשונים ממויינים ,ו Mהאיברים האחרונים אינם
ממויינים.
מהו זמן הריצה במונחים של M,Nבמקרה הגרוע
אם מבצעים מיון הכנסה ( )insertion sortעל ?A
פתרון:
בעצם צריך לבצע הכנסה רק ל Mהאיברים
האחרונים .לכל אחד מהם לוקח 𝑀 𝑂 𝑁 +לכן
סה"כ 𝑀 .𝑂 𝑀 𝑁 +
שאלה 7
•
•
•
•
נתון מערך Aעם M+Nאיברים N .האיברים
הראשונים ממויינים ,ו Mהאיברים האחרונים אינם
ממויינים.
מהו זמן הריצה במונחים של M,Nבמקרה הגרוע אם
מבצעים מיון הכנסה ( )insertion sortעל ?A
פתרון נוסף:
ניתן לבצע insertion sortל Mהאיברים האחרונים
בזמן 𝑂 𝑀2ואז לבצע איחוד בזמן 𝑀 𝑂 𝑁 +
• סה"כ זמן ריצה 𝑁 = 𝑂 𝑀2 +
𝑁 𝑂 𝑀2 + 𝑀 +
שאלה 7
• נתון מערך עם M+Nאיברים N .האיברים הראשונים
ממויינים ,ו Mהאיברים האחרונים אינם ממויינים.
• עבור כל אחד מהמקרים הבאים הציעו שיטה (או שילוב של
שיטות) למיון יעיל בזמן הגרוע.
M=O(1) (a
• מיון הכנסה ב )O(N
שאלה 7
• נתון מערך עם M+Nאיברים N .האיברים הראשונים
ממויינים ,ו Mהאיברים האחרונים אינם ממויינים.
• עבור כל אחד מהמקרים הבאים הציעו שיטה (או שילוב של
שיטות) למיון יעיל בזמן הגרוע.
M=O(logN) (b
• מיין את MבO(MlogM) – merge sort
• אחד ( )mergeאת שני החלקים – )O(M+N
• סה"כ זמן ריצה 𝑀 𝑂 𝑀 log 𝑀 + 𝑀 + 𝑁 = 𝑂(𝑀 log
𝑁 𝑂 =𝑁 +
שאלה 7
• נתון מערך עם M+Nאיברים N .האיברים
הראשונים ממויינים ,ו Mהאיברים האחרונים אינם
ממויינים.
• עבור כל אחד מהמקרים הבאים הציעו שיטה (או
שילוב של שיטות) למיון יעיל בזמן הגרוע.
M=O(N) (c
• מיין את כל המערך בO(NlogN) – merge sort
• הזמן של quicksortבמקרה הגרוע יהיה ),O(N2
ולכן לא נשתמש בו
שאלה 8
• הציעו שיטה להפוך אלגוריתם מיון מבוסס השוואות לא יציב
Aלאלגוריתם יציב ’.A
• פתרון:
• לכל איבר נוסיף את האינדקס שלו כך שיהיו זוג
).(key,index
• מיין את האיברים לפי:
[key1, index1] < [key2, index2] ⇔ key1 < key2 or
)( key1 = key2 and index1 < index2
[key1, index1] > [key2, index2] ⇔ key1 > key2 or
)(key1 = key2 and index1 > index2
• הוספנו 𝑛 𝑂 זיכרון וזמן הריצה נשאר זהה לשל
האלגוריתם .A
שאלה 8
• הציעו שיטה להפוך אלגוריתם מיון מבוסס השוואות
לא יציב Aלאלגוריתם יציב ’.A
• פתרון נוסף:
.1נוסיף לכל איבר את האינדקס שלו.
.2נריץ את אלגוריתם Aהרגיל על המפתחות.
.3לכל איבר המופיע יותר מפעם אחת נריץ מיון נוסף
הפעם לפי האינדקס.
• זמן ריצה :שלב – 3במקרה הגרוע אותו איבר מופיע
בכל התאים ולכן זמן הריצה הוא 𝑛 𝑂 𝑛 log
סה"כ זמן הריצה )𝑛 𝑂(𝑛 log