高速フーリエ変換の動作チェック(C++)
以前、JavaScriptで作成した高速フーリエ変換用関数をC言語に書き直しました。 シュレディンガー方程式を数値的に解くためにこれを用いる予定です。
元の関数と逆変換の結果
展開係数
プログラムソース
////////////////////////////////////////////////////////////////////
// 高速フーリエ変換
////////////////////////////////////////////////////////////////////
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdio>
#include <iomanip>
#include <stdio.h>
#include <complex>
#include <direct.h>
/////////////////////////////////////////////////////////////////
//項数と空間距離
const int N = 128;
double L = 3.0;
//高速フーリエ変換
void FFT(double* an, double* bn, int N, bool Inverse) {
/////////////////////////////////////////
//参考:Javaで学ぶシミュレーションの基礎
/////////////////////////////////////////
// 入力
// N : 項数(2のべき乗)
// an : 実数配列(順変換:実数空間データを項数Nで指定、逆変換:展開係数a(n))
// bn : 実数配列(順変換:虚数空間データを項数Nで指定、逆変換:展開係数b(n))
// Inverse : 逆変換の場合に true
/////////////////////////////////////////
// 出力
// an : 実数配列(順変換:展開係数a(n)、逆変換:実数空間データ)
// bn : 実数配列(順変換:展開係数b(n)、逆変換:虚数空間データ)
/////////////////////////////////////////
int ff = Inverse ? 1 : -1;
int* rot = new int[N];
for (int i = 0; i < N; i++) rot[i] = 0;
int nhalf = N / 2, num = N / 2;
double sc = 2.0 * M_PI / N;
while (num >= 1) {
for (int j = 0; j < N; j += 2 * num) {
int phi = rot[j] / 2;
int phi0 = phi + nhalf;
double c = cos(sc * phi);
double s = sin(sc * phi * ff);
for (int k = j; k < j + num; k++) {
int k1 = k + num;
double a0 = an[k1] * c - bn[k1] * s;
double b0 = an[k1] * s + bn[k1] * c;
an[k1] = an[k] - a0;
bn[k1] = bn[k] - b0;
an[k] = an[k] + a0;
bn[k] = bn[k] + b0;
rot[k] = phi;
rot[k1] = phi0;
}
}
num = num / 2;
}
for (int i = 0; i < N; i++) {
int j = rot[i];
if (j > i) {
double tmp = an[i];
an[i] = an[j];
an[j] = tmp;
tmp = bn[i];
bn[i] = bn[j];
bn[j] = tmp;
}
}
if (!Inverse) {
for (int i = 0; i < N; i++) {
an[i] = an[i] / N;
bn[i] = bn[i] / N;
}
}
delete[] rot;
}
//元の関数
double f( double x) {
// return Math.sin( 2 * Math.PI / L * 2 * x );
if (L / 2 - L / 5 < x && x < L / 2 + L / 5) return 1;
else return 0;
}
/////////////////////////////////////////////////////////////////
std::string folder = "output";//作成するフォルダ名
std::string filename1 = "FFT.txt";//ファイル名
std::string filename2 = "FFT_.txt";//ファイル名
const int precision_N = 4;
int main() {
//フォルダ生成
_mkdir(folder.c_str());
std::string filepath1 = folder + "/" + filename1;
std::string filepath2 = folder + "/" + filename2;
//展開係数
double an[N], bn[N];
for (int i = 0; i<N; i++) {
double x = L / N * i;
an[i] = f(x);
bn[i] = 0;
}
//順変換の実行
FFT(an, bn, N, false);
//出力ストリームによるファイルオープン
std::ofstream fout1;
fout1.open(filepath1.c_str());
fout1 << "#x:n" << std::endl;
fout1 << "#y:展開係数" << std::endl;
fout1 << "#legend: an bn" << std::endl;
fout1 << "#showLines: true true true true" << std::endl;
fout1 << "#showMarkers: true true false false" << std::endl;
fout1 << "#fills: false false false false" << std::endl;
fout1 << "#xrange:0 128 10" << std::endl;
fout1 << "#yrange:-0.5 0.5 0.1 " << std::endl;
for (int i = 0; i < N; i++) {
fout1 << i << " " << an[i] << " " << bn[i] << std::endl;
}
fout1.close();
//逆変換用
double _an[N], _bn[N];
for (int n = 0; n < N; n++) {
_an[n] = an[n];
_bn[n] = bn[n];
}
//逆変換の実行
FFT(_an, _bn, N, true);
//出力ストリームによるファイルオープン
std::ofstream fout2;
fout2.open(filepath2.c_str());
fout2 << "#x:x" << std::endl;
fout2 << "#y:f(x)" << std::endl;
fout2 << "#legend: 元の関数 逆変換(実数部) 逆変換(虚数部)" << std::endl;
fout2 << "#showLines: true true true true" << std::endl;
fout2 << "#showMarkers: false false false false" << std::endl;
fout2 << "#fills: false false false false" << std::endl;
fout2 << "#xrange:0 3 0.2" << std::endl;
fout2 << "#yrange:-0.1 1.1 0.1 " << std::endl;
for (int i = 0; i < N; i++) {
double x = L / N * i;
fout2 << x << " " << f(x) << " " << _an[i] << " " << _bn[i] << std::endl;
}
fout2.close();
}
・http://www.natural-science.or.jp/files/physics/FFT_test.cpp
※VisualStudio2017のソルーションファイルです。GCC(MinGW)でも動作確認しています。




