多項式乘法為什麼要繞一圈去頻域算?因為頻域裡乘法是 O(n),轉換的代價比直接算還小。

問題:大數/多項式乘法的瓶頸

兩個 n 次多項式相乘,係數對係數直接算是 O(n²)。兩個 n 位大數相乘同理——高中乘法。

n = 10^6 時,O(n²) = 10^12,絕對超時。

FFT 的突破:換一種表示法,讓乘法變簡單。

兩種多項式表示法

係數表示:A(x) = a₀ + a₁x + a₂x² + ... + aₙxⁿ
  → 兩個 n 次多項式相乘,直接算需要 O(n²)
 
點值表示:用 n+1 個「(輸入, 輸出)」對唯一確定多項式
  A 的點值 × B 的點值 = (A×B) 的點值
  → 逐點相乘,O(n)

關鍵問題:怎麼在係數表示和點值表示之間快速轉換?

單位根讓轉換快起來

選 n 個特殊的輸入點——n 次單位根(ω, ω², ω³, …, ωⁿ = 1):

ωₙ = e^(2πi/n) = cos(2π/n) + i×sin(2π/n)
 
特殊性質:ωₙ^(k+n/2) = -ωₙ^k   (消去引理)

有了這個性質,可以把 n 點 DFT 拆成兩個 n/2 點 DFT:

A(x) = A_even(x²) + x × A_odd(x²)
 
A[k]       = A_even[k] + ω^k × A_odd[k]   (蝴蝶操作)
A[k + n/2] = A_even[k] - ω^k × A_odd[k]

每層 n 次操作,共 log n 層,總 O(n log n)。

完整流程

多項式乘法(A × B):
 
1. FFT(A 的係數) → A 在 n 個單位根的點值
2. FFT(B 的係數) → B 在 n 個單位根的點值
3. 逐點相乘 → (A×B) 的點值,O(n)
4. IFFT(逆變換) → (A×B) 的係數
 
IFFT = FFT 的共軛版本 + 除以 n

競程中的 NTT(數論變換)

FFT 用複數浮點運算,精度問題在競程中很麻煩(係數可能是整數,要求精確答案)。

NTT 用模質數的整數單位根,避免浮點——競程標準做法,模數通常選 998244353(= 119 × 2²³ + 1,保證 2²³ 次方的 NTT 可行)。

好消息是你幾乎不用重寫演算法。NTT 跟 FFT 的骨架一模一樣(一樣拆奇偶、一樣蝴蝶操作),只是把複數單位根 ω 換成模意義下的「原根」——998244353 的原根是 3:

long g  = 3;                              // 998244353 的原根
long wn = modPow(g, (MOD - 1) / n, MOD);  // 模意義下的 n 次主根,取代 e^(2πi/n)
// 蝴蝶操作、拆奇偶、log n 層,全部照 FFT 那套,只是乘法都改成模乘

三種做法擺一起,取捨就清楚了:

方法時間精度
暴力多項式乘法O(n²)精確
FFTO(n log n)浮點誤差
NTTO(n log n)模意義下精確

要精確整數答案(競程判對錯)就用 NTT;資料本來就是浮點、或不在意末位誤差(信號、圖像)就用 FFT。

哪裡用到?

  • 大數乘法:n 位 × n 位,O(n log n)
  • 字串匹配(卷積匹配):模式中有通配符時,轉成多項式乘法
  • 「差為 k 的點對數量」:兩個頻率陣列的卷積
  • 圖像處理:高斯模糊、銳化等濾波操作底層是卷積,卷積用 FFT 加速

🎬 互動視覺化FFT 動畫 — 看係數表示怎麼被單位根「取樣」成點值、蝴蝶操作一層層合併,再 IFFT 轉回係數,繞頻域這一圈為什麼比直接乘還快,用動的比用想的清楚。


FFT 的精髓是:在時域做乘法很貴,去頻域後乘法免費——轉換的代價 O(n log n) 比直接算 O(n²) 更值得付。

接下來往哪走