1 / 42

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

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

donnan
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. Memoization • ניתן לחסוך את החישובים המיותרים ע"י שמירת תוצאות של חישובים שבוצעו: • נשמור תוצאות ביניים ב lookup tables כך שחישוב יתבצע פעם אחת לכל היותר. • לכל ערך שנרצה לחשב, נבדוק תחילה האם ערך זה חושב, כלומר, האם יש עבורו ערך מתאים ב lookup table. אם כן, נשתמש בערך זה. אחרת, נבצע את החישוב ונשמור את התוצאה בכניסה המתאימה בטבלה. • הערכים נשמרים בטבלה לפי ה input. כלומר, לכל קלט אפשרי יש כניסה מתאימה בטבלה.

  6. דוגמא

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

  8. 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 כעת עץ הקריאות לפונקציה יראה כך:

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

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

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

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

  13. Quicksort Examples Applets: • 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 Videos: • http://www.youtube.com/watch?v=o2dm4X-t8L0 • http://www.youtube.com/watch?v=2HjspVV0jK4

  14. מיון מהיר – הקוד publicclass QuickSort { //…. 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. מיון מהיר – partition 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) //scan upwards i = i+1; while (arr[j] > pivot) //scan downwards j = j-1; if (i < j) swap(arr,i,j); } swap(arr,start,j); //put pivot in place return j; }

  16. דוגמאת ריצה של partition pivot pivot Swap! j i i i j j end i j start j j i

  17. מיון מהיר – פונקציות עזר 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(); }

  18. מיון מהיר – הקוד (המשך) 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

  19. הדפסת מחרוזות בינאריות באורך n • בהינתן מספר מיספר n0 ≤, נרצה להחזיר מערך של 2n המחרוזות באורך n המורכבות מאפסים ואחדים.סדר המחרוזות הוא סדר המניה מ 0 ל 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”}

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

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

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

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

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

  25. הקוד 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; } 25

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

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

  28. הרעיון של הרקורסיה ““, “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,””

  29. הרעיון של הרקורסיה ““, “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,””

  30. הרעיון של הרקורסיה ““, “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”

  31. הרעיון של הרקורסיה ““, “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” . . .

  32. קוד הדפסת הפרמוטציות של מחרוזת public static void perms(String s){ // We call the method perm(s,"") which prints the // empty string followed by each permutation of s. perms(s,""); } // 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)); }

  33. פונקציית עזר 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()); }

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

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

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

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

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

  39. (המשך) 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; }

  40. דוגמה אחרונה • הפונקציה 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

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

  42. דוגמה אחרונה: פתרון 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); } } 42

More Related