1 / 41

תרגול מס' 7: Memoization Quicksort תרגילים מתקדמים ברקורסיה

תרגול מס' 7: Memoization Quicksort תרגילים מתקדמים ברקורסיה. סדרת פיבונצ'י. לאונדרדו מפיזה הידוע בשמות ליאונדרו פיזאנו, ליאונרדו בונאצ'י, או בפשטות פיבונצ'י נחשב בעיני רבים לגדול המתמטיקאים של ימי הביניים. על שמו קרויה סדרת המספרים הבאה (בניגוד לדעה הרווחת הוא לא המציא אותה, רק השתמש בה):

luann
Download Presentation

תרגול מס' 7: Memoization Quicksort תרגילים מתקדמים ברקורסיה

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. תרגול מס' 7: MemoizationQuicksortתרגילים מתקדמים ברקורסיה

  2. סדרת פיבונצ'י • לאונדרדו מפיזה הידוע בשמות ליאונדרו פיזאנו, ליאונרדו בונאצ'י, או בפשטות פיבונצ'י נחשב בעיני רבים לגדול המתמטיקאים של ימי הביניים. • על שמו קרויה סדרת המספרים הבאה (בניגוד לדעה הרווחת הוא לא המציא אותה, רק השתמש בה): • סדרה זו מופיעה בטבע בצורות מפתיעות: http://en.wikipedia.org/wiki/Fibonacci_number

  3. חישוב המספר ה-n בסדרה: publicclass Fib { publicstaticint fib(int n){ int ans; if (n==0) ans = 0; elseif (n==1) ans = 1; else ans = fib(n-1)+fib(n-2); return ans; } publicstaticvoid main(String[] args){ int n = 20; System.out.println("fib ("+n+") = "+fib(n)); } } רקורסית זנב? לא!

  4. fib(2) fib(1) fib(0) fib(5) fib(4) fib(3) fib(3) fib(2) fib(2) fib(1) fib(1) fib(1) fib(0) fib(1) fib(0) Memoization (without ‘r’) ברישום הבא, מובא עץ הקריאות לפונקציה כאשר ניתן לראות כי ישנם מספר חישובים שמתבצעים יותר מפעם אחת (אלו המודגשים בצבע):

  5. ניתן לחסוך את החישובים המיותרים ע"י שמירת תוצאות של חישובים שבוצעו: שמירה של תוצאות ביניים ב lookup tables כך שחישוב יתבצע פעם אחת לכל היותר. לכל ערך שנרצה לחשב, נבדוק תחילה האם ערך זה חושב, כלומר, האם יש עבורו ערך מתאים ב lookup table. אם כן, נשתמש בערך זה. אחרת, נבצע את החישוב ונשמור את התוצאה בכניסה המתאימה בטבלה. הערכים נשמרים בטבלה לפי ה input. כלומר, לכל קלט אפשרי יש כניסה מתאימה בטבלה.

  6. דוגמא בכדי ליעל את Fib נשתמש בטכניקת ה memoization לשמירת תוצאות החישובים שבוצעו. נראה תוכנית המחשבת את האיבר ה n-י בסדרת פיבונאצ'י, אשר משתמשת בטכניקת ה memoization השומרת תוצאות של חישובים רקורסיביים. במקרה זה ה lookup table הינה מערך חד מימדי, כך שתא בעל אינדקס במערך, מכיל את הערך . באם ערך זה עדיין לא חושב, יכיל תא זה את הערך 1-.

  7. publicclass FibMemo{ publicstaticvoid main(String[] args){ int n = 20; System.out.println("fibMemo("+n+") = "+fibMemo(n)); } publicstaticint fibMemo(int n){ int[] lookupTable = newint[n+1]; for (int i=0; i < lookupTable.length ; i=i+1) lookupTable[i] = -1; //EMPTY return fibMemo(lookupTable, n); } publicstaticint fibMemo(int[] lookupTable, int n){ if (n==0) lookupTable[n]= 0; if (n==1) lookupTable[n]= 1; if (lookupTable[n]==-1)//EMPTY lookupTable[n] = fibMemo(lookupTable,n-1) + fibMemo(lookupTable,n-2); return lookupTable[n]; } } והפתרון:

  8. Divide-and-Conquer • טכניקת ה Divide-and-Conquer מבוססת על רעיון דומה לפתרון רקורסיבי של בעיות: • חלק את הבעיה המקורית לתתי בעיות קטנות (שניים או יותר) - Divide • פתור כל תת בעיה – Conquer • צרף את תתי הפתרונות לפתרון לבעיה המקורית. • ישנם אלגוריתמים רבים המתוכננים לפי עיקרון זה, עליהם נמנים האלגוריתמים הבאים: • חיפוש בינארי • Mergesort • Quicksort

  9. מיון מהיר (Quick Sort) • חלוקת המערך לשני חלקים לפי ציר (pivot) שנבחר מחדש בכל שלב של הרקורסיה ומיון רקורסיבי של כל צד • הציר שנבחר הינו הערכה (ניחוש) של החציון של המספרים במערך • תזכורת: חציון הוא מדד למיקום המרכז של קבוצת נתונים מספריים. לדוגמא: החציון של קבוצת המספרים 1, 2, 22, 7, 19, 8, 16 הוא 8 • נבחר בקוד שלנו את האיבר הראשון כציר.

  10. מיון מהיר (Quicksort) - דוגמא

  11. Quicksort Example • Example: • http://www.cise.ufl.edu/~ddd/cis3020/summer-97/lectures/lec17/sld001.htm • www.cs.auckland.ac.nz/software/AlgAnim/Java/q_sort/tqs_new.html • Video: • http://www.youtube.com/watch?v=2HjspVV0jK4 • http://www.youtube.com/watch?v=o2dm4X-t8L0& • http://www.youtube.com/watch?v=FSyr8o8jjwM

  12. מיון מהיר – הקוד import java.util.Scanner; publicclass QuickSort { publicstaticvoid main(String[] args){ Scanner sc = new Scanner(System.in); System.out.println("Enter number of elements to sort:"); int n = sc.nextInt(); int[] arr = newint[n]; // Initializes arr with random numbers // in [0..10*N) initRandomArray(arr); System.out.println("The input array:"); printArray(arr); quicksort(arr); System.out.println("The sorted array:"); printArray(arr); } //…. continued

  13. מיון מהיר – הקוד (המשך) publicstaticvoid swap(int[] arr, int i, int j){ // swap arr[i] and arr[j] int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } publicstaticvoid initRandomArray(int[] arr){// shuffle the array arr int n = arr.length; for (int i = 0; i < n; i++) { arr[i] = (int) (Math.random() * 10 * n); } } publicstaticvoid printArray (int[] arr) { for (int i=0; i<arr.length; i=i+1) System.out.print (arr[i]+" "); System.out.println(); }

  14. מיון מהיר – הקוד (המשך) publicstaticvoid quicksort(int[] arr){ quicksort(arr, 0, arr.length-1); } publicstaticvoid quicksort(int[] arr, int start, int end){ if (start<end){ int i = partition(arr, start, end); quicksort(arr, start, i-1); quicksort(arr, i+1, end); } }

  15. מיון מהיר – הקוד (המשך) publicstaticint partition(int[] arr, int start, int end){ int pivot = arr[start]; int i = start; int j = end; while(i<j){ while(i<end && arr[i] <= pivot) i=i+1; while(arr[j] > pivot) j=j-1; if (i<j) swap(arr,i,j); } swap(arr,start,j); return j; }

  16. דוגמה 2 • בהינתן מספר מספר n0 ≤, נרצה להחזיר מערך של 2n המחרוזות באורך n המורכבות מאפסים ואחדים.סדר המחרוזת הוא סדר המניה מאפס ל 2n-1 בבסיס 2. • הפלט עבור n=0: • {“”} • הפלט עבור n=1: • {“0”,”1”} • הפלט n=2: • {“00”,”01”,”10,”11”} • הפלט עבור n=3: • {“000”,”001”,”010,”011”, “100”,”101”,”110,”111”}

  17. השלם את הפונקציה publicstatic String[] binaryNums(int n){ String[] answer; if (n==0){ answer = new String[1]; answer[0]=""; } else{ // // Your code here // } return answer; } 17

  18. הרעיון של הרקורסיה n=3 n=2 n=1 n=0 “”

  19. רעיון של הרקורסיה n=3 n=2 “0” + “” “1” + “” 0 0 1 n=1 “” n=0

  20. רעיון של הרקורסיה n=3 00 01 10 11 n=2 0 1 n=1 “” n=0

  21. רעיון של הרקורסיה n=3 000 001 010 011 100 101 110 111 n=2 00 01 10 11 0 1 n=1 “” n=0

  22. הקוד publicstatic String[] binaryNums(int n){ String[] answer; if (n==0){ answer = new String[1]; answer[0]=""; } else{ String[] prev = binaryNums(n-1); answer = new String[2*prev.length]; for (int i =0;i<prev.length; i = i+1){ answer[i]= "0" + prev[i]; answer[prev.length + i] = "1“+ prev[i]; } } return answer; } 22

  23. הדפסת הפרמוטציות של מחרוזת • פרמוטציה של מחרוזת מוגדרת כמחרוזת המכילה את אותן אותיות, ייתכן שבשינוי סדר. נניח בדוגמה זו שכל האותיות שונות זו מזו. • למשל הפרמוטציות עבור המחרוזת bcd הם: • “bcd" • “bdc" • “cbd“ • “cdb” • “dbc“ • “dcb”

  24. הרעיון של הרקורסיה i = האינדקס שלפיו מחלקים את המחרוזת ““, “a” + “b” + “c” +”d” i=1 i=0 d, “a” + “b” + “c” output: abcd cd, “a” + “b” i=0 cd, “a” + “b” bcd, “a” i=0 bcd, “a” i=0 abcd,”” abcd,””

  25. הרעיון של הרקורסיה ““, “a” + “b” + “c” +”d” i=1 d, “a” + “b” + “c” output: abcd cd, “a” + “b” i=0 i=1 cd, “a” + “b” i=0 bcd, “a” bcd, “a” i=0 abcd,”” abcd,””

  26. הרעיון של הרקורסיה ““, “a” + “b” + “c” +”d” d, “a” + “b” + “c” output: abcd i=1 i=2 cd, “a” + “b” cd, “a” + “b” “”, “a” + “b” + “d” + “c” i=0 c, “a” + “b” + “d” output: abdc bcd, “a” bcd, “a” i=0 abcd,”” abcd,””

  27. הרעיון של הרקורסיה ““, “a” + “b” + “c” +”d” d, “a” + “b” + “c” output: abcd i=1 cd, “a” + “b” cd, “a” + “b” i=1 “”, “a” + “b” + “d” + “c” i=1 c, “a” + “b” + “d” output: abdc bcd, “a” bcd, “a” i=0 abcd,”” abcd,”” bd, “a” + “c” bd, “a” + “c” d, “a” + “c” + “b” output: acbd “”, “a” + “c” + “b” + “d”

  28. הרעיון של הרקורסיה ““, “a” + “b” + “c” +”d” d, “a” + “b” + “c” output: abcd cd, “a” + “b” cd, “a” + “b” “”, “a” + “b” + “d” + “c” i=3 c, “a” + “b” + “d” output: abdc bcd, “a” bcd, “a” abcd,”” abcd,”” bd, “a” + “c” bd, “a” + “c” d, “a” + “c” + “b” . . . bc, “a” + “d” output: acbd “”, “a” + “c” + “b” + “d” . . .

  29. קוד של דוגמה 3: הדפסת הפרמוטציות של מחרוזת public static void perms(String s){ // We call the method perm(s,"") which prints // the empty string followed by each permutation // of s the empty string. perms(s,""); }

  30. קוד של דוגמה 3: הדפסת הפרמוטציות של מחרוזת /** Function prints all the permutation of a string. * Note: assume the string is a set (no duplicate * chars) */ // Prints string acc followed by all permutations of // string s1 publicstaticvoidperms(String s1, String acc){ if (s1.length()==0) System.out.println(acc); else for (int i=0; i<s1.length(); i=i+1) perms(delete(s1, i), acc +s1.charAt(i)); }

  31. פונק' עזר: delete // This function returns the string s with the i-th // character removed publicstatic String delete(String s, int i){ // Assumes that i is a position in the string return s.substring(0,i) + s.substring(i+1,s.length()); }

  32. דוגמה 4 - הרכבת סכום נתון ממשקולות בהינתן מערך משקולות אי-שליליים ומשקל נוסף (משקל סכום), נרצה לבדוק האם ניתן להרכיב מהמשקולות משקל השווה למשקל הסכום הנתון. • דוגמא לקלט:weights={1,7,9,3} Sum = 12במקרה זה הפונקציה תחזיר true כי ניתן לחבר את המשקולות 9 ו 3 ולקבל את הסכום 12. • דוגמא לקלט: weights={1,7,9,3} Sum = 15במקרה זה הפונקציה תחזיר false כי לא ניתן לחבר משקולות לקבלת הסכום 15.

  33. תיאור פתרון • נתבונן באיבר הראשון במערך. ייתכן שהוא ייבחר לקבוצת המשקולות שתרכיב את הפתרון ויתכן שלא. • אם הוא לא ייבחר (להיות כלול בסכום המהווה את משקל המטרה) – אזי נותר לפתור בעיה קטנה יותר והיא האם ניתן להרכיב את הסכום מבין המשקולות שנותרו במערך. • אם הוא ייבחר – אזי נותר לפתור בעיה קטנה יותר והיא האם ניתן להרכיב את הסכום שנותר מבין המשקולות שנותרו במערך. • וכנ"ל לגבי יתר האיברים בצורה רקורסיבית.

  34. תיאור פתרון - המשך • פתרון זה קל להציג כפונקציה רקורסיבית ,boolean calcWeights(int[] weights, int i, int sum ) • הפונקציה מחזירה ערך אמת האם ניתן להרכיב את הסכום מבין קבוצת המשקולות שבתת המערך. • הפרמטרים: • weights – מערך המשקולות • sum – הסכום שיש להרכיב מהמשקולות • i – פרמטר נוסף הנחוץ עבור הרקורסיה. i הוא אינדקס במערך weights ויסמן את האיבר הנוכחי במערך עליו מתבצעת הקריאה הרקורסיבית.

  35. i=0 50: [10,20,30] i=1 40: [20,30] 50: [20,30] i=2 20: [30] 40: [30] i=3 -10 [] 20: [] 10: [] 40: []

  36. האם צריך לחשב ענף ימני? i=0 50: [10,20,30] i=1 40: [20,30] 50: [20,30] 50: [30] i=2 30: [30] i=3 50: [] 0: [] 30: [] 20: []

  37. קוד דוגמה 4: calcWeights • הארגומנט i נחוץ עבור הרקורסיה – אך אינו באמת חלק מהקלט של הבעיה. בקריאה הראשונה ל calcWeights ערכו הוא 0. לכן נוסיף פונקצית מעטפת עם חתימה פשוטה יותר. // An envelope function, without i argument // A simpler signature public static boolean calcWeights(int[] weights, int sum) { return calcWeights(weights , 0, sum); }

  38. פתרון (המשך) publicstaticboolean calcWeights(int[] weights, int i, int sum) { boolean res = false; if (sum == 0) res = true; elseif (i >= weights.length) res = false; else res = calcWeights(weights,i+1,sum-weights[i]) || calcWeights(weights, i + 1, sum); return res; }

  39. דוגמה אחרונה • הפונקציה void subsetsSum(int[] weights, int sum) מקבלת מערך weights של משקולות (ערכים שלמים חיוביים) ומשקל שלם sum חיובי ומדפיסה את כל תתי הקבוצות של איברי המערך weights שסכומם sum. • דוגמא עבור weights={1,2,3,4,5} ו sum=10, הפונקציה תדפיס: • 1 2 3 4 • 1 4 5 • 2 3 5

  40. דוגמה אחרונה • שאלה זו דומה מאוד לשאלת הרכבת סכום נתון ממשקולות. נסו להשלים את הפונקציות לבד. public static void subsetsSum(int[] weights, int sum){ subsetsSum(________השלימו את החסר___); } public static void subsetsSum( ){ השלימו את החסר }

  41. דוגמה אחרונה: פתרון publicstaticvoid subsetsSum(int[] weights, int sum){ subsetsSum(weights, sum,0,””); } publicstaticvoid subsetsSum(int[] weights, int sum, int index, String acc){ if(sum == 0) System.out.println(acc); elseif (sum > 0 & index < weights.length){ subsetsSum(weights,sum-weights[index], index+1,acc + weights[index] + ' '); subsetsSum(weights,sum,index+1,acc); } } 41

More Related