שעור 6 Open addressing אין רשימות מקושרות. (נניח שהאלמנטים מאוחסנים בטבלה עצמה, לחילופין קיים מצביע בהכנסה המתאימה לאלמנט אם אין שרשור). ב- addressing open הטבלה עלולה להימלא ב- factor α load תמיד. במקום לעקוב אחר מצביעים, אנו מחשבים את סידרת התאים בהם עלינו לחפש. המקום שבוזבז ע"י מצביעים משמש למערך גדול יותר שמאפשר מספר קטן יותר של התנגשויות ופענוח h : V { 0,,, -} {0,,..., -} <h(k, 0), h(k, ),,h(k, -)> מהיר יותר. (לא תומך ב- deletions העניינים מסתבכים אם מרשים.(deletions כאשר h(k) h(k,o) = ו-( i h(k, התא שנגיע באיטרציה ה- i -ית (בהנחה שבאיטרציה הקודמת הגענו לתאים מלאים). אנו דורשים שהסדרה -)) (h(k, 0), h(k, ),, h(k, תהיה פרמוטציה של -} {0,,..., כך שבסופו של דבר נבחן את כל תאי הטבלה. למה: עבור ראשוני אם 0 x od אז i לכל ix od = y 0. x od, x od,... ( ) הערכים x od מהווים פרמוטציה של..{,..., -} הוכחה: -ים. כיוון שכל מספר שאינו אם א. = 0 ix od עבור i אז מתחלק ל- y <. אינו מתחלק ב- k אם y, i אז iy < אינו מתחלק ב-. (i j) x = (b a) (j i) x od = 0 - -
i < j אזי V = ix od = אם ב. jx od ix = a + V jx = b + V Insert (T, k) i 0 repeat j h(k, i) if T[j] is epty T[j] k return j i i+ until i = error "hash table overflow" פרוצדורת חיפוש בוחנת את תאי הטבלה לפי אותו הסדר. החיפוש יכול להסתיים (ללא הצלחה) כאשר נתקלים בתא ריק. Search (T, k) i 0 repeat j h(k, i) if T[j] contains k return j i i + until T[j] is epty or i + return null כאן השתמשנו בהנחה שאין.deletions אם הינו רוצים לתמוך ב- deletions הינו צריכים לסמן את התא שהתפנה כ- deleted (במקום.(null במהלך חיפוש הינו ממשיכים לחפש מעבר לתא שסומן כ- delete ובמהלך ההכנסה הינו מתייחסים לתא שסומן כ- deleted כתא ריק. - -
.α הבעיה בפתרון המוצע היא שעתה זמן החיפוש לא תלוי יותר ב- factor load (יתכן שהטבלה כמעט ריקה עכשיו, כלומר, רוב הכניסות מסומנות כ- deleted ). Unifor hashing היינו רוצים: בהינתן מפתח כלשהו k, ההסתברות של כל אחת מ-! הפרמוטציות של {-,..., 0, }להיות.! מטריצת הביקורים שמחושב ל- k היא Linear probing פונקצית hash רגילה. h ( k, i) = ( h'( k) + i) od, i = 0,,..., h' : V {0,,,..., }.T[ ] T[ h'( k) + ], התא הבא - T[ h'( k)] כלומר התא הראשון בו נבקר הוא - וכו', עד שנגיע ל-.T[ h'( k) התא הבא יהיה [0]T וכו' עד שנגיע לבסוף ל- [ עם linear probing יש רק סדרות ביקור שונות, כיוון שהסדרה נקבעת ע"י.h'(k) Linear probing לא כל כך טוב כי במשך הזמן נוצרים רצפים ארוכים. רצפים ארוכים גורמים לכך שזמן החיפוש הממוצע גדל.. 3 n = למשל: אם לעומת זאת אם וכל התאים עם אינדקס זוגי תפוסים, אזי זמן הממוצע של חיפוש ערך הוא התאים הראשונים תפוסים אזי הזמן הממוצע של חיפוש ערך הוא: ( ) ( )...... + + + + + + + = = 8 5 + 4 = + 8 5 4 8 ( + )( + ) -+ - 3 - =
i + הסיבה שנוצרים רצפים ארוכים היא: אם תא ריק בא אחרי i תאים מלאים, אזי ההסתברות שתא יתמלא בהכנסה הבאה היא במקרה שהתא הקודם הוא ריק. להבדיל מ- Hashing Direct addressing Chaining Open addressing Linear probing Double hashing Double hashing h ( k, i) = ( h ( k) + ih ( k)) od i = 0,,... h,h כאשר הן פונקציות hash רגילות. ב-. linear probing O( שיטה זו טובה מאוד. יש ) רצפים שמשתמשים בהם במקום h ( k ) T[ h התא הראשון [(k ( ואח"כ מתקדמים בקפיצות של שלא כמו ב- probing.linear סדרת הביקורים עבור מפתח k תלויה ב- k בשתי דרכים, אם התא הראשון וגם ה- offset מחלקות שקילות לתא הראשון ו- להמשך הרצף - 4 -
א. המחלק המשותף הגדול ביותר של (k) h ו- חייב להיות כדי שנקבל סידרת חיפוש ובאורך מלא. h (k) = k od h (k) = + (k od -) בחירת ראשוני ו- h מחזירה מספר בתחום - : ו h מחזיר תמיד ערכים אי-זוגיים (המצאה של פרופ' שלומי דולב). ( + (od ) h ( k) = ב. = l α משפט: המספר הממוצע של בדיקות בחיפוש לא מוצלח בהנחת.unifor hashing n ( α = < ) = = α ומספר הביקורים בממוצע הוא למשל אם הטבלה חצי מלאה, אזי הסתברות למצוא ריק הסתברות למצוא מלא α k k α ( α) = α k = ( α k = ( α) kα k = α k = kα α k α = α = ( ) α k = k מ.ש.ל ) ידוע ש- הגזירה תיתן תוחלת אורך החיפוש הערך: מסקנה: זה גם התוצאה עבור insertion (כי זה מחייב חיפוש עקר). ln α α משפט: מספר הביקורים הממוצע בחיפוש מוצלח = α נקבל.38 חיפושים. = α נקבל.55 חיפושים. למשל : 9 0 אם אם - 5 -
דוגמא: נתון: זיכרון 0000 ביטים מספר המילים שתופסת רשומה 7 מספר המילים שתופס מצביע מספר הרשומות 000 6 מהחיפושים מסתיימים בהצלחה (והשאר עקרים) מה עדיף? Double hashing עם רשימות מקושרות (חד כיווניות) או Chaining זמן חישוב פונקצית hash שווה לפעמיים לזמן השוואות. אין רשומות בטבלה עצמה. Chaining 000 רשומות 000 שתופסות 7000 מילים S-7000 L-000 T-0000 T L S עבור כל רשומה נצטרך מצביע + next, כלומר: 000 מילים ו- 000 מילים עבור המצביעים בטבלה - 6 -
3 יחידות = +α חישוב h α.5 חישוב = + יחידותh חיפוש עקר: חיפוש מוצלח: α = = חישוב h 5.5 + 3 3 6 6 5 30 + 3 זמן חיפוש ממוצע: Double hashing T 3000 7000 α = 3 באיטרציה ראשונה של חיפוש נחשב את (k) h בעלות ובאיטרציה השנייה של החיפוש נחשב את בעלות כאשר האינטרציות עלות החישוב היא נניח 0. = α 3 חיפוש עקר: h (k) α = 3 חיפוש מוצלח: ln α α - 7 -
זמן חיפוש ממוצע (מניחים לשם הפשטות שתמיד שתי פונקציות מחושבות למרות שזה לא ממש 3 3 5 [( 3ln ) + 4] + ( + 4) 5.5 6 6 נכון) : מסקנה: double hashing אינו עדיף. חישוב פונקציה כפול h + מספר השוואות h לא מוצלח מוצלח 0.9 4.58 חישוב פונקציה כפול h + h איך נקודד משתנים שאינם? integers להתבונן ב- web. למשל: טבלת משתנים קומפיילרים מחזיקים בד"כ ב- table.hash שם המשתנה salary תרגום למספרים טבעיים ע"י: ערך 5 4 key = ASCII(' S') 8 + ASCII(' G') 8 +... + ASCII(' Y ') if k = l i= k i l then k od = ( ( ki od))od and i= ( x y)odk = (( x odk)( y odk))odk אלה מספרים ענקיים, אם: Hornor's forula: k = a 0 (...(( a n + a x + a x + a n x ) x + a +... + a x n n n = ) x +...) x + a 0-8 -
To copute k od we copute: b a n for i (n-) to 0 b (bx+a i ) od For exaple: k = a (( x + a 3 0 + ax + a x + a3x = a3x + a ) x + a) 0 then k od is coputed as follows: ) b ( a x a ) od 3 + ) b ( bx + a) od 3) b ( bx + a ) 0 od. Linear probing: http://www.gnifty.net/cs/cs6/javaprogs/hash/hash.htl סימולציות:. Chaining: http://web.engr.oregonstate.edu/~inoura/cs6/javaprogs/hash/hashl.htl 3. http://ciips.ee.uwa.edu.au/~orris/year/plds0/hash_tables.htl 4. http://www.utdallas.edu/~ivor/cs35/hash/hash.htl - 9 -