1 / 49

קורס תכנות

קורס תכנות. שיעור עשירי: מיונים, חיפושים, וקצת סיבוכיות חישוב. משתנים, מצביעים וכתובות. משתנה מוגדר ע"י type name משתנה מצביע מוגדר ע"י type * name & - אופרטור שמחזיר כתובת של משתנה * - אופרטור שמקבל מצביע מחזיר הערך. מערכים ומצביעים. מצביע מכיל כתובת, שם המערך הוא כתובת

aidan-neal
Download Presentation

קורס תכנות

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. קורס תכנות שיעור עשירי: מיונים, חיפושים, וקצת סיבוכיות חישוב

  2. משתנים, מצביעים וכתובות • משתנה מוגדר ע"י type name • משתנה מצביע מוגדר ע"י type * name • &- אופרטור שמחזיר כתובת של משתנה • *- אופרטור שמקבל מצביע מחזיר הערך

  3. מערכים ומצביעים • מצביע מכיל כתובת, שם המערך הוא כתובת • ניתן לבצע השמה בין המערך ומצביע • לאחר ההשמה ניתן להשתמש במצביע כאילו היה שם המערך • ניתן להשתמש במערך כאילו היה מצביע int *ptr; int array[10]; ptr = array; ptr[2] = 25; *array = 4;

  4. למה צריך מצביעים?✝ • משתנה • ניתן לשנות את ערכו אך ורק בפונקציה בה הוגדר • מצביע • נותן לנו גישה אל משתנים שהוגדרו בפונקציות אחרות • דוגמא:הפונקציה scanf משנה ערכים של משתנים • פונקציה יכולה לחשב מספר ערכים ולשמור אותם בכתובות שהועברו אליה (כמו להחזיר מספר ערכים מפונקציה) ✝עוד סיבות בהמשך

  5. 6500 D i n g D o n g \0 str 6500 res הפונקציה strstr char str[] = “Ding Dong”; char *res = strstr(str,”Don”); printf(“res=%p\n”,res); printf(“res=%s\n”,res); res=6505 res=Dong

  6. דוגמא – כמה פעמים מופיעה מילה בשיר int main() { const char rhyme[] = "Humpty Dumpty sat on a wall,\n " "Humpty Dumpty had a great fall.\n " "All the king's horses,\n" "And all the king's men,\n" "Couldn't put Humpty together again.\n"; const char humpty[] = "Humpty"; char *ptr = NULL; int count = 0; for (ptr = strstr(rhyme, humpty); ptr != NULL; ptr = strstr(ptr + 1, humpty)) { count++; } printf("The string %s appears %d times\n", humpty, count); return 0; {

  7. הנושאים להיום סוגי מידע החשיבות של גישה מהירה למידע איך ניתן לעשות זאת? עיבוד מוקדם של המידע יאפשר גישה מהירה למידע שמעניין אותנו. מבנה הנתונים הבסיסי ביותר: מערך עיבוד מוקדם  מיון גישה מהירה  חיפוש סיבוכיות זמן ריצה 7

  8. סוגי מידע קיימים כ-20,000,000,000 אתרים באינטרנט... 8

  9. 2 5 1 8 9 13 67 מיון • מערך ממויין הוא מערך שערכיו מסודרים בסדר עולה/יורד. • אחד השימושים הכי נפוצים במערכים הוא לצורך מיון של מספרים. • דוגמא למערך ממויין:

  10. למה צריך למיין מידע? • כדי למצוא ערך, ומהר! • בהינתן מערך בגודל N נרצה לחפש בו כמה מספרים מסויימים כדי לדעת האם הם נמצאים בו ואיפה? • פתרון נאיבי: בהינתן מספר שברצוננו למצוא, נסרוק את המערך מההתחלה ונחפש את המספר. • הפתרון לא יעיל, כל חיפוש ידרוש בממוצע כ- N/2 פעולות. • פתרון יעיל יותר: • נמיין את המערך פעם אחת ואז ניתן לבצע בו חיפוש הרבה יותר מהר!

  11. חיפוש נאיבי במערך לא ממויין • נרצה לדעת האם ערך כלשהו (value) נמצא במערך ואיפה • אפשרות א': חיפוש "רגיל" – מעבר על כל ערכי המערך intregular_serach(int array[], int size, int value) { inti; for (i = 0; i < size; i++) { if (array[i] == value) return 1; } return -1; {

  12. חיפוש בינארי (דורש מערך ממוין) • קלט: • מערך ממויין של מספרים שלמים A • מספר שלם שברצוננו לחפש q • פלט: • 1- אם q לא נמצא ב- A • אחרת, את האינדקס במערך A שבו נמצא q. • האלגוריתם: • בדוק את האיבר האמצעי במערך A • אם הוא שווה ל- q החזר את האינדקס • אם האיבר האמצעי קטן מ- q, חפש את q ב- A[0, … , middle-1] • אם האיבר האמצעי גדול מ- q, חפש את q ב- A[middle+1, …, end]

  13. דוגמא נחפש את 56... 4 5 index 7 8 0 1 2 3 6 9 value

  14. דוגמא נחפש את 4... 4 5 index 7 8 0 1 2 3 6 9 value

  15. Code – Binary Search int binarySearchRec(int arr[], int quary, int start, int end) { int middle; if (start > end) return -1; middle = start + (end-start) / 2; if (arr[middle] == quary) return middle; if (arr[middle] > quary) return binarySearchRec(arr,quary,start,middle-1); else return binarySearchRec(arr,quary,middle+1,end); }

  16. Code – Main int a [] = {-5,-3,0,4,8,11,22,56,57,97}; printf("%d\n",binarySearch(a,SIZE,0)); printf("%d\n",binarySearch(a,SIZE,-4)); printf("%d\n",binarySearch(a,SIZE,8)); printf("%d\n",binarySearch(a,SIZE,1)); printf("%d\n",binarySearch(a,SIZE,-5)); printf("%d\n",binarySearch(a,SIZE,9)); printf("%d\n",binarySearch(a,SIZE,7)); int binarySearch(int arr[], int size, int quary) { return binarySearchRec(arr,quary,0,size-1); }

  17. Output int a [] = {-5,-3,0,4,8,11,22,56,57,97}; printf("%d\n",binarySearch(a,SIZE,0)); printf("%d\n",binarySearch(a,SIZE,-4)); printf("%d\n",binarySearch(a,SIZE,8)); printf("%d\n",binarySearch(a,SIZE,1)); printf("%d\n",binarySearch(a,SIZE,-5)); printf("%d\n",binarySearch(a,SIZE,9)); printf("%d\n",binarySearch(a,SIZE,7));

  18. אז... כמה זמן לוקח לעשות חיפוש בינארי? חישוב סיבוכיות זמן ריצה: • נחשוב על המקרה הגרוע ביותר • גודל המערך שבו אנו מחפשים הולך וקטן בכל קריאה רקורסיביתn n/2  n/4  …..  1 • כל צעד באלגוריתם הוא מאוד מהיר (מספר קבוע וקטן של פעולות = c) • יש log2(n)צעדים לכל היותר. • לכן סה"כ האלגוריתם יבצע c*log2(n) פעולות, שזה בקירוב log2(n). • אם n = 1,000,000 חיפוש בינארי יבצע כ-20 צעדים בלבד! • הרבה יותר מהיר מהאלגוריתם הנאיבי שמבצע כ-n פעולות

  19. סיבוכיות זמן ריצה על רגל אחת... • מודדים סיבוכיות של אלגוריתם עפ"י מדד של מקום (כמות זיכרון) ומדד של זמן ריצה. • הערכת הסיבוכיות נעשית בכלליות, ללא התחשבות בפעולות קצרות שמספרם קבוע (כלומר לא תלוי בגודל הקלט). • מעריכים את זמן הריצה בסדרי גודל – מסומן ב- O. • לדוגמא, נניח שאנו עובדים על מערך בגודל n = 1,000,000 • O(n2) = constant * trillion (Tera) • O(n) = constant * million (Mega) • O(log(n)) = constant * 20

  20. הבדלים מספריים 20

  21. השוואה גרפית

  22. חיפוש בינארי עם מצביעים int * binarySearch (intarr [ ], int size, intquary) { int * middle; if (size == 0) return NULL; middle = arr + size/ 2; if (*middle == quary) return middle; if (*middle > quary) returnbinarySearch(arr, size/2, quary); else returnbinarySearch(arr+size/2+1, size-size/2-1, quary); } 23

  23. Main & Output int a [] = {-5,-3,0,4,8,11,22,56,57,97}; int * ind = binarySearch(a,SIZE,0); if (ind != NULL) printf("%d\n",ind - a); 24

  24. חיפוש בינארי איטרטיבי int binarySearch(int arr [], int size, int quary){ int start= 0, end = size - 1; int middle; while (start <= end){ middle = (start + end) / 2; if (quary == arr [middle]) return middle; if (quary < arr [middle]) end = middle - 1; if (quary > arr [middle]) start = middle + 1; } return -1; }

  25. הוספת מספר למערך ממויין ?10< ?10< ?10< ?10< ?10< ?10< 2 5 1 8 9 13 67 2 5 10 13 67 67 1 8 9 13 קלט: מערך ממויין והערך 10 צעד אחר צעד: 26

  26. הוספת מספר למערך ממויין /* * Requires: * 1. The elements of the array are sorted in ascending order. * 2. length < capacity * length is the current number of elements in the array * capacity is the maximum number of elements array */ void array_add(int array[], int length, int value) { inti, j; for (i = 0; i < length && array[i] <= value; i++); for (j = length; j > i; j--) array[j] = array[j - 1]; array[i] = value; {

  27. יתרונות וחסרונות של שימוש במערכים • יתרונות: • גישה ישירה ומיידית ע"י אינדקסים • חיפוש מהיר (כאשר המערך ממויין) • חסרונות: • לא יעיל עבור שינויים דינמיים במערך כגון: • הוספת איבר באמצע המערך • מחיקת איבר (צמצום רווחים) • שינוי מיקום של איבר במערך • בהמשך הקורס נלמד מבנה נתונים נוסף שמאפשר ביצוע שינויים דינמיים כאלה ביעילות (רשימה מקושרת).

  28. עד עכשיו הנחנו שהמערך ממויין... איך נמיין מערך קיים ביעילות?

  29. מיון בועות - Bubble Sort • נסרוק את המערך ונשווה כל זוג ערכים שכנים • נחליף ביניהם אם הם בסדר הפוך • נחזור על התהליך עד שלא צריך לבצע יותר החלפות (המערך ממויין) • למה בועות? • האלגוריתם "מבעבע" בכל סריקה את האיבר הגדול ביותר למקומו הנכון בסוף המערך. 30

  30. 2 2 7 2 2 2 2 2 2 2 2 2 2 5 4 2 4 7 7 7 4 7 7 5 5 7 4 8 8 5 5 5 8 5 5 5 4 7 5 8 4 5 7 5 5 4 7 4 7 4 7 7 8 8 8 8 8 8 4 4 8 4 4 8 8 2 5 4 7 8 Bubble Sort Example (done)

  31. Bubble Sort Code void bubbleSort(int arr[], int size) { int i,j,tmp; for (i = size - 1; i > 0; --i) for (j = 0; j < i; ++j) if (arr[j] > arr[j+1]) { // swap tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } }

  32. Bubble Sort Code int main() { int i, a [] = {7,2,8,5,4}; bubbleSort(a,SIZE); for (i = 0; i < SIZE; ++i) printf("%d ",a[i]); printf("\n"); return 0; }

  33. מיון בועות – ניתוח סיבוכיות זמן ריצהעבור מערך בגודל n constant void bubbleSort(int arr[], int size) { int i,j; for (i = size - 1; i > 0; --i) for (j = 0; j < i; ++j) if (arr[j] > arr[j+1]) { // swap tmp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = tmp; } } n iterations i iterations (n-1 + n-2 + n-3 + …. + 1) * const ~ ½ * n2

  34. דוגמאות לחישוב סיבוכיות זמן ריצה • מצא ערך מקסימלי במערך לא ממויין • מצא ערך מקסימלי במערך ממויין • מצא את הערך החמישי הכי גדול במערך ממויין • מצא ערך מסויים במערך לא ממויין • מצא ערך מסויים במערך ממויין • ענה על n "שאלות פיבונאצ'י" • שאלת פיבונאצ'י: מהו הערך ה-K בסדרת פיבונאצ'י? • נניח ש-K מוגבל להיות קטן מ-MAX

  35. ראינו שאפשר למיין מערך ב- O(n2), האם אפשר למיין מהר יותר? כן! MERGE SORT

  36. Merge Sort - העקרונות • ניתן למיין מערך קצר הרבה יותר מהר מאשר מערך ארוך • בהנתן 2 מערכים ממויינים, ניתן לאחד אותם למערך ממויין אחד די מהר – O(n).

  37. איחוד 2 מערכים ממויינים p p p p p u q q p u u q q u u u u u u q u 1 7 2 4 5 6 3 8 9 10

  38. Merge Sort - אלגוריתם • אם המערך בגודל 1 או 0 אז הוא כבר ממויין.אחרת... • חלק את המערך ל-2 חצאים • מיין כל תת-מערך רקורסיבית (ע"י קריאה ל-MergeSort) • אחד את שני תתי-המערכים הממויינים למערך ממויין אחד.

  39. Merge Sort (partial) Code void mergeSortRec(int arr[], int start, int end) { int middle = (end - start) / 2; if ((end - start) < 2) return; mergeSortRec(arr,start,middle); mergeSortRec(arr,middle+1,end); mergeArrays(arr,start,middle,middle+1,end); } void mergeSort(int arr [], int size) { return mergeSortRec(arr,0,size-1); }

  40. Merge Sort - דוגמא

  41. n + n + … + n = (n+1) * log(n) log(n) +1 Merge Sort – ניתוח סיבוכיות זמן ריצה • אם המערך בגודל 1 או 0 אז הוא כבר ממויין.אחרת... • חלק את המערך ל-2 חצאים • מיין כל תת-מערך רקורסיבית (ע"י קריאה ל-MergeSort) • אחד את שני תתי-המערכים הממויינים למערך ממויין אחד. n + 2 * (n/2) + 22 * n/22 + 23 * n/23 + … + 2log(n) * n/2log(n) =

  42. השוואה גרפית

  43. ראינו שאפשר למיין מערך ב- O(n log(n)), האם אפשר למיין מהר יותר? לפעמים... Bucket sort

  44. Bucket Sort • אלגוריתם בזמן לינארי : O(n) • אבל... מוגבל למספרים שלמים, חסומים בטווח.

  45. Bucket Sort

  46. מיון מחרוזות • עד כה מיינו מספרים • איך נמיין מחרוזות? • בסדר לקסיקוגרפי (מילוני) • פרטים בתרגול...

  47. מיון גנרי • נרצה למיין מערך של int / long / double / float / char • בסדר עולה / יורד • מה ההבדל בין המקרים? • האלגוריתם זהה! • האם נהיה חייבים לשכפל קוד עבור כל מקרה?

  48. הרעיון של מיון גנרי • נכתוב פונקציה אחת שתוכל למיין מערכים שלint / long / double / float / char בסדר עולה או יורד. • מה יהיו הפרמטרים? • מצביע למערך • מצביע לפונקציית השוואה

  49. הרעיון של מיון גנרי • ב-C אפשר להעביר לפונקציה מצביע למערך כללי (void *) • וניתן להעביר לפונקציה מצביע לפונקציית ההשוואה. • לא נכנס לפרטים במסגרת קורס זה... Comperator code array Memory

More Related