Nhân hai đa thức là một thao tác cơ bản trong lập trình, nhất là trong mật mã. Giả sử chúng ta có hai đa thức:
Cách tiếp cận tự nhiên nhất mà chúng ta được học ở trường phổ thông là phép nhân phân phối (Elementary Multiplication). Để tìm hệ số của đa thức tích , ta lấy từng hạng tử của nhân với mọi hạng tử của . Trong khoa học máy tính, đây là độ phức tạp với lần lượt là bậc của đa thức . Với (trong các bài toán xử lý tín hiệu hoặc mã hóa), số phép toán lên tới 1.000 tỷ, khiến các hệ thống hiện đại cũng phải "đầu hàng" về mặt thời gian thực.
Sự xuất hiện của FFT (Fast Fourier Transform), cụ thể là thuật toán Cooley-Tukey, đã thay đổi hoàn toàn cuộc chơi này bằng cách đưa độ phức tạp về mức (để cho đơn giản, ta cứ giả định 2 đa thức có cùng bậc , với là bậc lớn nhất trong và ). Ví dụ đưa 2 đa thức về cùng bậc 3 như sau:
Thông thường, chúng ta định nghĩa đa thức qua các hệ số của nó. Nhưng thực ra, chúng ta vẫn có thể định nghĩa một đa thức thông qua các điểm phân biệt mà đồ thị của nó đi qua. Theo định lý cơ bản về đa thức: Một đa thức bậc được xác định duy nhất bởi tập hợp cặp điểm phân biệt.
Ví dụ:
Qua 2 điểm, ta xác định được duy nhất 1 đường thẳng (bậc 1).
Qua 3 điểm, ta xác định được duy nhất 1 đường parabol (bậc 2).
Hãy tưởng tượng bạn có hai đa thức và đã được chuyển sang dạng điểm tại cùng một tập hợp các giá trị .
Để tìm đa thức tích ở dạng điểm, chúng ta chỉ cần tính:
Phép toán này chỉ tốn đúng phép nhân, tức là độ phức tạp , yeah!!!
Vậy tại sao nhanh như vậy mà ta không dùng nó ngay từ đầu? Đó là vì chi phí chuyển đổi:
Chuyển từ Hệ số Điểm (Evaluation): Nếu tính toán thông thường (thế vào đa thức), ta mất .
Chuyển từ Điểm Hệ số (Interpolation): Dùng các phương pháp như Newton hay Lagrange cũng mất .
FFT cho phép chúng ta thực hiện cả hai quá trình chuyển đổi trên chỉ với độ phức tạp .
Để chuyển một đa thức từ dạng Hệ số sang dạng Điểm trong , thuật toán Cooley-Tukey sử dụng chiến lược Chia để trị (Divide and Conquer) dựa trên một lựa chọn toán học cực kỳ thông minh: Căn đơn vị phức (Complex Roots of Unity).
Căn đơn vị phức
Nếu ta chọn các điểm ngẫu nhiên, ta vẫn mất để tính giá trị đa thức. Tuy nhiên, nếu ta chọn là các nghiệm của phương trình , chúng ta có những tính chất đối xứng tuyệt vời. Các điểm này nằm trên vòng tròn đơn vị trong mặt phẳng phức, được xác định bởi công thức:
Với .
Tính chất: . Điều này cho phép ta thu nhỏ bài toán kích thước thành bài toán kích thước một cách đồng nhất.
Giả sử ta có đa thức bậc (với là lũy thừa của 2).
Chúng ta nhóm các số hạng có số mũ chẵn vào một nhóm, và các số hạng có số mũ lẻ vào một nhóm:
Nhóm chẵn:
Nhóm lẻ:
Bây giờ, thay vì giữ nguyên biến , chúng ta đặt . Khi đó:
Đa thức chứa các hệ số chẵn trở thành:
Đa thức chứa các hệ số lẻ trở thành:
Khi thay ngược lại vào hai đa thức trên, ta thấy:
(Chính là phần chẵn của )
(Chính là phần lẻ của )
Vậy, ta có biểu diễn cuối cùng:
Trong FFT, chúng ta không thay bằng các số ngẫu nhiên, mà thay bằng các căn đơn vị phức . Chìa khóa nằm ở tính chất: khi bình phương một căn đơn vị, bậc của nó giảm đi một nửa.
Chúng ta có công thức như đã đề cập ở trên: .
Để tính cho mọi , ta sử dụng công thức tách chẵn-lẻ:
Khi tính toán cho nửa sau của mảng (tức là khi chỉ số nằm trong khoảng ), ta đặt (với ). Lúc này, công thức trở thành:
Với nửa đầu ():
Với nửa sau (, ):
Thay vào công thức tổng quát, ta có:
Tại đây, có 2 biến đổi quan trọng:
Bình phương: . Vì , nên nó bằng .
Số hạng xoay (Twiddle factor): . Mà (đây là tính chất chu kỳ của số phức trên vòng tròn đơn vị), nên nó bằng .
Thay vào biểu thức trên, ta được kết quả cuối cùng:
Cả hai nửa đầu và nửa sau đều dùng chung đúng hai giá trị là và .
Để tính nửa đầu, lấy Tổng.
Để tính nửa sau, lấy Hiệu.
Thay vì phải chạy vòng lặp để tính toàn bộ mất , giờ đây với mỗi cặp , ta chỉ cần tính và một lần, sau đó cộng/trừ là ra được ngay 2 kết quả cho tầng trên. Đây chính là lý do giúp thuật toán giảm độ phức tạp xuống .
Kết quả: Mỗi bước tính toán ở tầng dưới cung cấp dữ liệu cho hai giá trị ở tầng trên chỉ bằng một phép nhân và hai phép cộng/trừ. Đây chính là cấu trúc Butterfly nổi tiếng giúp giảm số lượng phép tính từ xuống còn .

