X^n+1=0上的FFT和IFFT(基2)——C語言實現

我們一般意義上學習的FFT都是基於x^{n}-1=0的,即FFT中的單位根我們取的是\left ( e\tfrac{2\pi i}{n} \right ),但是在某些情況下我們需要x^{n}+1=0上的FFT和IFFT變換。

1、直接想到的思路是把x^{n}-1=0的根替換成x^{n}+1=0的根。

解法:x^{n}+1=0的根可以使用x^{2n}-1=0的2n個根中的奇數次根得到,即e\tfrac{2\pi \left ( 2i+1 \right )}{2n},但是這種做法在FFT運算中可行,在IFFT逆運算下則不可行,我們一般的IFFT運算時把\left ( e\tfrac{2\pi i}{n} \right )替換成\left (e \tfrac{-2\pi i}{n} \right ),並且最後除以一個n得到IFFT運算的結果。如下

 X(j)=\sum_{k=0}^{N-1}A(k)W^{jk}

A(j)=\frac{1}{N}\sum_{k=0}^{N-1}X(k)W^{-jk}   

 W=e\tfrac{2\pi i}{N} , j=0,1,2,3,...N-1

但是我們需要在x^{n}+1=0上做IFFT變換的時候不能簡單的把根替換成e\tfrac{-2\pi \left ( 2i+1 \right )}{2n},因爲根據FFT的點值多項式的形式,只有根是\left ( e\tfrac{2\pi i}{n} \right )的形式的時候,纔可以使用A(j)=\frac{1}{N}\sum_{k=0}^{N-1}X(k)W^{-jk}

因爲根是\left ( e\tfrac{2\pi i}{n} \right ) 的形式的時候,Dx=A,x=D^{-1}A,D^{-1}(w^{jk}) = \frac{1}{N}D(w^{-jk})

x^{n}+1=0上IFFT求逆的時候,D^{-1}(w^{jk}) = \frac{1}{N}D(w^{-jk})不成立,直接替換根的做法是不可行的。

 

2、新的做法,擴展到2n次

設 n=2^{k},m = 2n,\varphi (x) = x^{n} + 1,f(x)是n次多項式。

x^{m} - 1 = (x^{n}+1)(x^{n}-1)

令:l(x) = -\frac{1}{2}(x^{n}-1)

則有:l(x)mod(x^{n}+1) = 1,l(x)mod(x^{n}-1)=0

 

FFT計算步驟:

(1)計算F(x) = f(x)\cdot l(x) = -\frac{1}{2}f(x)\cdot x^{n}+\frac{1}{2}f(x)

(2)此時F(x)是m次方的,計算F(x)在x^{m} - 1=0上的FFT(就是以前一般形式的FFT)

(3)輸出F(x) 的FFT變換之後的奇數項,即爲f(x)在x^{n}+1=0上的FFT結果

 

IFFT計算步驟:

FFT(f(x))是f(x)在x^{n}+1=0上的FFT結果

(1)將FFT(f(x))擴展,使其奇數項爲FFT(f(x)),偶數項爲0,擴展到2n次

(2)使用2n階IFFT求出擴展後多項式的逆變換的值

(3)設(2)中逆變換對應的擴展多項式逆變換爲f^{'}(x),令f^{'}(x) = a(x)+b(x)\cdot x^{n}

還原出來的n次f(x) = a(x)-b(x)

 

大致整體思路就是擴展到2n次,然後使用x^{2n}-1=0上的FFT和IFFT求出x^{n}+1=0上的FFT和IFFT變換

下面貼C語言代碼:


#include "pch.h"
#define _CRT_SECURE_NO_WARNINGS
#include "stdlib.h"
#include "math.h"
#include "stdio.h"


#define N 8
#define MAXN 100

#define Pi  3.1415927   //定義圓周率Pi
#define LEN sizeof(struct Compx)  //定義複數結構體大小

//-----定義複數結構體-----------------------
typedef struct Compx
{
	double real;
	double imag;
}Compx;

//-----複數乘法運算函數---------------------
struct Compx mult(struct Compx b1, struct Compx b2)
{
	struct Compx b3;
	b3.real = b1.real*b2.real - b1.imag*b2.imag;
	b3.imag = b1.real*b2.imag + b1.imag*b2.real;
	return(b3);
}

//-----複數減法運算函數---------------------
struct Compx sub(struct Compx a, struct Compx b)
{
	struct Compx c;
	c.real = a.real - b.real;
	c.imag = a.imag - b.imag;
	return(c);
}

//-----複數加法運算函數---------------------
struct Compx add(struct Compx a, struct Compx b)
{
	struct Compx c;
	c.real = a.real + b.real;
	c.imag = a.imag + b.imag;
	return(c);
}

void fft(Compx *a, int n, int inv);
void fft_1(Compx *a, int n, int inv);

