מבני נתונים ואלגוריתמים - מחברת קורס/אלגוריתמים/תכנון דינאמי/תרגילים
מתוך ויקיספר, אוסף ספרי הלימוד והמדריכים החופשי.
[עריכה] תת-סידרה עולה ארוכה ביותר
[עריכה] שאלה
|
הגדרה: נתונה סידרה
|
|
דוגמה: נניח ש |
|
הגדרה: אם |
הנח שX הוא מערך גלובלי המתאר את הסדרה
, בעלת האורך
. בשאלה זו עליך למצוא אלגוריתם יעיל למציאת אורך הLIS.
[עריכה] תשובה
נשתמש במספר סימונים ומשפטים פשוטים:
- נגדיר את הרישה ה
של מחרוזת
כ
. - נגדיר כ
את אורך הLIS של
שאיברה האחרון הוא
. - נשים לב ש:
הוא אורך הLIS של
.
מקיים את הנוסחה
.
בפתרון נשתמש במערך גלובלי L המתאר, בכניסה ה
, את האיבר הקטן ביותר עד כה המסיים תת-סידרה עולה באורך
.
אלה שלבי פעולות הפתרון:
- נאתחל את כל אחד מאיברי
Lל
. - נעבור בלולאה, משמאל לימין, על איברי
X. כשנגיע לאיבר ה
:
- נחפש את האיבר הגדול ביותר ב ב
L[1], ..., L[i - 1]שאינו גדול מX[i]. - אם מצאנו איבר כזה בכניסה ה
של L, נקבעL[j + 1] = X[i]. - אם לא, נקבע
L[1] = X[i].
- נחפש את האיבר הגדול ביותר ב ב
- נמצא את האיבר הגדול ביותר ב
Lשאינו
, ונחזיר את האינדקס שלו כתשובה המבוקשת.
|
דוגמה: נניח ש כעת נעבור בלולאה על איברי
|
את המשפט הבא קל להוכיח באינדוקציה:
|
משפט: בסוף האיטרציה ה
|
להלן הפסוודו-קוד המתאים:
1 L = Make-Array(n) 2 for i in [1, ..., n] 3 L[i] = ∞ 4 for i in [1, ..., n] 5 j = Smaller-Index(L, X[i]) 6 L[j + 1] = X[i] 7 max = 0 8 for i in [1, ..., n] 9 if L[i] > max and L[i] < ∞ 10 max = L[i]
הנה הסבר לפסוודו-קוד:
- ב1-3 מאתחלים את
L. קל לראות שהסיבוכיות לינארית. - ב4-6 מעדכנים את
Lעל פי ההסבר שראינו למעלה. הפונקציהSmaller-Index, שמימושה אינו מופיע כאן, ניתנת למימוש בקלות באופן דומה למה שראינו ב[[מבני נתונים ואלגוריתמים - מחברת קורס/#|]] (שים לב שLממוין). הסיבוכיות היא
. - ב7-10 מחפשים את המקסימום ב
L. קל לראות שהסיבוכיות לינארית.
[עריכה] תת-סידרה בדלנית מקסימאלית כפלית
[עריכה] שאלה
נתונה סידרה
של מספרים גדולים או שווים ל1. תת-סידרה
של X נקראת חברותית אם היא מכילה לפחות שני איברים עוקבים של X, ובדלנית אם היא איננה חברותית.
|
דוגמה: נניח ש
|
אנא כתוב אלגוריתם אשר מקבל סידרה
ומדפיס תת-סידרה בדלנית שמכפלת איבריה גדולה ככל האפשר. (הנח שמכפלת איברי תת-סידרה ריקה היא 1.) קבע את Length(X) כ
, ונתח את סיבוכיות האלגוריתם במונחי
.
|
דוגמה: נניח שהאלגוריתם מקבל כקלט את |
[עריכה] תשובה
[עריכה] המבנה הרקורסיבי
נגדיר כ
את המכפלה הגדולה ביותר האפשרית של תת-סידרה בדלנית של
(שים לב שתת-הסדרה אינה בהרכח משתמשת ב
).
|
משפט:
|
הוכחה: נשים לב לנקודות הבאות:
- אם
, אז בוודאי שנבחר באיבר הראשון (כל איבר הוא לפחות 1); נקבל בהכרח
. - אם
, אז בוודאי שנבחר באיבר הראשון או באיבר השני (כל איבר הוא לפחות 1); נקבל בהכרח
. - אם
, אז או שנבחר ב
או שלא (אלו שתי האפשרויות היחידות).
- אם נבחר ב
, אז לא נוכל לבחור ב
; במקרה זה נרצה להכפיל את
בתוצאה הטובה ביותר האפשרית עד
, שהיא
. - אם לא נבחר את
, אז נרצה את תת-הסדרה הטובה ביותר עד
, שהיא, עפ"י ההגדרה,
.
- אם נבחר ב
בנקודה השלישית, היות שאיננו יודעים אם כדאי לבחור את האיבר ב
או לא - ניקח את המקסימום משתי האפשרויות.
[עריכה] מימוש רקורסיבי נאיבי
להלן פסוודו-קוד המממש רעיון זה:
Max-Product(i) 1 if i == 1 2 return X[1] 3 if i == 2 4 return max(X[1], X[2]) 6 guess-with = Max-Product(i - 2) * X[i] 7 guess-without = Max-Product(i - 1) 8 if guess-with > guess-without 9 return guess-with 10 else 11 return guess-without
קל לראות שסיבוכיות האלגוריתם גבוהה מדי. היא נתונה ע"י הפתרון לנוסחה
(כלומר אקספוננציאלית, מפני שזו סדרת Fibonacci), וסיבתה היא הסיבה הקבועה: פתירת אותן תת-בעיות שוב ושוב.
[עריכה] שימוש בmemoization
להלן פסוודו-קוד יעיל יותר:
1 L = Make-Array(n) 2 for i in [1, ..., n] 3 L[i] = Nil Max-Product(i) 1 if L[i] != Nil 2 return L[i] 3 if i == 1 4 L[1] = X[1] 5 return L[1] 6 if i == 2 7 L[2] = max(X[1], X[2]) 8 return L[2] 9 guess-with = Max-Product(i - 2) * X[i] 10 guess-without = Max-Product(i - 1) 11 if guess-with > guess-without 12 L[i] = guess-with 12 return L[i] 13 else 14 L[i] = guess-without 15 return L[i]
[עריכה] הדפסת האיברים
להלן פסוודו-קוד המאפשר גם הדפסת תת-הסדרה:
1 L = Make-Array(n) 2 U = Make-Array(n) 3 for i in [1, ..., n] 4 L[i] = Nil Max-Product(i) 1 if L[i] != Nil 2 return L[i] 3 if i == 1 4 L[1] = X[1] 5 U[1] = True 6 return L[1] 7 if i == 2 8 L[2] = max(X[1], X[2]) 9 U[2] = X[2] == L[2]? True : False 10 return L[2] 11 guess-with = Max-Product(i - 2) * X[i] 12 guess-without = Max-Product(i - 1) 13 if guess-with > guess-without 14 L[i] = guess-with 15 U[i] = True 16 return L[i] 17 else 18 L[i] = guess-without 19 U[i] = False 20 return L[i]
המערך U מחזיק בכניסה ה
את ההכרעה האם תת-הסדרה הטובה ביותר עד
אכן משתמשת באיבר ה
או לא. קטע הקוד הבא מראה כיצד להדפיס את האינדקסים של תת-הסידרה הטובה ביותר המסתיימת ב
.
Print-Seq(i) 1 if U[i] == True 2 Print-Seq(i - 2) 3 Print(i) 4 return 5 Print-Seq(i - 1)
כיצד נשתמש בקוד, אם כן?
- נאתחל המשתנים הגלובליים, ונקרא ל
Max-Product(n). - נקרא ל
Print-Seq(n).
[עריכה] ניתוח סיבוכיות
הסיבוכיות הכוללת הנה לינארית:
- מהתבוננות, ברור שהאתחול לינארי.
- הסיבוכיות של
Max-Product(n)נתון על ידי
, כאשר
הוא זמן הריצה של Max-Product(i)בהנחה שכל קריאה רקורסיבית היא
(ראו הניתוח בדג הסלמון). - מהתבוננות, קל לראות שהדפסת האיברים היא
.
[עריכה] חיתוך בד
[עריכה] שאלה
נתונה פיסת בד שמידותיה
(כלומר ארכה
מטר, ורחבה
מטר). הנח ש
ו
מספרים שלמים.
נתונות
מידות,
, שלהן מותאמים מחירים
. כלומר:
- חתיכת בד במידה
שווה
שקלים. - חתיכת בד במידה
שווה
שקלים. - ...
- חתיכת בד במידה
שווה
שקלים.חוץ מ
מידות אלה, כל המידות האחרות חסרות ביקוש, ולכן כל חתיכת בד במידה שונה
ממידות אלה שווה 0 שקלים. הנח שכל
ו
הוא מספר שלם חיובי ממש.
|
דוגמה: בתרשים הבא, A מראה פיסת בד במידות |
המטרה היא להפיק מהבד את מרב הערך, ע"י גזירת הבד אם יש תועלת בכך. בכל גזירה אפשר מותר לגזור את פיסת הבד לאורך או לרוחב. הנח שאין יכולת לסובב את פיסת הבד.
|
דוגמה: כיצד נוכל להשתמש בפיסת הבד מA שבדוגמה הקודמת? לו היינו יכולים לסובב אותה, היינו יכולים להפיק ממנה צורה ששווייה 35 שקל ללא גזירה אחת. עם זאת, עדיין יש סדרת גזירות שתפיק צורות בשווי 4 שקלים. בתרשים הבא:
יש עוד סדרות חיתוך שיניבו צורות בשווי 4 שקלים, אך אין סדרה שתניב יותר מכך. |
בשאלה זו עליך למצוא אלגוריתם יעיל למציאת סדרת החיתוך הרווחית ביותר. אנא הוכח שהאלגוריתם שמצאת עובד בסיבוכיות
.
הנח שקיימת פונקציה Value(i, j) המחזירה בזמן
:
- 0 אם
אינה אחת מ
המידות בעלות התועלת.
אם
.
[עריכה] תשובה
[עריכה] המבנה הרקורסיבי
נגדיר כ
את מקסימום הרווח מפיסת בד במידות
.
|
משפט:
|
|
כדאי לדעת: שים לב שהנוסחה הנ"ל כוללת בתוכה תנאי עצירה, מפני שעבור אפשר גם לכתוב את נוסחת הנסיגה בדרכים אחרות, ובחלקן גם יופיעו תנאי עצירה בצורה מפורשת יותר. |
הוכחה: יש שלוש אפשרויות:
- כדאי לחתוך את פיסת הבד אפקית באיזשהו
(כמתואר בתרשים הבא). נשים לב שאפשרות זו רלוונטית רק עבור עבור
. - כדאי לחתוך את פיסת הבד אנכית באיזשהו
(כמתואר בתרשים הבא אחריו). נשים לב שאפשרות זו רלוונטית רק עבור עבור
. - כלל לא משתלם לחתוך את פיסת הבד.
בתרשים הבא, לדוגמה, חיתוך אופקי של הצורה בA בשורה
ייצור שתי צורות מלבניות בB; לראשונה שבהן יהיו
שורות:
בתרשים הבא, לדוגמה, חיתוך אנכי של הצורה בA בעמודה
ייצור שתי צורות מלבניות בB; לראשונה שבהן יהיו
עמודות:
כעת נדון בכל אחת משלוש האפשרויות:
- כדאי לחתוך את פיסת הבד אפקית באיזשהו
. במקרה זה נקבל שתי חתיכות בד, האחת במידות
, והשניה במידות
. עפ"י ההגדרה, הטוב ביותר שאפשר להשיג עבור הראשונה הוא
, ועבור השניה
. - כדאי לחתוך את פיסת הבד אנכית באיזשהו
. במקרה זה נקבל שתי חתיכות בד, האחת במידות
, והשניה במידות
. עפ"י ההגדרה, הטוב ביותר שאפשר להשיג עבור הראשונה הוא
, ועבור השניה
. - כלל לא משתלם לחתוך את פיסת הבד: הטוב ביותר האפשרי הוא
Value(i, j).
היות שאיננו יודעים איזו משלוש האפשרויות היא הטובה ביותר, אנו לוקחים את המקסימום מבין שלושתן.
[עריכה] מימוש רקורסיבי נאיבי
הפסוודו-קוד הבא מראה מימוש פשוט לנוסחה הנ"ל:
Max-Value(i, j) 1 max-value = 0 # Check horizontal cuts. # This loop means the C-language loop for(i' = 1; i' < i; ++i') 2 for i' in [1, ..., i - 1] 3 guess = Max-Value(i', j) + Max-Value(i - i', j) 4 if guess > max-value 5 max-value = guess # Check vertical cuts. # This loop means the C-language loop for(j' = 1; j' < j; ++j') 6 for j' in [1, ..., j - 1] 7 guess = Max-Value(i, j') + Max-Value(i, j - j') 8 if guess > max-value 9 max-value = guess # Check the worth of this shape. 19 guess = Value(i, j) 11 if guess > max-value 12 max-value = guess 13 return max-value
[עריכה] שימוש בmemoization
כרגיל, נוסיף מבנה נתונים פשוט (במקרה זה המטריצה M) כדי לשמור את הערכים שכבר חישבנו:
1 M = Make-Matrix(m, n) 2 for i in [1, ..., m] 3 for j in [1, ..., n] 4 M[i][j] = Nil Max-Value(i, j) 1 if M[i, j] != Nil 2 return M[i, j] 4 max-value = 0 # Check horizontal cuts. 5 for i' in [1, ..., i - 1] 6 guess = Max-Value(i', j) + Max-Value(i - i', j) 7 if guess > max-value 8 max-value = guess # Check vertical cuts. 9 for j' in [1, ..., j - 1] 10 guess = Max-Value(i, j') + Max-Value(i, j - j') 11 if guess > max-value 12 max-value = guess # Check the worth of this shape. 13 guess = Value(i, j) 14 if guess > max-value 15 max-value = guess 16 M[i, j] = max-value 17 return max-value
[עריכה] הדפסת החלטות החיתוך
שוב כרגיל, כדי לשמור גם את סדרת הפעולות (ולא רק תוצאתן), נוסיף עוד מבנה נתונים פשוט (במקרה זה המטריצה C):
1 M = Make-Matrix(m, n) 2 C = Make-Matrix(n, n) 3 for i in [1, ..., m] 4 for j in [1, ..., n] 5 M[i][j] = Nil 6 C[i][j] = Nil Max-Value(i, j) 1 if M[i, j] != Nil 2 return M[i, j] 3 max-value = 0 # Check horizontal cuts. 4 for i' in [1, ..., i - 1] 5 guess = Max-Value(i', j) + Max-Value(i - i', j) 6 if guess > max-value 7 max-value = guess 8 C[i][j] = ('Horizontal', i') # Check vertical cuts. 9 for j' in [1, ..., j - 1] 10 guess = Max-Value(i, j') + Max-Value(i, j - j') 11 if guess > max-value 12 max-value = guess 13 C[i][j] = ('Vertical', j') # Check the worth of this shape. 14 guess = Value(i, j) 15 if guess > max-value 16 max-value = guess 17 C[i][j] = ('Shape', 0) 18 M[i, j] = max-value 19 return max-value
המטריצה C היא מטריצה של זוגות (בשפת C היינו ממשים זאת בעזרת מטריצה של מבנים):
- האיבר הראשון בכל זוג הוא מחרוזת, המתארת איזו משלול האפשרויות היא בחרנו.
- האיבר השני בכל זוג הוא מספר המתאר היכן חתכנו.
הנה הקוד המדפיס את סדרת הפעולות:
Print-Cuts(i, j) 1 Print('For the best value of ' + i + ' and ' + j) 2 (type, index) = C[i][j] 3 if type == 'Horizontal' 4 Print 'Cut horizontally at ' + index 5 Print-Cuts(index, j) 6 Print-Cuts(i - index, j) 7 else if type == 'Vertical' 8 Print 'Cut vertically at ' + index 9 Print-Cuts(i, index) 10 Print-Cuts(i, j - index) 11 else 12 Print 'Use the shape itself'
[עריכה] ניתוח סיבוכיות
נגדיר כ
את סיבוכיות Max-Cuts(i, j) בהנחה שכל קריאה רקורסיבית היא
. קל מאד לראות מהקוד ש
. נשים לב גם ש
, ולכן
. סיבוכיות
חסומה מלמעלה על ידי
.
גם כאשר לוקחים בחשבון את האתחול והדפסת הפרטים, הפתרון עדיין
.
[עריכה] תת-מחרוזת משותפת ארוכה ביותר
[עריכה] שאלה
|
הגדרה: מחרוזת היא רצף של תווים. אם
|
|
דוגמה:
אם
|
|
הגדרה: אם |
בשאלה זו עליך למצוא אלגוריתם יעיל למציאת הLCS לכל שתי מחרוזות
ו
:
- אנא תאר אלגוריתם יעיל המוצא את אורך הLCS.
- אנא תאר אלגוריתם יעיל המדפיס את הLCS עצמו.
|
שימו לב: בכל אחד משני הסעיפים:
|
[עריכה] תשובה
[עריכה] המבנה הרקורסיבי של הבעיה
ראשית מספר סימונים:
- כפי שצוין בשאלה, נגדיר את אורך
כ
, ואת אורך
כ
. - נסמן את הרישה ה
של מחרוזת
כ
. במילים אחרות,
. - נגדיר כ
את אורך הLCS של
ו
. במילים אחרות,
היא אורך תת-הסדרה הארוכה ביותר המשותפת ל
ו
.
|
עכשיו תורך: וודא שהמך מבין מדוע לפי הגדרה זו, |
|
משפט:
|
הוכחה: אם
או
, אז (לפחות) אחת המחרוזות היא המחרוזת הריקה, ורק המחרוזת הריקה יכולה להיות תת-מחרוזת של המחרוזת הריקה. למחרוזת הריקה יש אורך 0, ומכאן החלק הראשון של המשפט.
מכאן נניח ששתי המחרוזות אינן ריקות.
נניח ש
. ראשית, קל לראות שבהכרח תווה האחרון של
הוא
- אם זה לא היה המצב, אז
גם היא מחרוזת משותפת ל
ו
, דבר שנוגד עפ"י ההגדרה את היותה של
תת-המחרוזת המשותפת הארוכה ביותר. נחשוב כעת על כל תתי המחרוזות המשותפות מהצורה
: עבור כל אחת מהן, קל לראות ש
היא תת-מחרוזת משותפת ל
ו
, ולכן בהכרח הארוכה שבהן היא זו שהרישה שלה היא הLCS של
ו
.
לחלופין, נניח ש
; יש שלוש אפשרויות:
מסתיימת ב
- אם כן, הLCS בהכרח מהצורה
, כאשר
היא הLCS של
ו
.
מסתיימת ב
- אם כן, הLCS בהכרח מהצורה
, כאשר
היא הLCS של
ו
.
אינה מסתיימת ב
, וכן אינה מסתיימת ב
- אם זה המצב, אז הLCS של
ו
הוא הLCS של
ו
(וכן גם הLCS של
ו
), כך ששתי הבדיקות הקודמות מכסות כבר (פעמיים אפילו) מקרה זה.
[עריכה] מציאת אורך הLCS
להלן פסוודו-קוד למציאת אורך הLCS.
1 L = Make-Matrix(m, n) 2 for i in [1, ..., m] 3 for j in [1, ..., n] 4 L[i][j] = Nil LCS-Length(i, j) 1 if L[i][j] == Nil 2 if i == 0 or j == 0 3 return 0 4 else if X[i] == Y[j] 5 L[i][j] = 1 + LCS-Length(i - 1, j - 1) 6 else 7 guess-x = LCS-Length(i - 1, j) 8 guess-y = LCS-Length(i, j - 1) 9 L[i][j] = Max(guess-x, guess-y) 10 return L[i][j]
ארבע השורות הראשונות מאתחלות מטריצה המשמשת לmemoization, והפונקציה LCS-Length מוצאת את את האורך המבוקש. 1-4 מייצרות מטריצה (גלובלית) שכל אחד מאיבריה מאותחל לNil. 2-9 של LCS-Length, פועלות בדיוק לפי נוסחת הנסיגה הקודמת (שאת נכונותה כבר הראינו). המטריצה L, בפרט ב1 של LCS-Length, מטפלות במצב בו הפונקציה נקראת עם פרמטרים שאתם היא כבר נקראה בעבר.
[עריכה] ניתוח סיבוכיות
נגדיר את
כזמן שאורכת LCS-Length בהנחה שכל אחת מקריאותיה הרקורסיביות היא
. קל לראות ש
.
זמן הריצה של LCS-Length(m, n) חסום מלמעלה על ידי
. חוץ מכך ישנו האתחול 1-4 שאורך גם כן
. הזמן הכולל, לכן, הוא
.
[עריכה] הדפסת איברי הLCS
ראשית נשנה מעט את הפסוודו-קוד הקודם.
1 L = Make-Matrix(m, n) 2 D = Make-Matrix(m, n) 3 for i in [1, ..., m] 4 for j in [1, ..., n] 5 L[i][j] = Nil LCS-Length(i, j) 1 if L[i][j] == Nil 2 if i == 0 or j == 0 3 return 0 4 else if X[i] == Y[j] 5 L[i][j] = 1 + LCS-Length(i - 1, j - 1) 6 D[i][j] = 'b' 7 else 8 guess-x = LCS-Length(i - 1, j) 9 guess-y = LCS-Length(i, j - 1) 10 L[i][j] = Max(guess-x, guess-y) 11 if L[i][j] == guess-x 12 D[i][j] = 'x' 13 else 14 D[i][j] = 'y' 15 return L[i][j]
המטריצה הגלובלית D מתארת בכניסה ה
את ההחלטה לגבי הLCS של
ו
:
'x'מסמלת את ההחלטה לוותר על התו האחרון ב
.'y'מסמלת את ההחלטה לוותר על התו האחרון ב
.'b'מסמלת את ההחלטה לוותר על התו האחרון בשניהם. זה בדיוק המצב בו
נמצא כחלק מהLCS.הפונקציה הבאה מדפיסה את הLCS כאשר קוראים לPrint-LCS(m, n):
Print-LCS(i, j) 1 if i > 1 and j > 1 2 if D[i][j] == 'x': 3 Print-LCS(i - 1, j) 4 else if D[i][j] == 'y': 5 Print-LCS(i, j - 1) 6 else 7 Print-LCS(i - 1, j - 1) 1 if D[i][j] == 'b': 2 Print(X[i])
קל לראות שהסיבוכיות עדיין
: השינויים בLCS-Length אינם משנים את סיבוכיותה; כל קריאה לPrint-LCS(i, j) מורידה (לפחות) את אחד הארגומנטים שלה ב1, ולכן היא יכולה להיקרא לכל היותר
פעמים. הסיבוכיות, לכן, היא
+
=
.
[עריכה] גשר זבלה
[עריכה] שאלה
מעל הירקון עומד גשר שאורכו
מטרים (
מספר שלם חיובי גדול מ1 כלשהו). הגשר נמצא לא תקין אפילו בסטנדרטים של מדינת ישראל(!). יש לפרק את הגשר לחלקים ולפנותו בצורה הזולה ביותר. ראשית צריך לשבור את הגשר. ניתן לשבור את הגשר למספר חלקים. שבירת הגשר חייבת להתבצע בנקודות
בלבד. שבירת כל נקודה עולה
שקלים (
מספר שלם חיובי כלשהו).
|
דוגמה: נניח
|
כעת יש להוביל את חלקי הגשר. הנח שקיימת פונקציה
ידועה כך ש
מתארת את עלות הובלתו של חלק בעל אורך i. הנח ש
הוא מספר שלם חיובי לכל
.
|
שימו לב: וודא שאינך מניח במובלע דברים שלא נאמרו לגבי |
|
דוגמה: בהמשך לדוגמה הקודמת שראינו, נניח
|
אנא מצא אלגוריתם יעיל למציאת נקודות השבירה כך שסך העלויות (שבירה והובלה) נמוך ככל האפשר. כלומר, אנא כתוב אלגוריתם יעיל שימצא את העלות המינימלית וכן ידפיס את נקודות השבירה של עלות זו.
[עריכה] תשובה
[עריכה] המבנה הרקורסיבי
נגדיר כ
את העלות הזולה ביותר לטיפול בקטע באורך
.
|
משפט:
|
הוכחה: נשים לב לנקודות הבאות:
- אם
, אז אין מה לחתוך; ההובלה תעלה
. - אם
, אז או שלא חותכים את הקטע, או שכן. אם לא חותכים, עלות השבירה 0, ועלות ההובלה
. אם חותכים בנקודה
, אז עלות הטיפול בחלק השמאלי תהיה , עפ"י ההגדרה,
, עלות השבירה היא כמובן
, ועלות הטיפול בחלק הימני תהיה, עפ"י ההגדרה,
. נותר לקחת מינימום על פני
, ולהחליט האם לחתוך או לא.
|
כדאי לדעת: אפשר כמובן לחשוב על מבנה רקורסיבי אחר לבעיה, כמו לדוגמה המבנה בדג הסלמון. |
[עריכה] מימוש נאיבי
להלן פסוודו-קוד המממש את נוסחת הנסיגה.
Min-Cost(i) 1 if i == 1 2 return F(1) 3 guess-without = F(i) 4 guess-with = ∞ 5 for j in [1, …, i - 1] 6 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 7 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 8 if guess-with < guess-without 9 return guess-with 10 else 11 return guess-without
[עריכה] שימוש בmemoization
נוסיף memoization.
Min-Cost(i) 1 if M[i] != Nil 2 return M[i] 1 if i == 1 2 M[1] = F(1) 3 return F(1) 4 guess-without = F(i) 5 guess-with = ∞ 6 for j in [1, … i - 1]] 7 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 8 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 9 if guess-with M[i] < guess-without 10 M[i] = guess-with 11 return guess-with 12 else 13 M[i] = guess-without 14 return guess-without
(נניח שהמערך הגלובלי Mבאורך nמאותחל תחילה כולו לNil.)
[עריכה] הדפסת נקודות השבירה
נוסיף גם מספיק מידע לצורך הדפסת פרטי הפתרון.
Min-Cost(i) 1 if M[i] != Nil 2 return M[i] 1 if i == 1 2 M[1] = F(1) 3 Break[1] = Nil 4 return F(1) 5 guess-without = F(i) 6 guess-with = ∞ 7 for j in [1, … i - 1]] 8 if guess-with> Min-Cost(j) + k + Min-Cost(i - j) 9 guess-with = Min-Cost(j) + k + Min-Cost(i - j) 10 if guess-with 11 Break[i] = j 12 if guess-with 13 M[i] = guess-with 14 return guess-with 15 else 16 M[i] = guess-without 17 return guess-without
המערך הגלובלי Break מכיל את ההחלטה שעשינו לגבי כל קטע. Break[i]הוא Nilאם החלטנו לא לחלק את הקטע, והוא
אם החלטנו לשבור בנקודה j.
כעת נדפיס את העצירות, ע"י קריאה לPrint-Breaks(n, 0). הפונקציה מוגדרת להלן.
Print-Breaks(i, start) 1 if Break[i] == Nil 2 return 3 j = Break[i] 4 Print(j + start) 5 Print-Nums(j, start) 6 Print-Nums(i - j, start + j)
הפונקציה מקבלת שני ארגומנטים: אורך הקטע, i, ותחילת הקטע, start. נניח שהפונקציה נקראה עבור שני ערכים ספיציפיים כאלה. אם Break[i] == Nil, אז לא חתכנו את הקטע, ואין מה להדפיס. אם לא, וBreak[i] == j, אז יש להדפיס את הנקודה j(תוך זכירה שאנו נמצאים startמתחילת כל הגשר), את החלק השמאלי, ואת החלק הימני.
[עריכה] ניתוח סיבוכיות
נגדיר את זמן הריצה של Min-Time(i) בהנחה שכל קריאה רקורסיבית היא
כ
. קל לראות ש
. זמן הריצה חסום מלמעלה על ידי
.
גם כאשר לוקחים בחשבון את האתחול והדפסת הפרטים, הפתרון עדיין
.
[עריכה] זמן הריצה של הכפלת שרשרת מטריצות תחת Memoization
[עריכה] שאלה
אנא וודא את הנוסחות הבאות בזמן הריצה של הכפלת שרשרת מטריצות תחת memoization:
(נזכר ש
מוגדרת כזמן שאורכת Min-Time(i, j)בהנחה שכ"א מהקריאות הרקורסיביות היא
).
.
.
[עריכה] תשובה
[עריכה] א'
נתבונן בפסוודו-קוד עם memoization:
Min-Time(i, j) 1 if M[i][j] != Nil 2 return M[i][j] 3 if i == j 4 M[i][j] = 0 5 return 0 6 min-time = ∞ 7 for k in [i, ..., j - 1] 8 guess = Min-Time(i, k) + Min-Time(k + 1, j) + PQR(i, k, j) 9 if guess M[i][j] < min-time 10 M[i][j] = min-time = guess 11 return min-time
נשים לב שאם נניח שכל קריאה רקורסיבית אורכת
, אז יש לנו מספר קריאות שכ"א היא
(1-6, 11), ולולאה בת
איטרציות, כאשר כל איטרציה היא
. זמן הריצה, לכן הוא
.
[עריכה] ב'
ההוכחה חוזרת, למעשה, על זו של דג הסלמון.
נצייר את עץ הקריאות הרקורסיביות, תוך לקיחה בחשבון את נושא הmemoization.
הציור אינו מראה את כל צמתי הקריאה מטעמי נוחות. הצומת
קורא לצומת
, בעקיפין לצמתים
ו
, ולעוד צמתים. הקווים המקווקווים מציינים שהקריאות אינן בהכרח ישירות.
חלק מהצמתים מודגשים, וחלק אינם:
- צומת מודגש אם הוא מתאר קריאה "רצינית", כלומר אחת שבה החלק העיקרי בפונקציה מתבצע (שורות 3-11 ב
Min-Time). - צומת אינו מודגש אם הוא מתאר קריאה "פושטית", בה לכל היותר רק חלק הmemoization מתבצע (שורות 1-2 ב
Min-Time).
נשים לב לנקודות הבאות:
- אם צומת לא מודגש, אז אין לו ילדים, ובנוסף, הוא
(מדוע?) - כל צומת מופיע מודגש פעם אחת (מדוע?)
ננתח כעת בשלבים, לפי הרעיון הבא. בכל שלב:
- ניקח צומת
מודגש שאין לו ילדים מודגשים. - ננתח את זמן הריצה שלו, ונרשום את התוצאה בצד. נשים לב שאם לצומת אין ילדים מודגשים, אז כל קריאה רקורסיבית שלו היא אכן
(מפני שזו איננה ממש קריאה רקורסיבית). זמן הריצה שלו, לכן, הוא אכן
. - נמיר אותו בצומת לא מודגש, מפני שניתחנו כבר את חלק הקוד המסובך בו (ורשמנו אותו בצד). כל שנותר הוא הקריאה לפונקציה, והיא לוקחת
.
נתחיל בצומת
המודגש. לצומת זה אין כלל ילדים, ולכן זמן הקריאה הוא בבירור
. נרשום בצד שעד כה הקריאות שעברנו עליהן עלו
, ונחליף את הצומת
בצומת לא מודגש. זה מסמן את העובדה שניתחנו כבר את החלק "עם הבשר" בצומת
, וכל שנותר הוא
.
נחזור על אותה הפעולה עבור הצומת
.
כנ"ל לגבי הצומת
.
כעת נתבונן בצומת
בשלב בו כל ילדיו אינם מודגשים. כל קריאה רקורסיבית של צומת זה כוללת שני מחירים:
- עצם הקריאה לפונקציה, שעולה

