Fortran - (離散)フーリエ変換!
Updated:
Fortran 95 で(離散)フーリエ変換を実装する方法についての記録です。
0. 前提条件
- LMDE 3 (Linux Mint Debian Edition 3; 64bit) での作業を想定。
- GCC 6.3.0 (GFortran 6.3.0) でのコンパイルを想定。
1. アルゴリズムについて
当ブログ過去記事を参照のこと。
2. ソースコードの作成
File: discrete_fourier_transformation.f95
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
!****************************************************
! 離散フーリエ変換
! * f(t) = 2 * sin(4 * t) + 3 * cos(2 * t)
! ( 0 <= t < 2 * pi )
!
! date name version
! 2018.12.17 mk-mode.com 1.00 新規作成
!
! Copyright(C) 2018 mk-mode.com All Rights Reserved.
!****************************************************
!
module const
! SP: 単精度(4), DP: 倍精度(8)
integer, parameter :: SP = kind(1.0)
integer(SP), parameter :: DP = selected_real_kind(2 * precision(1.0_SP))
real(DP), parameter :: PI = 4.0_DP * atan(1.0_DP) ! 円周率
integer(SP), parameter :: NUM = 100 ! 分割数
end module const
module fourier
use const
implicit none
private
public :: gen_src, exec_dft, exec_idft
contains
! 元データ生成
!
! :param(in) integer(4) n: データ個数(行数)
! :param(inout) real(8) dat(7, n): データ配列
subroutine gen_src(n, dat)
implicit none
integer(SP), intent(in) :: n
real(DP), intent(inout) :: dat(7, 0:n-1)
integer(SP) :: i
do i = 0, n - 1
dat(1, i) = (2 * PI / n) * i
dat(2, i) = 2 * sin(4 * (2 * PI / n) * i) &
& + 3 * cos(2 * (2 * PI / n) * i)
dat(3, i) = 0.0_DP
end do
end subroutine gen_src
! 離散フーリエ変換
!
! :param(in) integer(4) n: データ個数(行数)
! :param(inout) real(8) dat(7, n): データ配列
subroutine exec_dft(n, dat)
implicit none
integer(SP), intent(in) :: n
real(DP), intent(inout) :: dat(7, 0:n-1)
integer(SP) :: i, j
real(DP) :: dft_re, dft_im ! 変換後(実部、虚部)
do i = 0, n - 1
dft_re = 0.0_DP
dft_im = 0.0_DP
do j = 0, n - 1
dft_re = dft_re &
& + dat(2, j) * ( cos((2 * PI / n) * i * j)) &
& + dat(3, j) * ( sin((2 * PI / n) * i * j))
dft_im = dft_im &
& + dat(2, j) * (-sin((2 * PI / n) * i * j)) &
& + dat(3, j) * ( cos((2 * PI / n) * i * j))
end do
dat(4, i) = dft_re
dat(5, i) = dft_im
end do
end subroutine exec_dft
! 逆離散フーリエ変換
!
! :param(in) integer(4) n: データ個数(行数)
! :param(inout) real(8) dat(7, n): データ配列
subroutine exec_idft(n, dat)
implicit none
integer(SP), intent(in) :: n
real(DP), intent(inout) :: dat(7, 0:n-1)
integer(SP) :: i, j
real(DP) :: idft_re, idft_im ! 逆変換後(実部、虚部)
do j = 0, n - 1
idft_re = 0.0_DP
idft_im = 0.0_DP
do i = 0, n - 1
idft_re = idft_re &
& + dat(4, i) * (cos((2 * PI / n) * i * j)) &
& - dat(5, i) * (sin((2 * PI / n) * i * j))
idft_im = idft_im &
& + dat(4, i) * (sin((2 * PI / n) * i * j)) &
& + dat(5, i) * (cos((2 * PI / n) * i * j))
end do
dat(6, j) = idft_re / n
dat(7, j) = idft_im / n
end do
end subroutine exec_idft
end module fourier
program discrete_fourier_transformation
use const, only : SP, DP, NUM
use fourier
implicit none
real(DP) :: dat(7, 0:NUM-1) ! 計算データ用配列
integer(SP) :: i
dat(:, :) = 0.0_DP ! 配列初期化
call gen_src(NUM, dat) ! 元データ作成
call exec_dft(NUM, dat) ! 離散フーリエ変換
call exec_idft(NUM, dat) ! 逆離散フーリエ変換
! 結果出力
! * 左から:f, 元(実), (虚), IFT(実), (虚), DIFT(実), (虚)
do i = 0, NUM - 1
print '(7(X, F11.6))', dat(:, i)
end do
end program discrete_fourier_transformation
3. ソースコードのコンパイル
$ gfortran -o discrete_fourier_transformation discrete_fourier_transformation.f95
4. 動作確認
$ ./discrete_fourier_transformation
0.000000 3.000000 0.000000 0.000000 0.000000 3.000000 -0.000000
0.062832 3.473724 0.000000 0.000000 -0.000000 3.473724 -0.000000
0.125664 3.869257 0.000000 150.000000 -0.000000 3.869257 -0.000000
0.188496 4.158424 0.000000 0.000000 -0.000000 4.158424 -0.000000
0.251327 4.317576 0.000000 0.000000 -100.000000 4.317576 -0.000000
:
===< 中略 >===
:
5.969026 0.524938 0.000000 -0.000000 -0.000000 0.524938 -0.000000
6.031858 0.940264 0.000000 0.000000 100.000000 0.940264 0.000000
6.094690 1.420235 0.000000 -0.000000 -0.000000 1.420235 0.000000
6.157522 1.942242 0.000000 150.000000 -0.000000 1.942242 0.000000
6.220353 2.478964 0.000000 0.000000 -0.000000 2.478964 -0.000000
※左から、 x 値、元の実部・虚部、変換後の実部・虚部、逆変換後の実部・虚部。
5. 結果確認
出力データを GNUPLOT で描画してみた。
変換→逆変換で元に戻っているのが分かる。
以上。
Comments