פולינומים, FFT

פולינום

פולינום מעל שדה F עם משתנה x מויצג באופן הבא:

A(x)=i=0n1aixi
פעולות על פולינומים
C(x)=i=0n1(ai+bi)xi C(x)=A(x)B(x)

זה פולינום מדרגה חסומה 2n1.
נוכל לבטא את C(x) באופן הבא:

C(x)=i=02n2cixi

מה זה ci ? זה תהליך של קונבולוצייה

ci=k=0iakbik

(בעצם זה כפל של כל הגורמים שהחזקות שלהם יביאו לחזקה i) .

ייצוג פולינומים

ייצוג מקדמים

נגדיר a כוקטור המקדמים a=(a0,a1,a2,a3,,an1) .
על ייצוג זה מאוד נוח לבצע את פעולת ההצבה והחיבור בזמן ליניארי למספר המקדמים O(n). כמו כן נשים לב שאכן יש פונקצייה חח״ע ועל בין פולינום A(x) למערך כזה.
ההוכחה לזה פשוטה :
נגיד f:FnF[n] על ידי :

f(a)=i=0n1aixi

זאת פונקצייה על כי לכל פולינום שנבחר מדרגה חסומה n נוכל לבנות וקטור מקדמים כזה (פשוט לשים 0 אם יש חזקות שלא נמצאות).
זאת פונקצייה חח״ע כי פולינום מדרגה חסומה n יהיו שווים רק אם המקדמים שלהם שווים משמע הקלטים של הפונקצייה (הוקטורים עצמם) שווים גם כן.

נשים לב שהכפלת שני פולינומים שמיוצגים על ידי המקדמים ייקח O(n2) זמן. (כל מקדם יופל בכל המקדמים של הוקטור השני ולבסוף נסכום הכל).

ייצוג דגימת הנקודות

נוכל לייצג את הפולינום A(x) מדרגה חסומה n על ידי קבוצה של n זוגות סדורים שכל זוג מייצג נקודה ואת ערך הפולינום בהצבת הנקודה.

{(xi,yi)  |  i[0,n1]yi=A(xi)}

ברור מאליו ש k,j[0,n1]:xkxj .

נוכיח שגם כאן יש התאמה חח״ע ועל בין הייצוג לבין הפולינום. כדי להוכיח זאת נשים שבעת הצבת ערך xi כלשהו ב A(x) המקדמים יישארו אותו דבר ולכן נוכל לייצג כל הצבה כזאת כשורה מטריציונית:

V=[(x0)0(x0)1(x0)n1(x1)0(xn1)0(xn1)n1]

כעת , נסמן את וקטור המקדמים כ a ויתקיים :

Va=y

כאשר y זה וקטור הפתרונות.

מטריצה זו ידועה בשם Vandermonde matrix.
כעת, אם נוכיח שהיא הפיכה נוכל להגיע לוקטור המקדמים על ידי

V1y

וזאת אכן תהיה פונקצייה חח״ע ועל כיוון שאם המטריצה הפיכה משמעות הדבר שיש פתרון יחיד למערכת. (באופן דומה יכלנו להגדיר את הפונקצייה שלנו להיות ההעתקה ליניארית שמייצגת את המטריצה הזאת וזאת תהיה העתקה הפיכה).

נוכל להפוך את מטריצת ונדרמונה למטריצה הפיכה בשלבים הבאים :

j>1:RjR1Rj

כלומר להפחית את השורה הראשונה מכל האחרות , זה יגרום לעמודה הראשונה להתאפס חוץ מהאיבר הראשון. כעת :

j>1:(xjx0)RjRj

נשים לב שהסקלר שמוציאים נכנס בחישוב של הדטרמיננטה
וכעת נוכל לעשות זאת עבור כל שורה ועמודה , באופן כללי הפעולות שנעשה יהיו באופן הבא :

i,j>i:RjRiRj  ,  (xjxi)RjRj

סך הכל נקבל מטריצה משולשית תחתונה עם איברי 1 כל הסקלרים שהוצאנו נקבל שהדטרמיננטה היא מכפלת איברי האלכסון שזה 1 במקרה זה ועם כל הסקלרים זה יצא

Π1jin(xjxi)

ובגלל שאמרנו שכל האיברים בייצוג נקודות שונים אחד מהשני אז הדטרמיננטה שונה מ0 ולכן המטריצה הפיכה.

מעבר בין ייצוג מקדמים לנקודות

נבחר n ערכים שונים ונציב אותם ב A. כיוון שכל הצבה דורשת סכימה של n מקדמים כפול הערך שהצבנו ואנחנו מבצעים זאת עבור כל ערך שבחרנו. זמן הריצה של פעולת המעבר תהיה O(n2).