- זמן הקריאה האמיתי, שאותו כבר חייבנו (ורשמנו בצד), ולכן אין צורך לחייב אותו כאן שוב
הפועל היוצא הוא שבשלב זה, מותר לנו לחייב את הצומת אך ורק בקריאות רקורסיביות שעלות כל אחת מהן היא
. זה בדיוק
.
קל להשתכנע שבשלב בו נחליף את צומת הראש בצומת לא מודגש, רשמנו בצד את כל האיברים מהצורה
, כאשר
ו
. בצורה אחרת, מה שרשום בצד הוא
.
[עריכה] ג'
נשלב את שני הסעיפים הקודמים:
.
כבר ראינו כיצד לפשט טור כפול זה בתרגילים בסדרי גדילה.
[עריכה] נוסע מתמיד ביטוני אויקלידי
[עריכה] שאלה
|
הגדרה: (הנוסע המתמיד האוקלידי) נתונות |
|
כדאי לדעת: לבעיית הנוסע המתמיד האוקלידי אין אלגוריתם יעיל ידוע - כל האלגוריתמים הידועים עובדים בזמן אקספוננציאלי. למרות שהדבר לא הוכח, יש סיבות המרמזות לכך שלא ייתכן אלגוריתם יעיל לבעיה זו. |
שאלה זו דנה בגרסה קלה יותר של הבעיה.
|
הגדרה: (מסלול ביטוני) מסלול הוא ביטוני אם הוא מורכב משני תתי מסלולים: בראשון נעים בכיוון ימין, ובשני נעים מימין חזרה לכוון שמאל. |
|
דוגמה: בתרשים הבא, A מראה מישור בעל בתרשים הבא, A וB מראים כל אחד מסלול ביטוני אפשרי. |
בשאלה זו עליך למצוא אלגוריתם למציאת המסלול הביטוני האוקלידי הזול ביותר לכל קבוצת נקודות במרחב: אנא תאר אלגוריתם יעיל המוצא את עלות המסלול הנמוכה ביותר.
[עריכה] תשובה
[עריכה] מספר סימונים ואבחנה פשוטה
ראשית מספר סימונים:
- כפי שצוין בשאלה, נגדיר את אורך
כ
. - נסמן את הנקודות שבמערך כ
. כאמור, אנו מניחים שהנקודות כבר ממוינות כך ש
. - נסמן את המרחק בין שתי נקודות
ו
כ
. - נאמר ששני מסלולים שקולים אם אחד מתקבל מהשני ע"י החלפת כווני החיצים.
|
דוגמה: בתרשים הבא, A וB מראים מסלולים שקולים. |
המשפט הבא נובע בקלות מהגדרת המרחק האוקלידי.
|
משפט: אם שני מסלולים שקולים, אז מחירם זהה. |
[עריכה] בעיה דומה לא-מעגלית
נגדיר את
(
) כעלות המסלול הזול ביותר שמקיים את התנאים הבאים:
- המסלול מתחיל ב
וממשיך כלפי שמאל עד ל
. - כשהמסלול מגיע (מכוון ימין) ל
, הוא מסתובב ימינה, וממשיך עד ל
שם הוא מסתיים. - המסלול עובר דרך כל אחד מהנקודות
בדיוק פעם אחת.
|
שימו לב: המסלול שעליו דיברנו זה עתה אינו מעגלי. הוא מתחיל ב |
אף על פי שהמסלול אינו בדיוק מהסוג שעליו מדברת השאלה, קל לחשבו ביעילות, וניתן להשתמש בו כדי לפתור את השאלה. נראה זאת כעת.
[עריכה] הקשר לבעיה המקורית (המעגלית)
|
משפט: מחיר המסלול הביטוני הזול ביותר הוא |
הוכחה: במסלול המעגלי הקצר ביותר, נסמן כ
את הנקודה הימנית ביותר לפני
שנמצאת בחלק המסלול לכוון ימין (ראה התרשים הבא). המסלול, לכן, מורכב משלושה חלקים:
- מ
ימינה ל
- מ
ימינה ישירות ל
- מ
שמאלה חזרה ל
עלות הקטע 2 היא בדיוק
. סכום עלויות הקטע 1 ו3 הוא בדיוק
.
[עריכה] נוסחת נסיגה לבעיה הלא-מעגלית
|
משפט:
|
הוכחה: נשקול את כל המקרים האפשריים:
הוא אורך המסלול הזול ביותר שעובר שמאלה מ
ל
, מ
פונה ימינה, אינו מגיע לאף נקודה אחרת מכוון ימין, ומכסה את כל הנקודות משמאלה ל
. לכן, בהכרח, מדובר במסלול שעובר נקודה אחרי נקודה מ
ל
.
הוא אורך המסלול הזול ביותר שעובר שמאלה מ
ל
, מ
ימינה ל
, ומכסה את כל הנקודות משמאלה ל
. נסמן כ
את הנקודה שאליה עובר המסלול ישירות מ
; A בתרשים הבא מציג זאת.
היות שהוכחנו למעלה שלמסלולים שקולים יש מחיר זהה, נוכל לטעון שB בתרשים הקודם בעל אותו מחיר כA.
הוא אורך המסלול הזול ביותר שעובר שמאלה מ
ל
, מ
ימינה ל
, ומכסה את כל הנקודות משמאלה ל
. היות ש
, המסלול חייב להתחיל במעבר ישיר (שמאלה) מ
ל
.
[עריכה] פסוודו-קוד
להלן פסוודו-קוד המשתמש ברעיון זה:
IJ-Length(i, j) 1 if j == 1 2 ij-length = 0 3 for k in [1, ..., i - 1] 4 ij-length = ij-length + D(k, k + 1) 5 else if j == i - 1 6 ij-length = ∞ 7 for k in [1, ..., i - 2] 8 guess = IJ-Length(i - 1, k) + D(k, i) 9 if ij-length > guess 10 ij-length = guess 11 else 12 ij-length = IJ-Length(i - 1, j) + D(i - 1, i) 13 return ij-length Min-Bitonic-Euclidean() 1 if n == 1 2 return 0 3 min = ∞ 4 for j in [1, ..., n - 1] 5 guess = IJ-Length(n, j) + D(j, n) 6 if min < guess 7 min = guess 8 return min D(P, P') 1 return √((P'.x - P.x)^2 + (P'.y - P.y)^2)
להלן הסבר לפסוודו-קוד.
IJ-Length(i, j)מממש את נוסחת הנסיגה שראינו מקודם לבעיה הלא-מעגלית.Min-Bitonic-Euclidean()מוצא את הנקודה
הימנית ביותר כפי שראינו מקודם בקשר בין הבעיה המעגלית והבעיה הלא-מעגלית.D(P, P')היא פונקציית עזר המוצאת את המרחק בין שתי נקודות.
[עריכה] תזכור (Memoization)
כרגיל, פשוט נוסיף מבנה נתונים כדי לזכור את הדברים שאותם כבר פתרנו. כאן, בפרט, יש שתי דרגות חופש, ולכן נשתמש במטריצה גלובלית M.
1 M = Make-Matrix(n, n) 2 for i in [1, ..., n] 3 for j in [1, ..., n] 4 M[i][j] = Nil IJ-Length(i, j) 1 if M[i][j] == Nil 2 if j == 1 3 M[i][j] = 0 4 for k in [1, ..., i - 1] 5 M[i][j] = M[i][j] + D(k, k + 1) 6 else if j == i - 1 7 M[i][j] = ∞ 8 for k in [1, ..., i - 2] 9 guess = IJ-Length(i - 1, k) + D(k, i) 10 if M[i][j] > guess 11 M[i][j] = guess 12 else 13 M[i][j] = IJ-Length(i - 1, j) + D(i - 1, i) 14 return M[i][j]
[עריכה] ניתוח סיבוכיות
הסיבוכיות היא
:
- איתחול המטריצה הוא
. - נסמן כ
את זמן הריצה של IJ-Length(i, j), בהנחה שכל אחת מהקריאות הרקורסיביות היא
. אפשר לראות ש
, כל עוד
, ואחרת
. קל לראות שסך כל ריצת הפונקציה הוא
.
[עריכה] רשת חומוסיות
[עריכה] שאלה
רשת החומוסיות מק-לאפה מתכננת להקים סניפים רבים לאורך רחוב איבן-גבירול בתל אביב.
מציינים את מספרי הבתים ברחוב בהם יש מקום פנוי להקמת סניף חדש. הרשת שוקלת היכן להקים את הסניפים החדשים:
- מסקר שוק, הרשת מעריכה שבבית מספר
רווחיה הצפויים יהיו
(
הוא מספר חיובי כלשהו). - הרשת אינה מוכנה להקים שני סניפים שמרחקם זה מזה הוא פחות מ
(
הוא מספר שלם חיובי כלשהו).
|
דוגמה: אם |
אנא כתוב אלגוריתם יעיל המקבל את המערך M (המכיל את מספרי הבתים), את המערך P (המכיל את הרווחים הצפויים), ואת המספר k (המציין את מגבלת המרחק), ומדפיס את מספרי הבתים שהקמת הסניפים בהם צפוייה למקסם את הרווחיות. אנא הוכח נכונות ונתח סיבוכיות.
[עריכה] תשובה
ראשית נניח שהמערך Mממויין, או, לחלופין, נמיין אותו בעזרת מיון מיזוג בסיבוכיות
.
נגדיר כ
את מירב הרווח שאפשר להרוויח מפתיחת סניפים הלקוחים מתוך
הבתים הראשונים.
|
משפט: לפי הגדרת
|
הוכחה: (2) אם
, אז ברור שהפתרון הרווחי ביותר הוא זה שבו בוחרים את הבית הראשון (והיחידי האפשרי לבחירה). אם
, אז או שבוחרים את בית
, או שלא. במקרה הראשון, מרוויחים
מהבחירה בבית, אך לפניו אסור לבחור באף בית בעל מיקום שהפרשו מ
קטן מ
. במקרה השני, הרווח הוא בדיוק הרווח כשמותר לבחור מבין
הבתים הראשונים.
להלן פסוודו-קוד המממש נוסחה זו.
Max-Profit(M, P, k, i) 1 if i == 0 2 return 0 3 if i == 1 4 return P[1] 5 guess-without = Max-Profit(i - 1) 6 guess-with = P[i] 7 if m[i] > k 8 j = Find-Index(M, M[i] - k) 9 guess-with = guess-with + Max-Profit(M, P, k, j) 10 if guess-with > guess-without 11 return guess-with 12 else 13 return guess-without
חשוב רק לשים לב לקריאה לפונקציה Find-Index ב8. פונקציה זו מוצאת את האינדקס הגדול ביותר בMשקטן מM[i] - k. היות שMממויין, נוכל לעשות זאת על ידי שינוי קטן בחיפוש בינרי. נוסיף גם Memoization:
1 MP = Make-Array(n) 2 for i in [1, ..., n] 3 MP[i] = Nil Max-Profit(M, P, k, i) 1 if i == 0 2 return 0 3 if MP[i] != Nil 4 return MP[i] 5 if i == 1 6 MP[1] = P[1] 7 return P[1] 8 guess-without = Max-Profit(i - 1) 9 guess-with = P[i] 10 if m[i] > k 11 j = Find-Index(M, M[i] - k) 12 guess-with = guess-with + Max-Profit(M, P, k, j) 13 if guess-with > guess-without 14 MP[i] = guess-with 15 return guess-with 16 else 17 MP[i] = guess-without 18 return guess-without
כעת נוסיף גם את הדפסת האיברים:
1 MP = Make-Array(n) 2 Used = Make-Array(n) 3 for i in [1, ..., n] 4 MP[i] = Nil Max-Profit(M, P, k, i) 1 if i == 0 2 return 0 3 if MP[i] != Nil 4 return MP[i] 5 if i == 1 6 MP[1] = P[1] 7 Used[1] = True 8 return P[1] 9 guess-without = Max-Profit(i - 1) 10 guess-with = P[i] 11 if m[i] > k 12 j = Find-Index(M, M[i] - k) 13 guess-with = guess-with + Max-Profit(M, P, k, j) 14 if guess-with > guess-without 15 MP[i] = guess-with 16 Used[i] = True 17 return guess-with 18 else 19 MP[i] = guess-without 20 Used[i] = False 21 return guess-without
המערך Used מתאר האם השתמשנו באיבר ה
או לא. נשתמש בו ע"י קריאה לPrint-Nums(k, n), כאשר Print-Nums מוגדרת כך:
Print-Nums(k, i) 1 if i &lt 1 2 return 3 if Used[i] 4 j = Find-Index(M, M[i] - k) 5 Print-Nums(k, j) 6 else 7 Print-Nums(k, i - 1) 8 if Used[i] 9 Print(i)
נותר רק לנתח את הסיבוכיות.
השורה j ← Find-Index(M, M[i] - k)אורכת
. אם נגדיר כ
את זמן הקריאה לMax-Profit(M, P, k, i)בהנחה שכל קריאה רקורסיבית היא
, אז
, והסיבוכיות היא
. מצד שני, מיון המערך הוא
, ולכן נקבל
.
[עריכה] תכנון פרוייקטים
[עריכה] שאלה
בבעיית "תכנון הפרוייקטים" יש
פרוייקטים ו
עובדות. צריך להחליט כמה עובדות יש להקצות לכל אחד מהפרוייקטים.
לכל פרוייקט
ומספר עובדות
,
הוא מספר המייצג את איכות ביצוע הפרוייקט ה
i אם ישבצו לו
עובדות. הנח:
פונקציה לא שלילית (אין איכות שלילית).
מונוטונית לא-יורדת ב
(הוספת עובדות לפרוייקט אינה מורידה מאיכותו).- ניתן להקצות 0 עובדות לפרוייקט.
- כל עובדת מוקצת לפרוייקט אחד בדיוק.
לכל פרוייקט
יש "חשיבות" לא-שלילית
. אנו רוצים למקסם את סך האיכויות המשוקללות. במילים אחרות, אנו רוצים למקסם את
.
אנא הצע אלגוריתם יעיל לפתרון הבעיה, כלומר אלגוריתם שימצא את הסכום הנ"ל המקסימלי וכן ידפיס את אופן הקצאת העובדות לכל פרוייקט. הנח שq(i, j) וh(i) הן פונקציות נתונות לך, שעלות קריאה להן היא
.
[עריכה] תשובה
[עריכה] המבנה הרקורסיבי
נגדיר את
כאיכות המשוקללת הגבוהה ביותר האפשרית כאשר יש
עובדות ו
פרוייקטים.
|
משפט:
|
הוכחה: אם יש פרוייקט יחיד, אז ברור שכדאי להקצות את כל העובדות לו (שהרי נתון שהוספת עובדות אינה יכולה להוריד את איכות הפרוייקט). לכן, אם יש פרוייקט יחיד ו
עובדות, נקבל שאיכותו הטובה ביותר היא
.
נניח שיש
פרוייקטים. אם נקצה
עובדות לפרוייקט ה
, אז איכות הפרוייקט תהיה
, ונוותר עם
עובדות. על פי הגדרתנו,
הוא הטוב ביותר שנוכל להשיג במקרה זה.
[עריכה] פסוודו-קוד
נתרגם את נוסחת הנסיגה הקודמת לפסוודו-קוד.
Max-Value(i, j) 1 if i == 1 2 return h(1) q(1, j) 3 max-value = 0 4 for j' in [0, ..., j] 5 guess = Max-Value(i - 1, j - j') + h(i) q(i, j') 6 if guess > max-value 7 max-value = guess 8 return max-value
[עריכה] הדפסת פרטי הפתרון
נוסיף עוד מטריצה גלובלית A כך שA[i][j] מתארת את מספר העובדות האופטימלי שכדאי להקצות לפרוייקט ה
אם יש
פרוייקטים ו
עובדות.
להלן הקוד המכיל את השינויים הנדרשים:
Max-Value(i, j) 1 if i == 1 2 A[1][j] = j 3 return h(1) q(1, j) 4 max-value = 0 5 for j' in [0, ..., j] 6 guess = Max-Value(i - 1, j - j') + h(i) q(i, j') 7 if guess > max-value 8 A[i][j] = j' 9 max-value = guess 10 return max-value
כעת נשתמש במטריצה כדי להדפיס את האלוקציות האופטימליות:
Print-Assignments(i, j) 1 if i == 0 2 return # The optimal assignment assigns j' workers to project i. 3 j' = A[i][j] 4 Print('Assign ' + j' + ' workers to project ' + i) 5 Print-Assignments(i - 1, j - j')
כך נפעיל את שתי הפונקציות:
1 A = Make-Matrix(k, n) 2 max-value = Max-Value(k, n) 3 Print(max-value) 4 Print-Assignments(k, n)
[עריכה] הוספת תזכור (memoization)
הפסוודו-קוד הרקורסיבי שמימש את הנוסחה הרקורסיבית, פותר אותן בעיות שוב ושוב. נייצר מטריצה גלובלית M שתשמור את התוצאות שקיבלנו, ונאתחל איברי מטריצה זו לNil. הפסוודו-קוד הבא מראה את השימוש במטריצה M. לשם הפשטות, השמטנו את חלקי הקוד המעדכנים את המטריצה המשמשת להדפסת ההשמות.
Max-Value(i, j) 1 if i == 1 2 M[1][j] = h(1) q(1, j) 3 return h(1) q(1, j) 4 max-value = 0 5 for j' in [0, ..., j] 6 guess = Max-Value(i - 1, j - j') + h(i) q(i, j') 7 if guess > max-value 8 M[i][j] = guess 9 max-value = guess 10 return max-value
[עריכה] ניתוח סיבוכיות
כרגיל, נגדיר את
כזמן הריצה של Max-Value(i, j) כאשר כל קריאה רקורסיבית היא
. קל לראות שמתקיים 
כרגיל, נמצא את זמן הריצה האמיתי של הקריאות הרקורסיביות כך:
![\displaystyle \sum_{i = 1}^k \sum_{j = 1}^n[T'(i, j)] =](http://upload.wikimedia.org/math/a/0/7/a07e5745b55a1b27864f6aa1b576ff17.png)
![\displaystyle \sum_{i = 1}^k \sum_{j = 1}^n[\Theta(j)] =](http://upload.wikimedia.org/math/6/0/1/60154e1ca933fd59b3b03fca013364db.png)
![\displaystyle \sum_{i = 1}^k [\Theta(n^2)] =](http://upload.wikimedia.org/math/0/3/5/035537eb386110deb8397dac322c864e.png)

יש לקחת בחשבון גם את זמן הריצה של אתחול המטריצה (M ואת הדפסת פרטי הפתרון:
- אתחול המטריצה אורך

- הדפסת הפרטים אורכת

הסיבוכיות הכוללת, לכן, היא

[עריכה] פתרון תת-מחרוזת עולה ארוכה ביותר ע"י תת-מחרוזת משותפת ארוכה ביותר
[עריכה] שאלה
נניח שנתנה לך פונקציה יעילה LCS(X, Y) הפותרת את בעיית תת-המחרוזת המשותפת הארוכה ביותר. אנא הצע אלגוריתם יעיל Convert(X) המקבל מחרוזת, ומחזיר מחרוזת שתקיים תמיד שLCS(X, Convert(X)) הוא אורך תת-המחרוזת העולה הארוכה ביותר של X.
אנא הוכח את נכונות תשובתך, ונתח את סיבוכיות הפונקציה Convert במונחי ארכו של X.
[עריכה] תשובה
הפונקציה Convert(X) פשוט תחזיר את המערך ממויין בסדר עולה. (אם נשתמש במיון מיזוג, אז הסיבוכיות תהיה
).
נניח שהמערך המקורי הוא
, והמערך הממויין הוא
.
כל תת-מחרוזת עולה של
, נניח
, היא תת-מחרוזת משותפת ל
ו
: היא בהכרח תת-מחרוזת של
לפי ההגדרה, והיא תת-מחרוזת של
מפני שהיא מחרוזת עולה שאיבריה הם איברי
.
לכן, בפרט, תת-המחרוזת המשותפת הארוכה ביותר היא גם בהכרח תת-המחרוזת העולה הארוכה ביותר של
.
[עריכה] הדפסה יפה
[עריכה] שאלה
ברצוננו להדפיס פסקת טקסט בצורה יפה.
נתונות לנו
מילים, Word[1], Word[2], ..., Word[n]. עלינו להחליט היכן לסיים כל שורה בפסקה, לפי הכללים הבאים:
- בכל שורת טקסט יש מקום ל
תווים (אין מילה בעלת יותר מ
תווים). - יש להדפיס רווח יחיד בין כל שתי מילים באותה שורה.
|
דוגמה: נניח ש If they have no rice, let them eat ladoo. נוכל לבנות פסקה כך: If they have no rice, let them eat ladoo. אך לא כך: If they have no rice, let them eat ladoo. (בדוגמה השניה יש יותר מ20 תווים בשורה הראשונה). |
אם בשורה יש
תווים (כולל הרווחים בין המילים), אז יופי השורה הוא
. יופי הפסקה מוגדר כסכום היופי של השורות שלו, למעט השורה האחרונה.
|
דוגמה: בפסקה If they have no rice, let them eat ladoo. יש 15 תווים בשורה הראשונה, ו14 תווים בשורה השניה. יופי הפסקה, לכן הוא |
אנא מצא אלגוריתם יעיל למציאת הפסקה היפה ביותר. הנח ש
הוא משתנה גלובלי נתון, ושקיימת פונקציה Length המקבלת מספר
ומחזירה את מספר התווים במילה ה
.
[עריכה] תשובה
פרק זה לוקה בחסר. אתם מוזמנים לתרום לוויקיספר ולהשלים אותו. ראו פירוט בדף השיחה.
של מספרים שונים זה מזה.
היא תת-סידרה עולה של
כך ש
לכל
. אם נקבע
, אז
תהיה
. נקבע, לכן,
. אז:
היא תת-סידרה חברותית.
היא תת-סידרה בדלנית.
היא תת-סידרה חברותית.
היא תת-סידרה בדלנית.
היא תת-סידרה בדלנית.
: זו תת-סידרה בדלנית של
, ואין תת-סידרה בדלנית אחרת שמכפלת איבריה גדולה מ35.
.
.
.
, וB מראה שתי מידות בעלת ערך: העליונה בעלת המידות
, והתחתונה בעלת המידות
. נניח שהמידה העליונה שווה 35 שקל, והתחתונה שווה 2 שקלים.





הוא הערך המוחזר מהפונקציה
, מה שכתוב כאן הינו פשוט 

,
, ו
הן שלוש מחרוזות, אז:
כך ש
לכל
ו
הן מחרוזות.
ו
, אז:
, אז
(כלומר
הוא אורך הLCS של
.
.
.
ו
(ראה A בתרשים הבא). אפשר לשבור את הגשר ב4 אפנים:
.
.
.
,
, ו
. ארבע האפשרויות שראינו מקודם יעלו כך:
.
.
.
.



נקודות; B מראה את המסלול הקצר ביותר, אך המסלול אינו ביטוני.


.
.
, לכל
.
, לכל
, לכל
.

והרשת בוחרת להקים סניף בבית מספר 135, אסור לה להקים סניף בבית מספר 143, שכן
.
.

, אם
, אם
, והמילים הן
.