簡単な例
次のように定義される関数 esin(x) を Fortran の関数として作成し,関数の概形を Gnuplot で図示してみましょう.
esin(x) = sin(x) * exp(-x*x/50)
これを関数副プログラムにすると次のようになります.
結果を表示する write 文の中の '(2E20.12)'
という文字列は,
2つの実数型変数を「小数部分12桁・全体20桁」で表示するための「書式 (format)」です.
あとで詳しく解説しますので,ここではおまじないと思って使ってください.
! esin(x) を計算する関数 real(8) function esin(x) implicit none real(8) :: x esin = sin(x) * exp(-x*x/50.0d+0) return end function esin ! [-10, 10] の範囲で esin(x) を計算し,結果を出力するメインプログラム program main implicit none real(8) :: esin ! ユーザ定義関数には型宣言が必要 real(8) :: x integer :: i do i = -100, 100 x = i / 10.0d+0 write(*,'(2E20.12)') x, esin(x) end do stop end program main
ソースプログラム(ファイル名を esin.f90 とします)をコンパイルして実行し,データを esin.out に保存します. 以下はターミナルから入力するコマンドの例です. % はシェルのプロンプトなので入力する必要はありません.
% gfortran esin.f90 % ./a.out > esin.out
Gnuplot を使って esin.out をグラフ表示してみましょう.
gnuplot>
は Gnuplot のプロンプトです.
plot 以下のコマンドを入力しましょう.
% gnuplot gnuplot> plot 'esin.out' w l t 'esin(x)' gnuplot> quit
w は with の略,l (エル) は line の略,t は title の略です. quit コマンドで Gnuplot を終了します.
小数部分を返す関数
関数 afrac(x) を実数 x の小数部分を返す関数と定義します. 例えば,
afrac(1.2) = 0.2 afrac(2.8) = 0.8 afrac(-3.4) = -0.4
などです. afrac(x) を Fortran の関数副プログラムとして書くには, x の小数点以下を切り捨てる組み込み関数 aint(x) を使うとよいでしょう. aint(x) は次のような計算を行います.
aint(1.2) = 1.0 aint(9.8) = 9.0
以下のプログラムを完成させ,afrac(x) を [-10, 10] の範囲で図示してみましょう.
! 小数部分を返す関数 real(8) function afrac(x) implicit none real(8) :: x !***************************************************************** ! この部分を自分で作ってみましょう !***************************************************************** return end function afrac ! データ作成のためのメインプログラム program main implicit none real(8) :: afrac real(8) :: x integer :: i do i = -100, 100 x = i / 10.0d+0 write(*,'(2E20.12)') x, afrac(x) end do stop end program main
プログラムが完成したらコンパイルして実行します. 正しく動作するようなら,出力データをファイルに保存し,Gnuplot で図示します. やり方は上の例を参考にしてください.
テイラー展開を計算する関数
指数関数 exp(x) の原点を中心とする n 次までのテイラー展開は次のようになります.
exp(x) = 1 + x + x**2/2! + x**3/3! + ... + x**n/n!
これを計算する関数副プログラム et(n, x) を作ってみましょう.
ここで,x**n
は x の n 乗,n!
は n の階乗を表します.
c(k) = x**k / k!
と定義すると,
et(n, x) = c(0) + c(1) + c(2) + ... + c(n)
と書くことができます. c(k) は次の漸化式を満たすので,簡単に計算することができます.
c(0) = 1 c(k) = c(k-1) * (x / k) (k = 1, 2, 3, ...)
do ループの中で c(0), c(1), ..., c(n) を順次計算し,足し合わせていけば,et(n, x) を計算することができます. この部分を書き足し,次のプログラムを完成させてください.
! exp(n) のテイラー展開を n 次まで計算する関数 real(8) function et(n, x) implicit none integer :: n real(8) :: x ! ローカル変数 integer :: k real(8) :: cn ! et = c(0) + c(1) + ... + c(n) を計算する cn = 1.0d+0 ! c(0) = 1 et = cn ! et = c(0) do k = 1, n !***************************************************************** ! この部分を自分で作ってみましょう !***************************************************************** end do return end function et ! [-3, 3] の範囲で関数をプロットする program main implicit none real(8) :: et real(8) :: x integer :: i do i = -30, 30 x = i / 10.0d+0 write(*,'(5E20.12)') x, exp(x), et(0, x), et(5, x), et(10, x) end do stop end program main
プログラムをコンパイルして実行すると,各行に x, exp(x), et(0, x), et(5, x), et(10, x) の値が表示されます. exp(x) と et(5, x) のグラフを比較するには次のようにします.
% gfortran et.f90 % ./a.out > et.out % gnuplot gnuplot> plot 'et.out' using 1:2 w l, 'et.out' using 1:4 w l
using 1:2 は1番目のデータを x 座標の値,2番目のデータを y 座標の値としてグラフをプロットするという意味です. using 1:4 では1番目と4番目のデータを使います.
同じようにして,exp(x) と et(10, x) のグラフも比べてみましょう.
ルジャンドル多項式
n 次のルジャンドル多項式 Pn(x) を計算する Fortran の関数 lp(n, x) を作成しグラフを描いてみましょう(関数名はエルピーです). Pn(x) は [-1, 1] の実数 x に対して次の漸化式で定義されます.
P0(x) = 1 P1(x) = x Pn(x) = [ (2*n-1) * x * Pn-1(x) - (n-1) * Pn-2(x) ] / n (n = 2, 3, ...)
以下は関数副プログラム lp(n, x) の例です. 不足している部分を書き足してプログラムを完成させてください. n = 0 または 1 の場合,漸化式の計算は不要なので,lp の値をセットしたらすぐに return を実行し,副プログラムから脱出します. n >= 2 の場合は do ループを使って Pn まで漸化式を計算します.
!------------------------------------------------------------ ! n 次のルジャンドル多項式 Pn(x) !------------------------------------------------------------ real(8) function lp(n, x) implicit none integer, intent(IN) :: n real(8), intent(IN) :: x ! local integer :: k real(8) :: pa, pb ! 漸化式用の変数 ! n = 0 の場合 if (n == 0) then lp = 1 ! lp に値をセット return ! 関数から抜ける end if ! n = 1 の場合 if (n == 1) then !***************************************************************** ! この部分を自分で作ってみましょう !***************************************************************** end if ! n = 2, 3, ... の場合 pa = 1.0d+0 ! P0 pb = x ! P1 do k = 2, n !***************************************************************** ! この部分を自分で作ってみましょう !***************************************************************** end do return end function lp !------------------------------------------------------------ ! メインプログラム !------------------------------------------------------------ program main implicit none integer :: n real(8) :: x, lp ! 関数 lp() は real(8) 型 do n = -20, 20 x = n / 20.0d+0 write(*,'(5E20.12)') x, lp(1, x), lp(2, x), lp(3, x), lp(4, x) end do stop end program main
プログラムを実行すると,各行に x, P1(x), P2(x), P3(x), P4(x) の値が出力されます. 結果を lp.out に保存して Gnuplot で表示してみましょう. ターミナルから入力するコマンドは次のようになります.
% gfortran lp.f90 % ./a.tou > lp.out % gnuplot gnuplot> plot 'lp.out' using 1:2 w l, 'lp.out' using 1:3 w l, (実際は改行しない) 'lp.out' using 1:4 w l, 'lp.out' using 1:5 w l
次回までの課題
n 次のエルミート多項式 Hn(x) を計算する関数副プログラム hp(n, x) を作成せよ. 任意の実数 x に対して Hn(x) は次の漸化式で定義される.
H0(x) = 1 H1(x) = 2 * x Hn(x) = 2 * x * Hn-1(x) - 2 * (n-1) * Hn-2(x) (n = 2, 3, 4, ...)
さらに,Gnuplot を用いて [-10, 10] の範囲で関数 Hn(x) * exp(-x*x/2) (n = 0, 1, 2, 3) をプロットせよ.
完成したら高木または TA 田中さんのチェックを受けること.