מעבר בין ייצוג נקודות למקדמים

תהליך זה נקרא אינטרפולציה. ובזכות מטריצת ונדרמונדה שדיברנו עליה תהליך זה מוגדר היטב על הפולינום המיוצג בנקודות. הראנו שיש פונקצייה חח״ע ועל בין הנקודות לבין פולינום מדרגה חסומה n כי המטריצה V הפיכה. לכן המעבר יסתמך על המטריצה ההופכית והמשוואה

V1y=a

כדי להגיע למקדמים .

האלגוריתם שבו השתמשנו למציאה ההופכית (גאוס ז׳ורדן) + בניית המשוואה ייקח והפתרון O(n3) , לא בעיה להראות זאת אבל ניתן להכנס לקישור להסבר מעמיק.

ניתן לייעל את האלגוריתם בבאמצעות משפט לגראנז :

A(x)=k=0n1ykΠjk(xxj)Πjk(xkxj)

אלגוריתם המעבר במקרה הזה ייקח O(n2) .
הסיבה שזה O(n2) היא כי גם למונה וגם למכנה מבצעים O(n) פעולות כל פעם. ופעולת הסכימה גם היא חסומה ב O(n) כלומר סך הכל חסום ב O(n2).

פעולות בתצוגת נקודות
חיבור : במצב שבוא שני פולינומים מיוצגים על ידי אותם ערכי {xi  | i[0,n1]} אז החיבור לוקח O(n)

{(xi,A(xi)) | i[0,n1]}{(xi,B(xi)) | i[0,n1]}C=A+B{(xi,C(xi)) | i[0,n1] C(xi)=A(xi)+B(xi)}

זה נובע ישירות מהגדרת חיבור פולינומים.

כפל : באופן דומה אם קיבלנו מצב כמו הנ״ל אז

i[0,n1]:C(xi)=A(xi)B(xi)

זה גם נכון מהגדרת הכפל אבל ההבדל הוא שכאן נצטרך לבחור 2n1 נקודות זהות ל A ו B כדי לייצג את C. הזמן עדיין ליניארי אבל התנאי נהיה ״כבד״ יותר.

הצבה : אם הנקודה שנרצה להציב היא אחת מהנקודות שיש לנו אז נצטרך רק לבצע חיפוש שבמקרה הטוב הוא O(logn) תלוי איך שמרנו את הנקודות. במידה ולא, עדיף לעשות אינטרפולצייה.

נשים לב: בשלושת הפעולות אם התנאים שציינתי לא מתקיימים נרצה לעשות אינטרפולציה כלומר מעבר למקדמים ב O(n2).

כרגע התהליכים שאנחנו מכירים הם כאלה:
Pasted image 20221029175405.png
כלומר על פניו נראה שכדאי לנו להשאר עם מקדמים וזהו. נראה שיש שיטות לייעל את התהליכים הרשומים למילה.

כפל מהיר של פולינמום בייצוג מקדמים.

נניח שn הוא חזקה של 2 (נוכל להניח זאת כי במצב שהוא לא פשוט נשלים אותו להיות חזקה של 2 עם מקדמי 0) .
נגדיר

A(x)=i=0n1aixi  ,  B(x)=i=0n1bixi

כעת נחלק את הפולינומים באופן הבא

A(x)=Q(x)+xn2P(x)  ,  B(x)=R(x)+xn2S(x)

כאשר-

Q(x)=i=0n21aixi  ,  R(x)=i=0n21bixiP(x)=i=n2n1aixin2  ,  S(x)=i=n2n1bixin2

נגדיר את מכפלתם להיות

A(x)B(x)=(Q(x)+xn2P(x))(R(x)+xn2S(x))=Q(x)R(x)+xn2(Q(x)S(x)+P(x)R(x))+xn(P(x)S(x))

כעת נוכל לחשב את ארבעת המכפלות שיצרנו שהם בעצם פולינומים מדרגה חסומה n2 באופן ריקורסיבי. החיבור של הכל הוא ליניארי וחסום ב O(n) והנוסחה הריקורסיבית תיראה כך :

