280 likes | 499 Views
התאמת מחרוזות תווים מציאת מחרוזת תווים בטקסט מסוים. משתמשים בזה הרבה במעבד תמלילים. חיפוש אחר תבנית מסוימת ברצפים של DNA T[1...n] – טקסט P[1...m] – תבנית כאשר m < n. מניחים כי אברי T ו- P לקוחים מאלפבית סופי. נאמר כי התבנית p מופיעה עם היסט S בטקסט T אם:
E N D
התאמת מחרוזות תווים מציאת מחרוזת תווים בטקסט מסוים. משתמשים בזה הרבה במעבד תמלילים. חיפוש אחר תבנית מסוימת ברצפים של DNA T[1...n] – טקסט P[1...m] – תבנית כאשר m<n
מניחים כי אברי T ו- P לקוחים מאלפבית סופי. נאמר כי התבנית p מופיעה עם היסט S בטקסט T אם: 0<s<n-m ו- T[s+1,…,s+m]=p[1…m] דוגמה: T טקסט P תבנית S=3
בעיית התאמת המחרוזות היא הבעיה של מציאת כל ההיסטים התקפים שבהם מופיעה תבנית נתונה p בטקסט נתון T. סימונים: - קבוצת כל המחרוזות בעלות אורך סופי שאפשר ליצור מתווים הלקוחים מן האלפבית . |x|- אורך של מחרוזת x. xy- שרשור של מחרוזת x למחרוזת y(מחרוזת הבנויה מתווי x ואחריהם תווי y).
מינוחים: רישא: w<x מחרוזת w היא רישא של מחרוזת x אם קיימת מחרוזת y שעבורה x=wy. מכך נובע |w|<|x|. סיפא: w >x מחרוזת w היא סיפא של מחרוזת x אם קיימת מחרוזת y שעבורה x=yw. מכך נובע |w|<|x|.
הערה: היחסים <, > הם טרנזטיביים. למה: נניח x,y,z הן מחרוזות המקיימות x > z ו- y > z. אם |x|<|y| אזי x > y אם |x|>|y| אזי y > x, אם |x|=|y| אזי x=y. נקצר את הסימונים: Pk יהיה הרישא בת k התווים p[1...k] של התבנית p[1...m]. (Pm=P=p[1...m]). באופן דומה Tk זה הרישא בת k תווים של הטקסט T.
הגדרת הבעיה בצורה אלגנטית: מציאת כל ההיסטים s בתחום 0<s<n-m שעבורם p > Ts+m. אלגוריתם נאיבי להתאמת מחרוזות: NAÏVE-STRING-MATCHER(T,P) n=length[T] m=length[P] For s=0 to n-m 3.1 if p[1…m] = T[s+1…s+m] 3.1.1 print “pattern with shift” s סיבוכיות o((n-m+1)m)
אנו נגיע למקרה הגרוע כאשר הטקסט והתבנית כי בשלב 3.1 הוא יבדוק את כל התווים בתבנית.(כאן הוא יצליח למצוא). האלגוריתם לא יעיל מאחר והוא מתעלם מן המידע על הטקסט שנרכש עבור ערך אחד של s בעת שהוא בוחר ערכי s אחרים. דוגמה: P=aaab ו-s=0 הוא היסט תקף אזי אף אחד מההסטים 1,2,3 אינו יכול להיות תקף מאחר ו- b=T[4] ואנו באלגוריתם לא מנצלים זאת.
אלגוריתם רבין –קארפ (RK): מסתכלים על הטקסט כמספרים ולא כאותיות. א"ב, d=| |, כל מילה מייצגת מספר בבסיס d. כל תו מ- מייצג ספרה בבסיס d. מילים עם תווי מייצגים מספרים בבסיס d. דוגמה:0 1 2 3 ={a,b,c,e} bbcaebc 1120312 : 2+1*41+3*42+0*43...בבסיס 10
P[1…m]- מספרm ספרתי בבסיס d. P=p[m]+p[m-1]*d+p[m-2]*d2 +…+p[1]*dm-1 צריך לחלק את הטקסט למספרים באורך התבנית. לכל 0<s<n-m נגדיר ts(המספר ה-m ספרתי שיישב אחרי היסט של s מקומות). ts= T[s+m]+t[s+m-1]*d +…+T[s+1]*dm-1 אנו מחפשים כל s כנ"ל כך ש-ts=p רעיון: נחשב את P, לכל s כנ"ל נחשב את tsבתוכו ונשווה בניהם.
סיבוכיות: חישוב P: 1+2+…+m-1=o(m2) חישוב ts: o(m2) סה"כ:o(n*m2) לכל s שיפור האלגוריתם: חישוב עפ"י שיטת הורנר 7514= 7*103+5*102+1*101+4*100 =(((7*10+5)*10+1)*10)+4 בצורה כללית: P=((..(p[1]*d)+p[2])*d+…)*d+p[m]
בדומה לכל ts: ts=((..(T[s+1]*d)+T[s+2])*d+…)*d +T[s+m] 2. נראה כי יש קשר בין ts ובין ts+1 (נרצה לשפר גם כאן). 7*104+1*103+0*102+3*10+1 1*104+0*103+3*102+1*10+2 נראה כי: t1= (t0-T[1]*dm-1)*d+T[m+1]
בדומה לכל s כנ"ל: ts+1= (ts-T[s+1]*dm-1)*d+T[s+m+1] בכל פעם נחשב את ts+1 ע"י ts. אלגוריתם משופר: נחשב מראש(פעם אחת) את p ואת dm-1 ,נחשב את t0 ועבור כל s כנ"ל נחשב את ts+1 מ-ts. סיבוכיות: חישוב p חישוב dm-1o(m) חישוב t0 חישובts+1 מ- ts=o(1) סה"כ: o(m+n)
שיפור נוסף: עריכת כל החישובים מודולו q. (q-ראשוני). מכאן- המספר הגדול ביותר שנעבוד אתו יהיה q-1 ומספר הספרות של המספר יהיה לכל היותר log q. בעיה: p mod q = ts mod q p = ts פתרון: כאשר נקבל p mod q = ts mod q נבדוק ישירות אתp לעומת ts.
דוגמה: P mod 13 T פגיעה מדומהפגיעה תקפה
האלגוריתם: Rabin-Karp-Matcher(T,P,d,q) n= length[T] m= length[P] h= dm-1mod q p= 0 t0= 0 For i=1 to m 6.1 p=(d*p+P[i])mod q 6.2 t0=(d*t0+T[i])mod q For s=0 to n-m 7.1 if p=ts 7.1.1 if p[1…m] = T[s+1…s+m] 7.1.1.1 print “ pattern shift” s
7.2 if s<n-m 7.2.1 ts+1=(d(ts-T[s+1]*h)+ T[s+m+1])mod q סיבוכיות: O(m+n+m*(v+ ) מעבר על המערך חישובים ראשונים בדיקה ממשית עבור פגיעה V- מספר ההיסטים התקפים. - תוחלת מספר הפגיעות המקריות(הסיכוי ש-ts יהיה שווה ל- P mod q ניתן לאמוד ב-1/q). אם q יהיה גדול מספיק אז יתנהג כמו קבוע.
כלומר עבור v קטן ו-q גדול הסיבוכיות תהיה o(m+n).
אלגוריתם קנות' מוריס-פראט (kmp) האלגוריתם מוצא התאמה מדויקת בזמן o(n+m). השיטה באלגוריתם זה חישוב פונקצית הרישא של התבנית. פונקצית הרישא של התבנית אוצרת ידע אודות התאמת התבנית להיסטים של עצמה. במידע זה ניתן להשתמש כדי להימנע מבדיקה מיותרת של היסטים חסרי תועלת כפי שעושה האלגוריתם הנאיבי.
דוגמה: T P אנו רואים כי התו האחרון של התבנית לא הותאם לתו של הטקסט. אם נעבד את התבנית נוכל לראות כי יש הסטים שלא יהיו תקפים ולא נצטרך אפילו לבדוק אותם. למשל נמנע מההיסטים s=1 ו-s=2.
את החישובים המקדימים של התבנית ננסח באופן פורמלי כדלקמן: בהינתן תבניתp[1…m], פונקצית הרישא עבור התבנית p היא הפונקציה :{1,2,…,m} {0,1,…,m-1} המקיימת: [q] = max{k|k<q וגם pk pq} כלומר [q] הוא אורכה של הרישא הארוכה ביותר של p שהיא הסיפא ממש של pq.
דוגמה: I P[i] [i] Compute-prefix(p) [1] = 0 K=0 For q=2 to m 3.1 while k>0 and p[k+1]p[q] 3.1.1 k= [k] 3.2 if p[k+1]=p[q] 3.2.1 k=k+1 3.3 [q] = k 4. Return
נראה דוגמה איך עובד אלגוריתם התאמת המחרוזות: i P[i] [i] T 1 2 3 4 5 6 7 8 9 10 … 16 … 23 התאמה התאמה s=16-10=6 s=23-10=13 (הסבר בכיתה).
KMP Matcher(T,P) = compute-prefix(P) q = 0 For i=1 to n 3.1 while q>0 and p[q+1] T[i] 3.1.1 q = [q] 3.2 if p[q+1] = T[i] 3.2.1 q = q+1 3.3 if q=m 3.3.1 print “pattern shift” i-m 3.3.2 q= [q]
סיבוכיות: הפונקציה לחישוב דורשת o(m). מספר הפעולות הוא מספר הפניות ל-K ול-q. q מקבל m-1 ערכים. בכל לולאת for , k מקודם לכל היותר ב-1. הוא יכול לרדת בלולאה מספר פעמים עד 0, אבל כיוון שהעלייה איטית, לא תהינה ירידות משמעותיות רבות. כל הזמן נשמר אי השוויון q>k ולכן K אינו עולה על m וסה"כ הזמן הוא o(m).
פונקצית ההתאמה(בין התבנית לטקסט) עובדת באופן דומה ולכן סיבוכיות הזמן שלה היא o(n).