Bức ảnh này giải thích chính xác cơ chế "phép màu" đã được đề cập:
Đầu vào (bên trái): Hai giá trị và từ tầng tính toán trước đó.
Hệ số xoay (Twiddle Factor - ): Giá trị phức được nhân với đầu vào thứ hai trước khi kết hợp.
Điểm kết hợp (Cánh bướm):
Phép cộng (trên): Tạo ra đầu ra thứ nhất: .
Phép trừ (dưới): Tạo ra đầu ra thứ hai: .
Cấu trúc đối xứng này cho phép máy tính tái sử dụng các kết quả tính toán trung gian, biến đổi độ phức tạp từ thành .
Để nhân hai đa thức và , quy trình gồm 3 bước:
Bước 1: Mở rộng và Biến đổi (Padding & Forward FFT)
Trước khi bắt đầu, có một chi tiết kỹ thuật cực kỳ quan trọng: Xử lý bậc của đa thức kết quả.
Nếu có bậc và có bậc , thì đa thức tích sẽ có bậc .
Zero Padding: Chúng ta phải thêm các hệ số vào và sao cho tổng số hệ số của mỗi đa thức ít nhất bằng bậc của , và phải là một lũy thừa của 2 (ví dụ: ).
Thực thi: Áp dụng FFT Cooley-Tukey để chuyển và từ dạng hệ số sang dạng điểm (tại các căn đơn vị ).
Bước 2: Nhân trong miền giá trị (Point-wise Multiplication)
Với các giá trị điểm đã có, ta tính giá trị của đa thức tích tại từng điểm tương ứng:
Chỉ cần đúng phép nhân số phức, chúng ta đã có đầy đủ đại diện dạng điểm của đa thức tích.
Bước 3: Biến đổi ngược (Inverse FFT - IFFT)
Bây giờ, chúng ta đang có các giá trị , nhưng mục tiêu là phải xác định kết quả là các hệ số . Lúc này, ta cần đến quá trình biến đổi ngược Inverse FFT. Thú vị thay, thuật toán cho IFFT gần như y hệt FFT. Sự khác biệt duy nhất nằm ở hai điểm:
Thay thế hệ số xoay bằng nghịch đảo của nó (tương đương với việc đổi dấu phần ảo trong số phức).
Sau khi tính xong, tất cả kết quả phải được chia cho
Hãy nhìn lại toàn bộ hành trình:
FFT cho A và B:
Nhân điểm:
IFFT cho C:
Tổng cộng: .
Để cài đặt FFT Cooley-Tukey, chúng ta sử dụng cấu trúc đệ quy chia mảng hệ số thành hai nửa: vị trí chẵn và vị trí lẻ.
Hàm FFT (Hệ số Điểm)
HÀM FFT(Mảng_Hệ_Số A):
N = Độ dài của A
NẾU N == 1:
TRẢ VỀ A // Trường hợp cơ bản: Đa thức bậc 0 là chính nó
// BƯỚC 1: CHIA (Divide)
A_chẵn = [A[0], A[2], ..., A[N-2]]
A_lẻ = [A[1], A[3], ..., A[N-1]]
// BƯỚC 2: TRỊ (Conquer - Đệ quy)
Y_chẵn = FFT(A_chẵn)
Y_lẻ = FFT(A_lẻ)
// BƯỚC 3: KẾT HỢP (Combine - Cấu trúc Cánh bướm)
Mảng_Kết_Quả Y = Mảng rỗng kích thước N
Hệ_số_xoay_gốc = e^(i * 2π / N)
w = 1
CHO k CHẠY TỪ 0 ĐẾN N/2 - 1:
// Tính toán dựa trên tính đối xứng và chu kỳ
Y[k] = Y_chẵn[k] + w * Y_lẻ[k]
Y[k + N/2] = Y_chẵn[k] - w * Y_lẻ[k]
w = w * Hệ_số_xoay_gốc // Xoay dần trên vòng tròn đơn vị
TRẢ VỀ Y
Quy trình nhân hai đa thức và
HÀM NHÂN_ĐA_THỨC(P1, P2):
1. Padding: Thêm các số 0 vào P1, P2 sao cho độ dài N là lũy thừa của 2
(N phải đủ lớn để chứa bậc của đa thức kết quả).
2. Chuyển đổi sang dạng điểm:
Điểm_P1 = FFT(P1)
Điểm_P2 = FFT(P2)
3. Nhân trực tiếp (Point-wise):
Điểm_Kết_Quả = Mảng chứa (Điểm_P1[i] * Điểm_P2[i]) với mọi i từ 0 đến N-1
4. Chuyển ngược về dạng hệ số:
Hệ_Số_Kết_Quả = IFFT(Điểm_Kết_Quả)
(IFFT dùng thuật toán y hệt FFT nhưng đổi dấu số phức và chia kết quả cho N)
TRẢ VỀ Hệ_Số_Kết_QuảNo comments yet. Be the first to comment!