1、介紹。
在類FourierUtils的fftProgress方法中,有這個代碼段,我們可以將Complext.euler(flag * i)提前計算好,設置大小爲2次冪N,如果沒有的話,也要調節到2次冪N。我們設置大小爲N,求得複數數組,前半部分存儲給FFT使用的,後半部分給IFFT使用。
2、其中複數類和工具類代碼不變。可以直接使用文章傅里葉變換 二維快速傅里葉變換(快速的二維離散傅里葉變換、分治法)的複數類和工具類代碼。
3、FourierUtils類(只粘貼修改的部分)
package com.zxj.reptile.utils.image;
import com.zxj.reptile.utils.number.Complex;
import com.zxj.reptile.utils.number.NumberUtils;
public class FourierUtils {
/**
* 一維快速傅里葉變換FFT 當N不是2次冪時,自動補0
*
* @param array 一維數組
*/
public static Complex[] getFft(double[] array, Complex[] eulerComplexArray) {
//實際的長度
int length = array.length;
//調節過的長度
int variableLength = eulerComplexArray.length;
Complex[] variableArray = new Complex[variableLength];
for (int i = 0; i < variableLength; i++) {
if (i < length) {
variableArray[i] = new Complex(array[i]);
} else {
variableArray[i] = new Complex();
}
}
return fftProgress(variableArray, 0, eulerComplexArray);
}
/**
* 一維逆快速傅里葉變換IFFT 將結果超過realLength的全部移除
*
* @param complexArray 一維複數數組
* @param realLength 返回的數組長度
*/
public static double[] getInverseFft(Complex[] complexArray, Complex[] eulerComplexArray, int realLength) {
int length = complexArray.length;
Complex[] resultArrays = fftProgress(complexArray, eulerComplexArray.length / 2, eulerComplexArray);
double[] array = new double[realLength];
//每個數都要除以N
for (int i = 0; i < realLength; i++) {
array[i] = NumberUtils.getRound(resultArrays[i].getReal() / length, 2);
}
return array;
}
/**
* 一維快速傅里葉變換FFT和一維逆快速傅里葉變換IFFT遞歸過程
*
* @param complexArray 一維複數數組
* @param offset FFT爲0,IFFT爲N/2
* @param eulerComplexArray 歐拉數組
*/
private static Complex[] fftProgress(Complex[] complexArray, int offset, Complex[] eulerComplexArray) {
int length = complexArray.length;
if (length == 2) {
//F(0)=f(0)+f(1),F(1)=f(0)-f(1)
return new Complex[]{
complexArray[0].add(complexArray[1]),
complexArray[0].sub(complexArray[1]),};
} else if (length == 1) {
return complexArray;
}
int middle = length / 2;
//
Complex[] a = new Complex[middle];//a(x)=f(2x)
Complex[] b = new Complex[middle];//b(x)=f(2x+1)
for (int i = 0; i < middle; i++) {
a[i] = complexArray[2 * i];
b[i] = complexArray[2 * i + 1];
}
//
Complex[] complexesA = fftProgress(a, offset, eulerComplexArray);//計算G(k)
Complex[] complexesB = fftProgress(b, offset, eulerComplexArray);//計算P(k)
Complex[] resultArray = new Complex[length];//F(k)
int multiple = eulerComplexArray.length / length;
for (int i = 0; i < middle; i++) {
//e^(2Pi*k/N)
Complex complex = eulerComplexArray[multiple * i + offset].mul(complexesB[i]);
//F(k)=G(k)+(e^(2Pi*k/N))*P(k)
resultArray[i] = complexesA[i].add(complex);
//F(k+(N/2))=G(k)+(e^(2Pi*(k+(N/2))/N))*P(k+(N/2))
resultArray[i + middle] = complexesA[i].sub(complex);
}
return resultArray;
}
/**
* 二維快速傅里葉變換FFT 當N不是2次冪時,自動補0
*
* @param arrays 二維數組
*/
public static Complex[][] getFft(double[][] arrays, Complex[] eulerComplexArray) {
//實際的行列
int row = arrays.length;
int column = arrays[0].length;
//調節過的長度
int variableLength = eulerComplexArray.length;
Complex[][] complexArrays = new Complex[variableLength][variableLength];
for (int i = 0; i < variableLength; i++) {
for (int j = 0; j < variableLength; j++) {
if (i < row && j < column) {
complexArrays[i][j] = new Complex(arrays[i][j]);
} else {
complexArrays[i][j] = new Complex();
}
}
}
return fftProgress(complexArrays, 0, eulerComplexArray);
}
/**
* 二維逆快速傅里葉變換IFFT 將結果行列分別超過realRow和realColumn的全部移除
*
* @param complexArrays 二維複數數組
*/
public static double[][] getInverseFft(Complex[][] complexArrays, Complex[] eulerComplexArray, int realRow, int realColumn) {
int size = complexArrays.length * complexArrays[0].length;
complexArrays = fftProgress(complexArrays, eulerComplexArray.length / 2, eulerComplexArray);
double[][] arrays = new double[realRow][realColumn];
//每個數
for (int i = 0; i < realRow; i++) {
for (int j = 0; j < realColumn; j++) {
arrays[i][j] = NumberUtils.getRound(complexArrays[i][j].getReal() / size, 2);
}
}
return arrays;
}
/**
* 二維快速傅里葉變換DFT和二維逆快速傅里葉變換IDFT處理過程
*
* @param complexArrays 二維複數數組
* @param offset FFT爲0,IFFT爲N/2
* @param eulerComplexArray 歐拉數組
*/
private static Complex[][] fftProgress(Complex[][] complexArrays, int offset, Complex[] eulerComplexArray) {
int length = complexArrays.length;
//對每行進行一維DFT
for (int i = 0; i < length; i++) {
complexArrays[i] = fftProgress(complexArrays[i], offset, eulerComplexArray);
}
//倒置,即行和列互換
complexArrays = Complex.transform(complexArrays);
length = complexArrays.length;
//對每行進行一維DFT,實際上是對沒倒置前數組的列做一維DFT
for (int i = 0; i < length; i++) {
complexArrays[i] = fftProgress(complexArrays[i], offset, eulerComplexArray);
}
//倒置回來
complexArrays = Complex.transform(complexArrays);
return complexArrays;
}
/**
* 提前生成要用到的關於歐拉計算的數組,因爲考慮過將大小調節到2的冪,所以只適合FFT
*
* @param length 數組大小
*/
public static Complex[] getEulerComplex(int length) {
//調節過的長度
int variableLength = (int) NumberUtils.getVariablePow(length, 2);
int middle = variableLength / 2;
double flag = 2 * Math.PI / variableLength;//2Pi / N
Complex[] eulerComplexArray = new Complex[variableLength];
for (int i = 0; i < middle; i++) {
double angle = flag * i;
//- 2PI * k / N 給FFT使用
eulerComplexArray[i] = Complex.euler(-angle);
//2 * PI * k / N 給IFFT使用
eulerComplexArray[i + middle] = Complex.euler(angle);
}
return eulerComplexArray;
}
}
4、主要流程代碼。
package com.zxj.reptile.test.image;
import com.zxj.reptile.utils.image.FourierUtils;
import com.zxj.reptile.utils.number.ArrayUtils;
import com.zxj.reptile.utils.number.Complex;
import java.util.Arrays;
public class FourierTest {
public static void main(String[] args) {
System.out.println("------開始------");
testDft(1000000);//一維離散傅里葉變換
// testDft(900, 800);//二維離散傅里葉變換
System.out.println("------結束------");
}
private static void testDft(int row, int column) {
long time;
System.out.println("原數據: ");
double[][] arrays = ArrayUtils.getRandom(row, column, 0, 255);
System.out.println(String.format("大小爲%d行%d列", row, column));
System.out.println();
//
time = System.currentTimeMillis();
Complex[] eulerComplexArray = FourierUtils.getEulerComplex(row > column ? row : column);
System.out.println("生成關於歐拉計算的數組花費時間 :" + (System.currentTimeMillis() - time));
System.out.println();
//
System.out.println("二維快速傅里葉變換FFT: ");
time = System.currentTimeMillis();
Complex[][] fftArrays = FourierUtils.getFft(arrays, eulerComplexArray);
System.out.println("花費時間 :" + (System.currentTimeMillis() - time));
System.out.println();
//
System.out.println("二維逆快速傅里葉變換IFFT: ");
time = System.currentTimeMillis();
double[][] inverseFftArrays = FourierUtils.getInverseFft(fftArrays, eulerComplexArray, row, column);
System.out.println("花費時間 :" + (System.currentTimeMillis() - time));
if (ArrayUtils.equals(arrays, inverseFftArrays)) {
System.out.println("FFT變換成功");
} else {
System.out.println("FFT變換失敗");
}
System.out.println();
}
private static void testDft(int length) {
long time;
System.out.println("原數據: ");
System.out.println(String.format("大小爲%d", length));
double[] array = ArrayUtils.getRandom(length, 0, 255);
System.out.println();
//
time = System.currentTimeMillis();
Complex[] eulerComplexArray = FourierUtils.getEulerComplex(length);
System.out.println("生成關於歐拉計算的數組花費時間 :" + (System.currentTimeMillis() - time));
System.out.println();
//
System.out.println("一維快速傅里葉變換FFT: ");
time = System.currentTimeMillis();
Complex[] fftArray = FourierUtils.getFft(array, eulerComplexArray);
System.out.println("花費時間 :" + (System.currentTimeMillis() - time));
System.out.println();
//
System.out.println("一維逆快速傅里葉變換IFFT: ");
time = System.currentTimeMillis();
double[] inverseFFTArray = FourierUtils.getInverseFft(fftArray, eulerComplexArray, array.length);
System.out.println("花費時間 :" + (System.currentTimeMillis() - time));
if (Arrays.equals(array, inverseFFTArray)) {
System.out.println("IFFT成功");
} else {
System.out.println("IFFT失敗");
}
System.out.println();
}
}
5、測試一維快速傅里葉。在文章傅里葉變換 一維快速傅里葉變換(快速的一維離散傅里葉變換、分治法)中測試大小爲1百萬時,花費了時間6s多。而現在只要4s左右。
6、測試二維快速傅里葉。在文章 傅里葉變換 二維快速傅里葉變換(快速的二維離散傅里葉變換、分治法)中測試大小爲行爲800,列爲900時,花費了時間4s多。而現在只要2s左右。