T(n)={Θ(1)n=14T(n2)+O(n)n2

לפי משפט המאסטר האלגוריתם חסום ב O(n2).
נוכל לייעל את האלגוריתם אם נשים לב ש:

p1(x)=Q(x)R(x)p2(x)=P(x)S(x)p3(x)=(Q(x)+P(x))(R(x)+S(x))

ויתקיים :

p3(x)p1(x)p2(x)=P(x)R(x)+Q(x)S(x)

ובהצבת בביטוי למעלה נקבל

p1+xn2(p3p1p2)+xnp2

כלומר נוכל לחשב את שלושת הפילונים בצורה ריקורסיבית במקום ארבעה, (החיבור פולינומים וכל זה נעשה בזמן ליניארי O(n)) וכן מבצעים פחות פעולות ריקורסיביות :

T(n)={Θ(1)n=13T(n2)+O(n)n2

כעת זמן הריצה הינו O(nlog23)=O(n1.543) .

אז מה יש לנו עכשיו?

Pasted image 20221029182918.png

אומנם צמצמנו את הכפל בתצוגת מקדמים אבל לא הגענו למקרה הטוב שאנחנו מקבלים במקרה שבו מבצעים כפל בתצוגת נקודות.
על פניו נראה שפעולת ההמרה ממקדמים לדגימה ואז כפל לא יעזור לגם במצב שבו ערכים הנקודות שרוצים להציב בשתי הפולינומים הם זהים בגלל שאינטרפולציה זה החסום ב O(n2) . עם זאת נראה שאכן ישנו אלגוריתם שמייעל את התהליך הזה לזמן ריצה החסום ב O(nlogn).

FFT

עבור פולינום A(x) מדרגה חסומה n. נרצה לעבור מתצוגת המקדמים לנקודות על ידי דגימת {xi|i[0,n1]} ערכים שונים והצבת ב A(x).
נחלק את הפולינום ל 2

Aeven(x)=i=0n21a2ixiAodd(x)=i=0n21a2i+1xi

כעת יתקיים :

Aeven(x2)+xAodd(x2)=i=0n21a2ix2i+i=0n21a2i+1x2i+1=A(x)

נשים לב גם ש A(x)=Aeven(x2)xAodd(x2)

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

T(n)=2T(n2)+O(n)O(nlogn)

הקריאות האלו יוצרות 2 בעיות עיקריות

  1. לא מובטח ש i,j[n]:(xi)2(xj)2 למרות שבלי העלאה בריבוע הם היו שונים.
  2. הקטנו את דרגת הפולינום החסומה בריקורסיה אבל מספר האיקסים נשאר אותו דבר n ולכן הבעיה הריקורסיבית לא פותרת את המקרה שהדרגה החסומה של הפולינום ומספר הx שווה אלא מצב בו הם שונים. כלומר אנחנו עדיין נצטרך לחשב n ערכים בסופו של דבר.

כדי לפתור את הבעיה הנ״ל נגדיר ונוכיח מספר טענות :

תכונת הנגדיות החלשה

יהי n חזקה של 2, נאמר שוקטור של נקודות (x0,x1,x2,,xn1) יקיים את תכונת הנגדיות החלשה אם:

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

(xn2+j)2=(xj)2=xj

עם זאת , תכונת הנגדיות החלשה עבור וקטור x לא מבטיח שכשנפעיל את הריקורסייה פעם נוספת נקבל שוב וקטור שיקיים תכונה זו. למשל בוקטור הנ״ל שנתנו בחציתו לשתיים והעלאה בריבוע נקבל $$(25,1,9,4)$$ וקטור זה כבר לא מקיים את התכונה שרצינו. לכן הבעיה לא נפתרה לגמרי.

תכונת הנגדיות החזקה

יהי n חזקה של 2, נאמר שוקטור של נקודות (x0,x1,x2,,xn1) יקיים את תכונת הנגדיות החזקה אם:

ההגדרות שלנו אכן פותרות את הבעיות שהצגנו למעלה, אבל עכשיו צריך למצוא סדרה מספרים שתקיים את הנ״ל

מספרים מרוכבים וFFT

בוא ננסה לעשות reverse engineering לבעיה שלנו.
נניח והיינו עם איבר אחד בפולינום מדרגה חסומה 1 וערך הx שנרצה לחשב הוא 1.
קריאה אחת לפני היינו צריכים לחשב פולינום מדרגה חסומה 2 בשתי נקודות ±1 כי לקחנו את חצי המערך השמאלי והעלנו אותו בריבוע. במצב זה יתקיים (1,1) הוא הוקטור של הערכים במציבים. מה שזה אומר שקריאה אחת לפני הוקטור מייצג פולינום מדרגה חסומה 4 עם הערכים (1,1,1,1) כלומר (1,1,i,i) .

אנחנו מתקדמים, עוד קצת הגדרות :

שורשי היחידה

שורש יחידה מסדר n הוא מספר מרוכב ω כאשר wn=1 . ובאופן כללי

wn=ei2πn

כאשר r=1 כלומר על מעגל היחידה

תזכורת: מספר מרוכב ניתן להציג בשלוש צורות

z=a+ib=rcis(Θ)=reiΘ

טענה :

wnn2=(ei2πn)n2=eiπ=1

נוסחה זאת נקראת נוסחת אוילר.

טענה :

k[0,n1]:(wnk)n=1

במילים, לכל k מתקיים שהעלאת wn בחזקת k גם הוא יהיה שורש יחידה מסדר n .
הוכחה פשוטה-

(ei2kπn)n=ei2kπ=1

על מעגל היחידה .

אנקדוטה : שורשי היחידה מהווים חבורה חילופית ביחס לפעולת הכפל.

wnjwnk=wn(j+k)mod n

וגם

wn1=wnn1

ובאופן כללי

(wni)1=wnni

כלומר יש מעין מחזוריות כזאת על מעגל היחידה בהתאם ל n.

Pasted image 20221029230822.png|600

טענה : וקטור שורשי היחידה מסדר n מקיים את תכונת הנגדיות החזקה .
הוכחה -
שלב א נוכיח קודם שהוא מקיים את תכונת הנגדיות החלשה.

wn(n2+k)=wnn2wnk=wnk(1)=wnk

שלב ב נסתכל על הוקטור:

(wnk | k[0,n21])2

צריך להוכיח שוקטור זה מייצג את n2 שורשי היחידה מסדר n2 . (כלומר העלאה בריבוע של החצי הראשון של שורשי היחידה מסדר n) .

k[0,n21]:wn2k=e2πi2kn=e2πikn2=(ei2πn2)k=wn2k

שזה שורש יחידה מסדר n2 .

שלב ג נוכיח את תכונת הנגדיות החזקה באינדוקצייה על n באשר הוא חזקה מדויקת של 2.
בסיס : n=1 מקיים מהגדרה.
צעד : נניח שהטענה נכונה לכל k[0,n1] ונוכיח עבור k=n .
הוקטור של שורשי היחידה מסדר n מקיים את תכונת הנגדיות החלשה כפי שהראנו, כעת ניקח את חציו השמאלי ונעלה בריבוע וזה לפי מה שהוכחנו מייצג את n2 שורשי היחידה מסדר n2 ולפיכך הם בעצמם מקיימים את תכונת הנגדיות החזקה לפי הנחת האינדוקצייה.

נשים לב שקיבלנו במתנה את העובדה ש n שורשי היחידה מסדר n הם ערכים שונים .

האלגוריתם FFT

כעת משמצאנו סדרת מספרים שתקיים את התנאים שלנו נוכל לכתוב אלגוריתם פסודו לבעיה שלנו

FFT((A = [a0,a1,a2,...,a_n-1]))
	if n=1 
		return a0
	let A_even = (a0,a2,a4,..., a_n-2)
	let A_odd = (a1,a3,a5,...,a_n-1)
	P_even = FFT(A_even)
	P_odd = FFT(A_odd)

	for j=0 to (n/2 -1) :
		y_j = P_even[j]+ pow(w_n , j) * P_odd[j]
		y_(n/2 +j) = P_even[j] - pow(w_n, j) * P_odd[j]

	return (y_0, y_1 ,y_2 , ... ,y_n-1)	

הסבר :
נרצה לחשב את ערכי הפולינום A(x)=i=0n1aixi על ידי הצבת n שורשי היחידה מסדר n. האלגוריתם יחזיר וקטור

y=(y0,y1,,yn1)

כאשר

yk=A(xk)=A(wnk)=A(e2πkn) Peven=[Aeven(wn20),Aeven(wn21),,Aeven(wn2n21)]

באופן דומה :

Podd=[Aodd(wn20),Aodd(wn21),,Aodd(wn2n21)]

עכשיו ניתן לחשב את הערכים

(A(wnk)|k[0,n1])

לפי הנוסחא שבנינו ונוסחת הנגדיות החלשה.
בגלל התכונות הנ״ל ניצחנו את הבעיות שהיו לנו כשחשבנו על האגוריתם הזה מלכתחילה . על כל תת וקטור שמאלי נצטרך להציב רק חצי מהערכים כדי לקבל את החצי השני.

זמן ריצה:
באופן הזה זמן הריצה אכן תואם את הנוסחה הריקורסיבית ונקבל

T(n)=2T(n2)+O(n)=O(nlogn)

inverse FFT

נניח שהעברנו שני פולינומים מייצוג מקדמים לייצוג דגימה על ידי FFT, והכפלנו את שניהם בזמן ליניארי (כי משתמשים בשורשי היחידה שזה אותם נקודות). כעת קיבלנו פולינום C(x)=A(x)B(x) בתצוגת נקודות. כעת נרצה להעביר את C(x) לייצוג מקדמים:

יש לנו

y=(y0,y1,,yn1) where: yk=C(wnk)

ואם אנחנו זוכרים מתקיים

Vc=y

כאשר c הוא וקטור המקדמים.

[(wn0)0(wn0)1(wn0)2(wn0)n1(wn1)0(wn1)1(wn1)2(wn1)n1(wn0)0(wnn1)1(wnn1)2(wnn1)n1]c=y

נשים לב שזה בעצם עמודה ושורה שכולם 1.
כדי למצוא את וקטור המקדמים נשאלת השאלה מהי הפונקצייה ההופכית יתקיים כי :

DFT=FFT1=1n[11111(wn1)1(wn1)2(wn1)n11(wn1n)1(wn1n)2(wn1n)n1]

נשים לב שבגלל שמדובר בחבורה אז קיים ההופכי ואמרנו כבר מהו למעלה זה בעצם פשוט (w1)k=wnk כלומר עדיין מדובר בשורש יחידה מסדר n .

גם זאת מטריצה ונדרמונדה ולכן תהליך ההמרה לכיוון השני הוא גם O(nlogn) .

נוכיח שזאת אכן המטריצה ההופכית -
ראשית ארצה להוכיח טענת עזר :
נסמן את מטריצה ונדרמונדה עם שורשי היחידה מסדר n כ Mn(wn) ונרצה להוכיח שכאשר מציבים במקום wn את wn1 נקבל

Mn(w)Mn(w1)=nI

האלמנט ה mi,j של המטריצה יהיה לפי כפל מטריצות :

k=0n1w(i1)kw(j1)k=k=0n1w(ij)k

כעת יתקיים שבכל נקודה שבה i=j נקבל n כיוון שאנחנו סוכמים 1 סך הכל n פעמים.
אפשר להסתכל על זה גם כסדרה חשבונית ונקבל

sn=1wn(ij)w(ij)=11wij=0

אחרת, אנחנו יודעים שלכל איבר בשורשי יחידה יש אחד שיבטל אותו בסכום ולכן נקבל 0 . כלומר קיבלנו את nI .
מה שאומר ש

FFT=Mn(w) , FFT1=1nMn(w1)

שזה בידיוק מה שרשמנו למעלה.

כעת רק נרצה לתאר בהליך פסודו קוד את האלגוריתם IFFT שמטרתו תהיה לבצע אינטרפולצייה הפוכה, ממצב של תצוגה לפי נקודות למצב של תצוגה לפי מקדמים :

IFFT([A(w^0), A(w^1),....,A(w^n-1)]):
	return FFT(A(w^n),A(w^n-1),...., A(w))) / n 

הרעיון הוא שהדרך להגיע לוקטור המקדמים באמצעות ההופכית שהראנו שגם היא מטריצת ונדרמונדה עם שורשי יחידה מסדר n ולכן נוכל לנצל את התכונה הזאת כדי להפעיל את אלגוריתם FFT כאשר מתייחסים למקדמים בסדר הפוך (כפי שהם מסודרים במטריצה FFT1).

שימוש ב FFT

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

בעיית פולינום המכפלה מדרגות שונות

קלט : A(x)=i=1n1aixi,B(x)=i=1m1bixi (בלי הגבלת הכלליות נניח mn).
פלט: פולינום המכפלה C(x)=(AB)(x) .

דרך א : נוסיף מקדמי 0 לפולינום B(x) כדי להשוות בדרגות ולאחר מכן נריץ fft.
דרך ב: כפל פולינומים בשיטה הנאיבית O(mn)
דרך ג : נבדוק min(mn,nlogn) ובהתאם לזה נריץ את האלגוריתם הטוב.

נרצה פתרון ב O(nlogm). במבנה נתונים היינו נעזרים בחלוקת המבנה כדי לעשות אלגוריתם כלשהו בזמנים שכאלה. נעשה את אותו דבר על הפולינום.

נניח כי m|n אחרת, פשוט נשלים את n עם מקדמי 0 עד שהתנאי יתקיים (השלמה של לכל היותר m1 איברים).
כעת הפולינום A(x) ייראה כך:

A(x)=i=0m1aixi+i=m2m1aixi+i=m(nm)mn1aixi=i=0m1aixi+xm(amx0+a2m1xm1)+x2m(a2mx0++a3m1xm1)++xm(nm1)(am(nm1)x0++an1xm1)

כעת , נגדיר

k[0,nm1]:Ak(x)=i=0m1akm+ixi

זאת חלוקה שמכסה את הפולינום A(x) ונוכל לסמנו ככה:

A(x)=k=0nm1xkmAk(x)

כעת חילקנו את הפולינום מדרגה חסומה n לnm פולינומים מדרגה חסומה m ונוכל להכפיל כל אחד מהם ב B באמצעות FFT .

C(x)=k=0nm1xkm(BAk)(x)

זמן ריצה : האלגוריתם מבצע nm פעולות כפל של פולינומים שדרגתפ חסומה ב m סך הכל זמן הריצה

O(nmmlogm)=O(nlogm)

בנוסף נדרשת סכימה של התוצאות ב C(x) שהוא פולינום מדרגה 2m1 ויש nm תתי פולינומים לסכום כלומר הסכום הוא בזמן ליניארי O(n) .

שימו לב: פעולת כפל של פולינום P(x) בפולינום xt היא בסה"כ הזזה של כל מקדם pi בפולינום P להיות המקדם pi+t בםולינום המכפלה. לכן, זמן חישוב המכפלה הוא לינארי במספר המקדמים השונים מ־0 של P.

בעיית חישוב מרחקי האמינג בין טקסט לתבנית

הגדרה : בעית התאמת המחזורות Pattern Matching -
קלט - מחרוזת T באורך n ומחרוזת P באורך mn שמייצגת תבנית.
פלט- כל המקומות ב T ש P מופיע בהם.

הגדרה : Hamming Distance - לשתי מחרוזות A,B באורך זהה n מרחק ההאמינג של A,B הוא מספר האינדקסים בהם A,B שונים כלומר:

Ham(A,B)=|{i | A[i]B[i]}|

לדוגמה :
Pasted image 20221030175254.png

הגדרה : בעיית חישוב מרחקי האמינג בין טקסט לתבנית
קלט - מחרוזת T[1..n] באורך n ומחרוזת P באורך mn
פלט - $$\forall_{i\in [0, n-m]} : Ham(P, T[i+1\dots i+m])$$
כאשר Σ={1,0} כלומר הא״ב הבינארי.

אלגוריתם Fischer Paterson
נסמן את הטקסט באופן הבא T=a0a1an1 ואת התבנית P=b0b1bm1
FFT מאפשר לבצע כפל של פולינומים בזמן ליניארי. ננסה להסתכל על על כפל בצורה מופשטת המתארת פעולה בינארית בין שתי אובייקטים ומחזירה מספר. כפל פולינומים דומה לכפל של מספרים מרובה הספרות. אפשר להסתכל על ספרה כמקדם של הבסיס בחזקת i למשל עבור המספר 245 הספרה 2 היא המקדם של 102 כמו בפולינום 2x2+4x+5 . נשים לב שהייצוג של חישוב מספרים בכפל ארוך גם הוא יעבוד במקרה של פולינומים. זה לא משנה ש FFT מחשב את התוצאה באמצעי שאינו כפל ארוך מה שחשוב זה רק עניין הייצוג. מה שיקרה בפועל באלגוריתם שלנו זה שנבין אין זה נראה בייצוג של כפל ארוך ולאחר מכן נשתמש ב FFT אחרי שנעביר את המספר הבינארי לייצוג פולינומי בשיטת מקדמים.

נסתכל על כפל ארוך בין שני מספרים/פולינומים המייצגים את הטקסט והתבנית.
נכפול את T ב Pr כשהסימון הזה מייצג את היפוך סדר האותיות. למשל אם n=5,m=3 נסתכל על הפולינומים (a0a1a2a3a4),(b2b1b0) ונכפול אותם.
הסיבה שכופלים בסדר הפוך היא בגלל איך שעובד חישוב המקדם ה i בפולינום המכפילה. כפי שנאמר למעלה המקדם ci זה k=0iakbik וההתקדמות הזאת אחורה אינה טובה לנו כדי לתאר התאמת מחרוזת כפולינום. נרצה שההיסט תמיד יהיה ממוקם כמו שצריך ובreverse זה מה שקורה.

Pasted image 20221030181341.png
נשים לב שבכל אחת משלוש העמודות האמצעיות אנו נקבל סכום של מכפלת אותיות מקבילות בתבנית ובטקסט, כאשר שמים את התבנית מול הטקסט בהיסטים שונים. אלה יתנו את המקדם במיקום מסויים. הסכום לכאורה אמור לתאר את מספר ההתאמות בטקסט בהיסט מסויים.
נרצה שהסכימה הזו תהיה משמעותית אנחנו נדאג לכך שהסכום יהיה בדיוק מספר המקומות בהם התבנית מתאימה לטקסט.
לצורך כך, נרצה שה"כפל" בין שני תווים יקיים ab=1 אם a=b ו ab=0 אם ab .
בטבלה זה ייראה כך
Pasted image 20221030182019.png
היינו יכולים לעשות כפל בצורה הטריוויאלית ולחשב את הפעולה הנ״ל שרצינו בזמן O(nm) ואז את הבדיקה הנ״ל בזמן O(1). אבל אנחנו רוצים זמן יותר טוב מזה באמצעות אלגוריתם FFT . הבעיה היא שהאלגוריתם הזה תומך בכפל פולינומים כאשר הכפל מוגדר ככפל רגיל בשדה המרוכבים ולא באופן שתיארנו אותו עכשיו (כלומר יהיה לנו בעיות עם מחרוזות שנראות ככה 001001010 למשל).
כדי להתגבר על הבעיה הזאת נפריד את החישוב לשני חישובים.
רצינו לספור את כל המקומות בהם תו התבנית זהה לתו בטקסט בהיסט המתאים. כל מקום כזה עונה בידיוק על אפשרות אחת מתוך השניים

  1. a=b=1
  2. a=b=0
    בשביל הראשון נוכל להשתמש בפועל הכפל הרגילה בין שני מקדמים
    Pasted image 20221030182644.png
    בגלל שזאת פעולת הכפל הרגילה, נוכל להשתמש באלגוריתם FFT באופן מיידי.
    למקרה השני נזדקק לפעולה הבאה :
    Pasted image 20221030182720.png
    כדי לקבל פעולה כזאת באמצעות הכפל הרגיל פשוט נהפוך את הביטים בטקסט ואת הביטים בתבנית ובמצב זה נקבל בידיוק את מה שרצינו
    Pasted image 20221030183036.png

סך הכל נצטרך לעשות שתי פעולות FFT וסכימה של תוצאותיהן, נדע כמה התאמות יש בין התבנית לטקסט בכל היסט. נשים לב שאת ההתאמות נבדוק במקדמים m עד nm1 בגלל שרק במצבים האלה אין ״זליגה״ של היסט של P מאורך המחרוזת T.
המקדם ה i בפולינום התוצאה, ייתן לנו את מספר ההתאמות כאשר ההיסט התחיל באינדקס i.
לבסוף כל מה שצריך זה לקחת את המספר ההתאמות ולחסר אותו מאורך התבנית m כדי לקבל את מספר אי ההתאמות, שזה בידיוק מרחק האמינג. כעת צריך לבדוק האם באחד האינדקסים מספר אי ההתאמות הוא 0 ואז אכן מצאנו התאמה.
סך הכל זמן הריצה O(nlogm) עם האלגוריתם מהתרגיל הקודם.

מרחק האמינג על א״ב גדול יותר

נראה גם כיצד ניתן לחשב את מרחק האמינג על א״ב מהצורה {0,1,2}
בדומה לבעיית המרחק הרגילה, נגדיר את הטקסט כפולינום T=a0a1an1 וגם את התבנית P=b0b1bm1 . המשמעות היא שלכל i,j בגבולות של אורכי הטקסט והתבנית בהתאמה יתקיים ש ai הוא המקדם של הפולינום xi ב T ו bj יהיה xj ב P . כלומר ייצוג לפי מקדמים.

נשים לב שטבלת הכפל הרגילה תיתן לנו

T/P 0 1 2
0 0 0 0
1 0 1 2
2 0 2 4

זה לא ממש עוזר לנו להבין האם יש התאמה בדומה לכפל הרגיל כאשר הא״ב היה רק {1,0}.

באלגוריתם המקורי מה שעשינו היה להחליף בין 1 ל 0 על ידי פעולת not . כעת מה שנעשה הוא פשוט 3 החלפות ל0 כדי שכל החלפה נקבל התאמה לספרה אחרת, כלומר אם באלגוריתם המקורי עשינו פעמיים FFT לכל ספרה. כעת נעשה 3 פעמים.

  1. בכפל הראשון נגדיר את 2=0 ואז נקבל התאמה עבור הספרה 1.
  2. בפעם השנייה נגדיר את 0=1 ו 2,1=0 ואז נקבל התאמה עבור הספרה 0
  3. לבסוף נגדיר 2=1 ואת 1=0 כדי לקבל התאמה עבור הספרה 2.

נזכיר שכופלים PR כל פעם ונשים לב שאת ההחלפות הנ״ל עושים גם על T וגם על P למעשה אפשר לבטא את כפל הפולינומים שנעשה באופן כללי על ידי:

σΣ:FFT(Tσ,PσR)

כאשר Pσ,Tσ הם המחרוזות לאחר ההחלפה של כל התווים ל 0 פרט לσ.

כעת קיבלנו 3 פולינום והאלגוריתם נשאר אותו דבר, מכאן, מבצעים סכימה באינדקסים בהם אין גלישה של התבנית m עד nm1 ונקבל את מספר ההתאמות,
כל אינדקס נחסר מ m כדי לקבל את מספר אי ההתאמות.

זמן הריצה זהה לזמן ריצה המקורי שהוא O(nlogm+n+m) . הסיבה שזה nlogm הוא בגלל שהכפלנו פולינומים מדרגות שונות.

3sum

קלט: מערך A[1n] של מספרים שלמים.
פלט: flag האם קיימים שלושה מספרים x,y,zA כך ש x+y=z .

א. בזמן ריצה O(n2) - נשתמש באלגוריתם 2SUM שכולל מיון של המערך ולאחר מכן נרוץ על איברי המערך ולכל איבר A[i] נבדוק האם קיימים זוג איברים שהסכום שלהם ייתן את A[i] . זה יקרה על ידי החזקת מצביע בתחילת המערך וסופו (נזכיר שהוא ממויין בשלב זה) ואם הסכום גדול מידי נזיז אחורה את המצביע לסוף המערך אחרת נזיז ימינה את הפוינטר שבתחילת המערך.

ב. על ידי שימוש ב FFT : (זה יעבוד בהינתן שמספר האיברים חסום בגודל מאוד גדול למשל 10n1.5)
נייצר מערך B באורך 10n1.5 שכולו אפסים. כעת, לכל xA נכניס B[x]=1. נוכל להתייחס למערך הזה כפולינום בצורת מקדמים למשל עבור המערך A=[1,2,4] הפולינום יהיה x+x2+x4 .
כעת נסמן C=B2 שהגענו אליו באמצעות כפל של B בעצמו עם FFT . זמן הריצה הזה ייקח O(n1.5logn1.5)=O(n1.5logn) .
כעת נוכל פשוט לרוץ על איברי המערך A ולבדוק האם C[A]1 . במצב זה המשמעות היא שהיו שתי חזקות שסכומם יצא xA . כלומר בצורה כללית יותר אם קיימים s,t שסכומם ייתן z משמעות הדבר שקיים בפולינום C איבר מהצורה kxs+t ששניהם שייכים ל A . אם זה לא מתקיים נחזיר false.

שאלת הקורדינטות

בהינתן שתי קבוצות A,B שכל אחת מהן מכילה נקודה דו מימדית מ Z×{1,1} וגם בקבוצה A זה תמיד יהיה 1 ובקבוצה B זה תמיד יהיה 1 .
Pasted image 20221114215437.png|400
נגדיר a,bA,B:δa,b שזה הקטע המחבר את a,b . כמו כן, נגדיר את הקבוצה

LA,B={δa,b  |  aAbB}

כמו כן בדומה לסעיף הקודם נניח שערכי x חסומים מלמעלה על ידי 10n1.5 . נרצה למצוא אלגוריתם שמחשב את מספר נקודות החיתוך השונות ב L עם ציר ה x .

כדי לפתור את זה נשתמש בשיטה מהתרגיל הקודם כלומר נייצג את A על ידי פולינום באמצעות השמה של 1 באינדקס (a,1)A . באופן פורמלי :

PA=(a,1)A:xa

נגדיר בידיוק אותו דבר על B וכעת נבנה פולינום חדש PC=PAPB. את זה נעשה בעזרת האלגוריתם FFT . התשובה תהיה מספר המקדמים השונים מ 0 ב PC

למה זה נכון?
נשים לב שבגלל הנתון של החסמים בציר ה y אנחנו מקבלים שלכל 2 נקודות כאשר אחת מ A והשנייה מ B יתקיים שהחיתוך עם ציר הx היא בידיוק באמצע הישר הזה. כלומר, שיעור ה x של נקודת המפגש תהיה a+b2 . המשמעות היא שהפתרון טמון בעובדה שאנחנו מחפשים בעצם את בעיית 3sum על a,b כלומר אנחנו מחפשים ערכי c שקיימים a,b שיתנו את הסכום הנ״ל. זה יהיה שקול ללחפש ישירות c ו a,b כך ש a+b=c כיוון שאלו מספרים שלמים חסומים ולכן אם מצאנו מספר c שסכומו ייתן a+b2 אז בהכרח 2c=c יהיה בתחום [0,20n1.5] והוא יתאים לנו.

נוכל לטעון אם כן, שקיים c בתחום שציינו שיקיים את זה אמ״מ במקדם של xc בפולינום PC שציינו יהיה שונה מ0.
זה נובע מהוכחת הנכונות של בעיית 3sum . אם קיים סכום כזה אז המקדמים של B[b],A[a] יהיו 1 . ובכפל פולינומים נקבל xa+b כלומר בפולינום PC בייצוג מקדמים יהיה באינדקס a+b=c את הערך 1. זמן הריצה הוא O(n1.5logn).