int main()
{
	int i;

	int x[N] = { 0 }, y[N] = { 0 };

	printf("\nN=%d\n", N);
	printf("\n輸入兩個多項式的係數,輸入係數爲N多項式長度的一半\n");
	printf("\n輸入第一個多項式的係數\n");
	for (i = 0; i < N / 2; i++)
	{
		scanf("%d", &x[i]);
	}

	struct  Compx * a = (struct Compx *)malloc(N*LEN);			//爲結構體分配存儲空間
	struct  Compx * b = (struct Compx *)malloc(N*LEN);
	struct  Compx * c = (struct Compx *)malloc(N*LEN);
	struct  Compx * F = (struct Compx *)malloc(2*N*LEN);

	//初始化======================================= 
	printf("\na多項式初始化:\n");
	for (i = 0; i < N; i++)
	{
		a[i].real = x[i];
		a[i].imag = 0;
		printf("%.4f ", a[i].real);
		printf("+%.4fj  ", a[i].imag);
		printf("\n");
	}

	/*--------------x^2n-1=0的解法----start----------*/
	printf("\n--------------------------FFT---------------------------------\n");
	int m = 2 * N;
	int n = N;
	double f[2 * N] = { 0 };

	for (i = 0; i < N; i++) {
		f[i] = 0.5 * x[i];
	}
	for (i = N; i < 2*N; i++) {
		f[i] = -0.5 * x[i-N];
	}

	printf("\nf多項式初始化:\n");
	for (i = 0; i < 2*N; i++)
	{
		F[i].real = f[i];
		F[i].imag = 0;
		printf("%.4f ", F[i].real);
		printf("+%.4fj  ", F[i].imag);
		printf("\n");
	}

	fft(F, m, 1);
	printf("\nF多項式FFT計算結果:\n");
	for (i = 0; i < 2*N; i++)
	{
		printf("%.4f ", F[i].real);
		printf("+%.4fj  ", F[i].imag);
		printf("\n");
	}

	for (i = 0; i < N; i++ ) {
		a[i] = F[2 * i + 1];
	}


	printf("\n--------------------------IFFT---------------------------------\n");
	fft(F, m, -1);
	for (i = 0; i < m; i++) {
		F[i].real = F[i].real / m;
		F[i].imag = F[i].imag / m;
	}
	printf("\nF多項式IFFT計算結果:\n");
	for (i = 0; i < 2 * N; i++)
	{
		printf("%.4f ", F[i].real);
		printf("+%.4fj  ", F[i].imag);
		printf("\n");
	}

	//F(x)的低次
	double temp_low[N] = { 0 };
	double temp_high[N] = { 0 };
	for (i = 0; i < N; i++)
	{
		temp_low[i] = F[i].real;
	}
	for (i = N; i < 2*N; i++)
	{
		temp_high[i-N] = F[i].real;
	}
	double res[N] = { 0 };
	for (i = 0; i < N; i++)
	{
		res[i] = temp_low[i] - temp_high[i];
	}

	printf("\nIFFT計算結果:\n");
	for (i = 0; i < N; i++)
	{
		printf("%.4f", res[i]);
		printf("\n");
	}





	/*--------------x^2n+1=0的解法----end----------*/

	//fft_1(a, N, 1);

	printf("\n第一個多項式FFT計算結果:\n");
	for (i = 0; i < N; i++)
	{
		printf("%.4f ", a[i].real);
		printf("+%.4fj  ", a[i].imag);
		printf("\n");
	}

	return 0;
}


//x^n+1=0的FFT形式
void fft_1(Compx *a, int n, int inv) {
	if (n == 1)return;
	int mid = n / 2;
	static Compx b[MAXN];
	int i;
	for (i = 0; i < mid; i++) {
		b[i] = a[i * 2];
		b[i + mid] = a[i * 2 + 1];
	}

	for (i = 0; i < n; i++)
		a[i] = b[i];
	fft(a, mid, inv);
	fft(a + mid, mid, inv);//分治
	for (i = 0; i < mid; i++)
	{
		Compx x;
		x.real = cos(2 * Pi*(2*i+1) / (2*n));
		x.imag = inv * sin(2 * Pi*(2 * i + 1) / (2*n));

		b[i] = add(a[i], mult(x, a[i + mid]));
		b[i + mid] = sub(a[i], mult(x, a[i + mid]));
	}
	for (i = 0; i < n; i++)
		a[i] = b[i];
}

//x^n-1=0的FFT形式
void fft(Compx *a, int n, int inv) {
	if (n == 1)return;
	int mid = n / 2;
	static Compx b[MAXN];
	int i;
	for (i = 0; i < mid; i++) {
		b[i] = a[i * 2];
		b[i + mid] = a[i * 2 + 1];
	}

	for (i = 0; i < n; i++)
		a[i] = b[i];
	fft(a, mid, inv);
	fft(a + mid, mid, inv);//分治
	for (i = 0; i < mid; i++)
	{
		Compx x;
		x.real = cos(2 * Pi*i / n);
		x.imag = inv * sin(2 * Pi*i / n);

		b[i] = add(a[i], mult(x, a[i + mid]));
		b[i + mid] = sub(a[i], mult(x, a[i + mid]));
	}
	for (i = 0; i < n; i++)
		a[i] = b[i];
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章