Ruby - (離散)フーリエ変換!
Updated:
前回、「離散フーリエ変換」の C++ での実装に関する記事を紹介しました。
今回は、同じアルゴリズムを Ruby で実装してみました。
実際、ほとんど同じです。
0. 前提条件
- Linux Mint 14 Nadia (64bit) での作業を想定。
- Ruby 2.0.0-p195
1. (離散)フーリエ変換について
前回の記事を参照ください。
2. Ruby スクリプト作成
- 変換元の周期関数は、 \(f(t) = 2 * \sin(4t) + 3 \times \cos(2t)\) とした。
t
の範囲は \(0 \sim 2\pi\) に限定している。- 分割数は
N
の値を変更して対応する。(以下の例ではN=100
としている) - 「元データ作成」、「元データを離散フーリエ変換」、「離散フーリエ変換されたデータを逆離散フーリエ変換」としている。
File: discrete_fourier_transform.cpp
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
#! /usr/local/bin/ruby
#*********************************************
# 離散フーリエ変換
# f(t) = 2 * sin(4 * t) + 3 * cos(2 * t)
# ( 0 <= t < 2 * pi )
#*********************************************/
#
# 計算クラス
class DiscreteFourierTransform
N = 100 # 分割数
CSV_DFT = "DFT.csv" # 出力ファイル (DFT)
CSV_IDFT = "IDFT.csv" # 出力ファイル (IDFT)
def initialize
@src_re, @src_im = Array.new, Array.new
@dft_re, @dft_im = Array.new, Array.new
end
# 元データ作成
def make_source_data
N.times do |i|
@src_re << 2 * Math.sin(4 * (2 * Math::PI / N) * i) \
+ 3 * Math.cos(2 * (2 * Math::PI / N) * i)
@src_im << 0.0
end
rescue => e
raise
end
# 離散フーリエ変換
def execute_DFT
# 出力ファイルOPEN
open(CSV_DFT, "w") do |f|
# ヘッダ出力 ( k, 角周波数, 元データ(実部), 元データ(虚部), DFT(実部), DFT(虚部) )
f.puts "k,f,x_re,x_im,X_re,X_im"
# 計算・結果出力
N.times do |k|
dft_re = 0.0
dft_im = 0.0
N.times do |n|
dft_re += @src_re[n] * ( Math.cos((2 * Math::PI / N) * k * n))
+ @src_im[n] * ( Math.sin((2 * Math::PI / N) * k * n))
dft_im += @src_re[n] * (-Math.sin((2 * Math::PI / N) * k * n))
+ @src_im[n] * ( Math.cos((2 * Math::PI / N) * k * n))
end
@dft_re << dft_re
@dft_im << dft_im
f.puts "%d,%.6f,%.6f,%.6f,%.6f,%.6f" %
[k, (2 * Math::PI / N) * k, @src_re[k], @src_im[k], dft_re, dft_im]
end
end
rescue => e
raise
end
# 逆離散フーリエ変換
def execute_IDFT
# 出力ファイルOPEN
open(CSV_IDFT, "w") do |f|
# ヘッダ出力 ( k, 角周波数, DFT(実部), DFT(虚部), IDFT(実部), IDFT(虚部) )
f.puts "k,f,X_re,X_im,x_re,x_im"
# 計算・結果出力
N.times do |n|
idft_re, idft_im = 0.0, 0.0
N.times do |k|
idft_re += @dft_re[k] * (Math.cos((2 * Math::PI / N) * k * n)) \
- @dft_im[k] * (Math.sin((2 * Math::PI / N) * k * n))
idft_im += @dft_re[k] * (Math.sin((2 * Math::PI / N) * k * n)) \
+ @dft_im[k] * (Math.cos((2 * Math::PI / N) * k * n))
end
idft_re /= N
idft_im /= N
f.puts "%d,%.6f,%.6f,%.6f,%.6f,%.6f" %
[n, (2 * Math::PI / N) * n, @dft_re[n], @dft_im[n], idft_re, idft_im]
end
end
rescue => e
raise
end
end
if __FILE__ == $0
begin
obj = DiscreteFourierTransform.new # 計算クラスインスタンス化
obj.make_source_data # 元データ作成
obj.execute_DFT # 離散フーリエ変換
obj.execute_IDFT # 逆離散フーリエ変換
rescue => e
$stderr.puts "[#{e.class}] #{e.message}"
e.backtrace.each{ |tr| $stderr.puts "\t#{tr}" }
end
end
離散フーリエ変換と逆離散フーリエ変換の処理は、実質符号を変えるだけなので、1つにまとめても良いかもしれない。
3. 実行
まず、実行権限を付与。
$ chmod +x discrete_fourier_transform.rb
そして、実行。
$ ./discrete_fourier_transform
コンソールには特に何も表示しない。
アプリと同じディレクトリに “DFT.csv”, “IDFT.csv” という CSV ファイルが作成される。
内容は以下のようになっているはず。
File: DFT.csv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
k,f,x_re,x_im,X_re,X_im
0,0.000000,3.000000,0.000000,0.000000,0.000000
1,0.062832,3.473724,0.000000,0.000000,-0.000000
2,0.125664,3.869257,0.000000,150.000000,-0.000000
3,0.188496,4.158424,0.000000,0.000000,-0.000000
4,0.251327,4.317576,0.000000,0.000000,-100.000000
5,0.314159,4.329164,0.000000,-0.000000,0.000000
6,0.376991,4.182959,0.000000,0.000000,0.000000
7,0.439823,3.876846,0.000000,0.000000,0.000000
8,0.502655,3.417134,0.000000,0.000000,0.000000
9,0.565487,2.818364,0.000000,0.000000,-0.000000
:
====< 途中省略 >====
:
90,5.654867,-0.248520,0.000000,-0.000000,-0.000000
91,5.717699,-0.263689,0.000000,-0.000000,0.000000
92,5.780530,-0.202174,0.000000,-0.000000,-0.000000
93,5.843362,-0.052303,0.000000,-0.000000,0.000000
94,5.906194,0.190852,0.000000,0.000000,0.000000
95,5.969026,0.524938,0.000000,-0.000000,-0.000000
96,6.031858,0.940264,0.000000,0.000000,100.000000
97,6.094690,1.420235,0.000000,-0.000000,-0.000000
98,6.157522,1.942242,0.000000,150.000000,-0.000000
99,6.220353,2.478964,0.000000,0.000000,-0.000000
File: IDFT.csv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
k,f,X_re,X_im,x_re,x_im
0,0.000000,0.000000,0.000000,3.000000,-0.000000
1,0.062832,0.000000,-0.000000,3.473724,-0.000000
2,0.125664,150.000000,-0.000000,3.869257,-0.000000
3,0.188496,0.000000,-0.000000,4.158424,-0.000000
4,0.251327,0.000000,-100.000000,4.317576,-0.000000
5,0.314159,-0.000000,0.000000,4.329164,-0.000000
6,0.376991,0.000000,0.000000,4.182959,-0.000000
7,0.439823,0.000000,0.000000,3.876846,-0.000000
8,0.502655,0.000000,0.000000,3.417134,-0.000000
9,0.565487,0.000000,-0.000000,2.818364,-0.000000
:
====< 途中省略 >====
:
90,5.654867,-0.000000,-0.000000,-0.248520,0.000000
91,5.717699,-0.000000,0.000000,-0.263689,-0.000000
92,5.780530,-0.000000,-0.000000,-0.202174,-0.000000
93,5.843362,-0.000000,0.000000,-0.052303,0.000000
94,5.906194,0.000000,0.000000,0.190852,-0.000000
95,5.969026,-0.000000,-0.000000,0.524938,-0.000000
96,6.031858,0.000000,100.000000,0.940264,0.000000
97,6.094690,-0.000000,-0.000000,1.420235,0.000000
98,6.157522,150.000000,-0.000000,1.942242,0.000000
99,6.220353,0.000000,-0.000000,2.478964,-0.000000
離散フーリエ変換後のデータを逆離散フーリエ変換して、元のデータに戻っていることが分かる。
また、C++ 版と同じ結果になっていることも分かる。(結果が同じなのでグラフ出力は省略)
電気工学、音響学、振動、光学等でよく使用する重要な概念です。応用範囲は広いので他にも利用できるかと思います。
以上。
Comments