多項式乘法(FFT)
1 前言
作為一名OI選手,至今未寫過fft相關的部落格,真是一大遺憾,這也導致我並沒有真正推過fft的所有式子
這一篇fft的部落格我將詳細介紹多項式乘法,易於理解,主要是為了等我啥時候忘了回來看,當然,一些公式會有些枯燥,如果是初學者請耐心看完哦,還有,畢竟這是手寫出來的,如果有錯誤,歡迎指正!
2 介紹
本欄用來普及一些知識和對FFT的思路進行描述
多項式乘法,顧名思義,首先是講到多項式,那麼什麼是多項式呢?
2.1 多項式
首先是多項式的定義,想必大家都知道(你上過初中吧),而在這裡,我們所說的多項式都是單個未知數x的
所以,在我們正常人眼中的一個次多項式就是形如
沒錯,這就是大名鼎鼎的係數表示法
然後呢,由於在後面要用到,所以我在這裡再介紹一種點值表示法
就是將n個不同的值
分別帶入
,獲得n個結果
,這n對數
就可以表示出這個多項式
看到這裡,如果你是初學者,你一定會感到非常迷茫,這為什麼對呢?
看到這裡,如果你是個FFT高手,你可能會感到迷茫,這為什麼對呢?
(dalao勿噴)
這張圖片裡係數表示法相當於是最右側的那個矩陣
而點值表示法則包含了左邊的兩個矩陣,可以通過這兩個矩陣計算出最右側的那個矩陣,所以兩種表示法是等價的
撒花
注:另外要說的是,由於演算法需要,本部落格所說的n次多項式都預設n是2的冪次(如果不足可以新增0來補)
2.2多項式的乘法
在做了那麼久的各種數學題後,我對多項式乘法有了有了的理解
對於一個一般的給定係數表示法的多項式乘法問題
比如兩個n次多項式A(x),B(x),給出係數表示法,求它們的乘積C(x)
分別列舉兩個多項式中的每一項,分別是
,所以總複雜度為
這是一個很方便的做法
你可以發現一件很有趣的事情,那就是如果給出的是點值表示法,並且兩個多項式的x分別對應相等,那麼把y對應相乘,就能 的獲取乘積的點值表示法
2.3 快速傅立葉變換(FFT)
那麼,FFT是用來幹什麼的呢?
對於一個多項式乘法問題,當給出係數表示法的時候,
的複雜度有時候並不足夠優越,而FFT就是一個能使多項式乘法做到
的一個演算法,具體的原理其實非常清晰
- 兩個多項式的係數表示法
求值,O(nlogn) - 兩個多項式的點值表示法
點值乘法,O(n) - 兩個多項式乘積的點值表示法
插值,O(nlogn) - 兩個多項式乘積的係數表示法
是不是一目瞭然呢?當然,要具體實現,還需要細細說來
3 實現
現在你已經大致知道FFT要幹什麼了,現在你已經會在點值情況下 進行多項式乘法,剩下的就是要解決兩個問題——求值與插值了
3.1 暴力演算法( )
要先做題,必先暴力
首先是求值,加入你現在隨便找了n個互不相同的x,帶入其中,是什麼複雜度呢
的
然後是插值,有一個非常妙的方法,假設所有的a都是未知數,那麼這個問題就變成了經典的高斯消元問題,複雜度
不好意思,這兩個操作的複雜度都光榮的在
以上,使得當前這個演算法的總複雜度為
,比文章開始的那個
都要差,不要灰心,既然複雜度不優,那就循序漸進的優化
3.2 離散傅立葉變換(通過優化使上面演算法複雜度降到 ,請仔細看完,這是基礎)
你會發現,點值表示法有一個很好的特性,就是那個代入的x可以自己選擇
離散傅立葉變換的思路是將n個x的值取n個單位根(模長為一的複數)
複數(這是一個知識拓展框)
這個數,在實數範圍內是不存在的,所以拓展出複數這一概念,設 ,複數就是能夠被表示為 的數。所以對一個複數,可以用有序數對(x,y)表示,在座標軸上有對應的點,而這個複數就是從(0,0)到(x,y)的一條有向線段(
只會向量的同學可以把它看成向量),而這個複數的模長就等於(0,0)到(x,y)的距離
由於複數是數,所以也有各種運算
加法:(a+bi)+(c+di)=(a+c)+(b+d)i
減法:(a+bi)-(c+di)=(a-c)+(b-d)i
乘法:(a+bi)*(c+di)=(ac-bd)+(ad+bc)i
當然,C++有專門的complex變數可以宣告,但是不推薦使用!!!
為什麼呢?因為FFT本身就有一定的常數,如果再用系統complex常數會更大,所以推薦自己手寫struct
那麼什麼是單位根呢?
3.2.1 單位根
單位根所在的點是把單位圓(以原點為圓心,半徑為1的圓)從(0,1)開始平均分成n份的分割點
如下圖,這就是n=8時的單位圓,綠色圓上的紅點就是單位根所在的點
從(0,1)開始逆時針將這n個點編號,所表示的單位根分別為
,特殊的,
被稱為n次單位根。容易發現每個單位根都非常好算,即
這個用三角函式的想法非常好證
知道了這個之後,你會發現很多性質
性質1:
證明: