- Fortranで数値計算|科学技術計算の基本
- 数値計算とは
- 実数の精度を意識する
- 整数と実数の違いに注意する
- Fortranの数学関数
- 平方根を計算する
- 三角関数を計算する
- 指数関数と対数を計算する
- 配列を使った数値計算
- 測定値の合計と平均を求める
- 最大値と最小値を求める
- 標準偏差を計算する
- ベクトルの計算
- 内積を求める
- 行列を扱う
- 行列積を計算する
- 転置行列を求める
- 数値積分とは
- 台形公式で積分する
- 方程式を数値的に解く
- ニュートン法で√2を求める
- 繰り返し計算では終了条件が重要
- 簡単な物理計算|自由落下
- 時間ごとの位置を計算する
- 指数減衰を計算する
- 数値計算結果をファイルへ保存する
- 計算誤差とは
- 実数同士を==で直接比較するときの注意
- オーバーフローや不正な計算にも注意する
- 数値計算で関数を使うメリット
- 科学技術計算でよく使う組み込み関数
- ここまででできること
- さらに発展すると何ができる?
- まとめ
Fortranで数値計算|科学技術計算の基本
Fortranは、数値計算や科学技術計算を得意とするプログラミング言語です。
基本文法を覚えた後は、実際に数式や測定値をプログラムで処理してみると、
Fortranがどのような用途に向いているのか理解しやすくなります。
この記事では、Fortranを初めて科学技術計算に使う人向けに、
実数の精度、数学関数、配列を使った統計計算、行列計算、
数値積分、方程式の数値解法、簡単な物理計算までを順番に紹介します。
サンプルコード内には「!」を使ったコメント(注釈)を多めに入れています。
最初はコードをそのまま実行し、
数値を変更しながら結果がどう変わるか確認してみてください。
数値計算とは
数値計算とは、数式や物理法則などをコンピュータで数値として計算することです。
例えば、次のような処理が数値計算に含まれます。
- 平方根や三角関数を計算する
- 複数の測定値から平均値や標準偏差を求める
- 行列を計算する
- 積分を近似計算する
- 方程式の解を繰り返し計算で求める
- 物体の運動や温度変化を計算する
Fortranはもともと科学技術計算を強く意識して設計された言語であり、
このような処理を比較的自然に書くことができます。
実数の精度を意識する
Fortranで小数を扱う場合はrealを使いますが、
科学技術計算では「何桁程度の精度で計算するか」が重要になることがあります。
短い学習用プログラムでは通常のrealでも十分ですが、
より精度を確保したい場合はreal64を利用できます。
program precision_example
use iso_fortran_env, only: real64
! iso_fortran_envはFortran標準の組み込みモジュールです。
! real64を利用すると、一般に倍精度相当の実数を扱えます。
implicit none
real(real64) :: x
! real64の実数定数であることを _real64 で明示します。
x = 1.0_real64 / 3.0_real64
print *, x
end program precision_example
use iso_fortran_env, only: real64は、
Fortran標準で用意されているreal64という種類を利用するための指定です。
科学技術計算の記事では、計算誤差の影響を減らすため、
以降の例でreal64を使う場合があります。
整数と実数の違いに注意する
数値計算で特に注意したいのが整数同士の割り算です。
integer :: a, b
real :: answer
a = 1
b = 2
answer = a / b
この場合、a / bは整数同士の割り算として処理されます。
小数を含む結果が必要な場合は、実数として計算します。
real :: a, b, answer
a = 1.0
b = 2.0
answer = a / b
科学技術計算では小数を扱うことが多いため、
変数の型を確認する習慣を付けておくとよいでしょう。
Fortranの数学関数
Fortranには、科学技術計算でよく使用する数学関数が標準で用意されています。
| 関数 | 意味 |
|---|---|
| sqrt(x) | 平方根 |
| sin(x) | 正弦 |
| cos(x) | 余弦 |
| tan(x) | 正接 |
| asin(x) | 逆正弦 |
| acos(x) | 逆余弦 |
| atan(x) | 逆正接 |
| exp(x) | 指数関数 |
| log(x) | 自然対数 |
| log10(x) | 常用対数 |
| abs(x) | 絶対値 |
平方根を計算する
program sqrt_example
implicit none
real :: x
real :: answer
x = 25.0
! sqrt(x)で平方根を求めます。
answer = sqrt(x)
print *, "平方根 =", answer
end program sqrt_example
三角関数を計算する
sin、cos、tanへ渡す角度は、
通常ラジアンで指定します。
度数法の角度をそのまま入力しないよう注意してください。
例えば30度の正弦を求める場合、
30度をラジアンへ変換してからsinへ渡します。
program trig_example
implicit none
real :: degree
real :: radian
real :: answer
real, parameter :: pi = 3.14159265
degree = 30.0
! 度数法をラジアンへ変換します。
radian = degree * pi / 180.0
! sinにはラジアンを渡します。
answer = sin(radian)
print *, "sin(30 degree) =", answer
end program trig_example
指数関数と対数を計算する
program exp_log_example
implicit none
real :: x
x = 2.0
! eのx乗
print *, "exp(x) =", exp(x)
! 自然対数
print *, "log(x) =", log(x)
! 常用対数
print *, "log10(x) =", log10(x)
end program exp_log_example
配列を使った数値計算
科学技術計算では、
1個の値だけではなく、
複数の測定値や計算値をまとめて扱うことが多くあります。
Fortranでは、配列全体に対して計算したり、
配列用の組み込み関数を利用したりできます。
測定値の合計と平均を求める
program average_example
implicit none
real :: data(5)
real :: total
real :: average
! 5個の測定値を配列へ保存します。
data = [10.2, 10.5, 9.9, 10.4, 10.0]
! sum(data)で全要素の合計を求めます。
total = sum(data)
! size(data)は配列の要素数です。
! real()を使い、整数を実数へ変換して割ります。
average = total / real(size(data))
print *, "合計 =", total
print *, "平均 =", average
end program average_example
最大値と最小値を求める
program min_max_example
implicit none
real :: data(5)
data = [10.2, 10.5, 9.9, 10.4, 10.0]
! maxvalは配列の最大値を求めます。
print *, "最大値 =", maxval(data)
! minvalは配列の最小値を求めます。
print *, "最小値 =", minval(data)
end program min_max_example
標準偏差を計算する
標準偏差は、データが平均値からどの程度ばらついているかを表す指標です。
ここでは学習用として、
母標準偏差に相当する形で計算します。
program standard_deviation
implicit none
real :: data(5)
real :: average
real :: variance
real :: standard_deviation_value
data = [10.2, 10.5, 9.9, 10.4, 10.0]
! 平均値を求めます。
average = sum(data) / real(size(data))
! 配列全体について、
! 各値と平均値との差を2乗します。
! sum()で合計し、データ数で割ります。
variance = sum((data - average) ** 2) / real(size(data))
! 分散の平方根が標準偏差です。
standard_deviation_value = sqrt(variance)
print *, "平均 =", average
print *, "分散 =", variance
print *, "標準偏差 =", standard_deviation_value
end program standard_deviation
data – averageのように、
配列と1つの値を直接計算できる点もFortranの特徴です。
配列の各要素からaverageが引かれます。
ベクトルの計算
ベクトルも1次元配列として扱えます。
program vector_example
implicit none
real :: a(3)
real :: b(3)
real :: c(3)
a = [1.0, 2.0, 3.0]
b = [4.0, 5.0, 6.0]
! 同じ位置の要素同士を足します。
c = a + b
print *, "a + b =", c
end program vector_example
内積を求める
Fortranにはdot_productという組み込み関数があります。
2つのベクトルの内積を求めることができます。
program dot_product_example
implicit none
real :: a(3)
real :: b(3)
real :: result
a = [1.0, 2.0, 3.0]
b = [4.0, 5.0, 6.0]
! aとbの内積を求めます。
result = dot_product(a, b)
print *, "内積 =", result
end program dot_product_example
行列を扱う
Fortranでは2次元配列を行列として扱うことができます。
例えば2行2列の行列は次のように宣言します。
real :: a(2, 2)
Fortranの配列は列優先で格納されますが、
初学者の段階では、
まず「a(行, 列)の形で値を指定できる」と理解しておけば十分です。
行列積を計算する
Fortranにはmatmulがあり、
行列積を直接計算できます。
program matrix_example
implicit none
real :: a(2, 2)
real :: b(2, 2)
real :: c(2, 2)
! reshapeを使って2×2配列を作ります。
! Fortranでは配列要素が列方向を先に埋める点に注意します。
a = reshape([1.0, 3.0, &
2.0, 4.0], shape(a))
b = reshape([5.0, 7.0, &
6.0, 8.0], shape(b))
! 行列積 A×B を計算します。
c = matmul(a, b)
print *, "C ="
print *, c
end program matrix_example
行列の表示はそのままだと読みづらいことがあります。
本格的な行列計算では、
DO文を使って行ごとに整形して表示する方法もあります。
転置行列を求める
transposeを使うと、
行と列を入れ替えた転置行列を求められます。
real :: a(2, 3)
real :: b(3, 2)
b = transpose(a)
数値積分とは
積分を解析的に解けない場合や、
測定データから面積を求めたい場合には、
数値的に近似して積分値を求めることがあります。
最も基本的な方法の一つが台形公式です。
区間を小さく分割し、
各区間を台形として面積を足し合わせます。
台形公式で積分する
ここでは、0から1までのf(x)=x²を数値積分します。
正確な積分値は1/3ですが、
数値計算でどの程度近づくか確認できます。
program trapezoidal_rule
use iso_fortran_env, only: real64
implicit none
integer :: i
integer, parameter :: n = 1000
real(real64) :: a
real(real64) :: b
real(real64) :: h
real(real64) :: x
real(real64) :: integral
a = 0.0_real64
b = 1.0_real64
! 区間幅 h を求めます。
h = (b - a) / real(n, real64)
! 最初と最後の点は1/2の重みで加えます。
integral = 0.5_real64 * (f(a) + f(b))
! 内側の点を順番に加えます。
do i = 1, n - 1
x = a + real(i, real64) * h
integral = integral + f(x)
end do
! 最後に区間幅を掛けます。
integral = integral * h
print *, "数値積分 =", integral
print *, "1/3 =", 1.0_real64 / 3.0_real64
contains
function f(x) result(y)
use iso_fortran_env, only: real64
implicit none
real(real64), intent(in) :: x
real(real64) :: y
! 積分する関数 f(x)=x^2
y = x ** 2
end function f
end program trapezoidal_rule
nを大きくすると区間を細かく分割するため、
一般には近似精度が高くなります。
ただし、計算量や丸め誤差もあるため、
単純に無限に大きくすればよいというわけではありません。
方程式を数値的に解く
方程式の解を公式で求められない場合、
繰り返し計算によって近似解を求める方法があります。
代表的な方法の一つがニュートン法です。
ここでは、
x² – 2 = 0の正の解、
つまり√2を求めます。
ニュートン法で√2を求める
program newton_method
use iso_fortran_env, only: real64
implicit none
integer :: i
integer, parameter :: max_iteration = 20
real(real64) :: x
real(real64) :: next_x
real(real64), parameter :: tolerance = 1.0e-12_real64
! 最初の予想値です。
x = 1.0_real64
do i = 1, max_iteration
! ニュートン法
! x_new = x - f(x) / f'(x)
! f(x)=x^2-2、f'(x)=2xです。
next_x = x - (x ** 2 - 2.0_real64) / &
(2.0_real64 * x)
! 前回との差が十分小さくなったら終了します。
if (abs(next_x - x) < tolerance) then
x = next_x
exit
end if
x = next_x
end do
print *, "sqrt(2)の近似値 =", x
print *, "sqrt(2) =", sqrt(2.0_real64)
end program newton_method
toleranceは、
どの程度値が変化しなくなったら
「解に十分近づいた」と判断するかを決める値です。
繰り返し計算では終了条件が重要
数値計算では、答えに近づくまで繰り返す処理がよく使われます。
その場合、終了条件を必ず考える必要があります。
例えば次の2つを用意すると安全です。
- 誤差が一定値より小さくなったら終了する
- 最大繰り返し回数に達したら終了する
最大回数を設定しないと、
条件によってはプログラムがいつまでも終了しない可能性があります。
簡単な物理計算|自由落下
Fortranは物理計算とも相性のよい言語です。
ここでは空気抵抗を無視し、
静止状態から物体を落としたときの落下距離を計算します。
落下距離は、
時間t、重力加速度gを使って
1/2 × g × t²で求められます。
program free_fall
implicit none
real :: time
real :: distance
real, parameter :: gravity = 9.80665
print *, "落下時間[s]を入力してください。"
read *, time
! 静止状態からの自由落下距離を計算します。
distance = 0.5 * gravity * time ** 2
print *, "落下距離[m] =", distance
end program free_fall
このプログラムは非常に単純ですが、
時間を少しずつ変化させて計算すれば、
物体の運動を時系列で調べることもできます。
時間ごとの位置を計算する
program free_fall_table
implicit none
integer :: i
real :: time
real :: distance
real, parameter :: gravity = 9.80665
real, parameter :: dt = 0.5
! 0秒から5秒まで0.5秒間隔で計算します。
do i = 0, 10
time = real(i) * dt
distance = 0.5 * gravity * time ** 2
print *, time, distance
end do
end program free_fall_table
このように、
一定時間ごとに物理量を計算して出力する方法は、
さまざまなシミュレーションの基本になります。
指数減衰を計算する
放射性物質の減衰や一部の物理・化学現象では、
指数関数的な変化が現れます。
program exponential_decay
implicit none
integer :: i
real :: time
real :: amount
real, parameter :: initial_amount = 100.0
real, parameter :: decay_constant = 0.2
real, parameter :: dt = 1.0
do i = 0, 10
time = real(i) * dt
! N = N0 * exp(-λt)
amount = initial_amount * &
exp(-decay_constant * time)
print *, time, amount
end do
end program exponential_decay
数値計算結果をファイルへ保存する
科学技術計算では、
大量の結果を画面へ表示するより、
ファイルへ保存した方が扱いやすい場合があります。
次の例では、
自由落下の時間と距離をfree_fall.txtへ保存します。
program save_free_fall
implicit none
integer :: i
integer :: unit_number
real :: time
real :: distance
real, parameter :: gravity = 9.80665
real, parameter :: dt = 0.1
! 出力ファイルを作成します。
open(newunit=unit_number, file="free_fall.txt", &
status="replace", action="write")
! 見出しを書き込みます。
write(unit_number, *) "time distance"
do i = 0, 100
time = real(i) * dt
distance = 0.5 * gravity * time ** 2
! 時間と距離を1行ずつ保存します。
write(unit_number, *) time, distance
end do
close(unit_number)
print *, "free_fall.txtへ保存しました。"
end program save_free_fall
計算誤差とは
コンピュータは、すべての実数を完全に正確な値として保存できるわけではありません。
そのため、計算結果には小さな誤差が含まれることがあります。
例えば、0.1のような十進小数でも、
コンピュータ内部の2進数では有限桁で正確に表せない場合があります。
科学技術計算では、
次のような誤差を意識する必要があります。
- 浮動小数点による丸め誤差
- 数値積分などの近似誤差
- 繰り返し計算による誤差の蓄積
- 入力データそのものが持つ測定誤差
初学者の段階では、
「コンピュータの計算結果は必ず数学的に完全な値になるわけではない」
ということを覚えておけばよいでしょう。
実数同士を==で直接比較するときの注意
浮動小数点数には丸め誤差があるため、
計算結果が理論上同じ値でも、
内部ではわずかに異なる場合があります。
そのため、次のように完全一致だけで判定するよりも、
差の絶対値が十分小さいかを確認する方法が使われます。
real :: a
real :: b
real :: tolerance
a = 0.1 + 0.2
b = 0.3
tolerance = 1.0e-6
! aとbの差が十分小さいかを確認します。
if (abs(a - b) < tolerance) then
print *, "ほぼ等しい"
end if
オーバーフローや不正な計算にも注意する
非常に大きな値を計算したり、
数学的に定義できない値を計算したりすると、
正常な数値が得られない場合があります。
例えば、次のようなケースです。
- 0で割る
- 実数として負の値の平方根を求める
- 非常に大きな指数関数を計算する
- log(0)や負の実数の対数を求める
入力値を確認するIF文を入れるなど、
計算前に条件を確認すると安全です。
数値計算で関数を使うメリット
数値積分や方程式の解法では、
計算対象となる数式を関数として分けておくと便利です。
function f(x) result(y)
implicit none
real, intent(in) :: x
real :: y
y = x ** 2 + 2.0 * x + 1.0
end function f
後から計算する式を変更したい場合も、
関数の中身を変更するだけで済みます。
Fortranの関数やサブルーチンは、
単にコードを整理するためだけでなく、
数値計算プログラムを再利用しやすくするためにも重要です。
科学技術計算でよく使う組み込み関数
| 関数 | 用途 |
|---|---|
| sqrt | 平方根 |
| sin / cos / tan | 三角関数 |
| exp | 指数関数 |
| log / log10 | 対数 |
| abs | 絶対値 |
| sum | 配列の合計 |
| maxval | 配列の最大値 |
| minval | 配列の最小値 |
| size | 配列の要素数 |
| dot_product | ベクトルの内積 |
| matmul | 行列積 |
| transpose | 転置行列 |
ここまででできること
この記事の内容を理解すると、
Fortranだけでも次のような基礎的な科学技術計算ができます。
- 平方根・三角関数・指数・対数の計算
- 測定値の平均・最大・最小・標準偏差の計算
- ベクトルの加算と内積
- 行列積や転置行列の計算
- 台形公式による数値積分
- ニュートン法による方程式の近似解
- 自由落下などの簡単な物理計算
- 指数減衰の時系列計算
- 計算結果のファイル保存
さらに発展すると何ができる?
Fortranの数値計算をさらに発展させると、
次のようなテーマへ進むことができます。
- 連立一次方程式
- 固有値・固有ベクトル
- 微分方程式
- Runge-Kutta法
- モンテカルロ法
- 偏微分方程式
- 熱伝導計算
- 振動・波動シミュレーション
- 流体力学
- 有限要素法
より高度な行列計算では、
BLASやLAPACKなどの外部ライブラリを利用することもあります。
外部ライブラリについては、別の記事で導入方法と使い方を扱うことができます。
まとめ
Fortranは、
単純な四則演算だけでなく、
配列、数学関数、行列演算など、
科学技術計算に役立つ機能を標準で備えています。
特に、
配列全体をまとめて計算できることや、
dot_product、matmul、sumなどの組み込み関数を利用できることは、
数値計算を行う上で便利です。
また、台形公式やニュートン法のような数値計算法も、
DO文、IF文、関数などの基本文法を組み合わせることで実装できます。
科学技術計算では、
計算式そのものだけでなく、
実数の精度、近似誤差、終了条件、異常値などにも注意する必要があります。
基本的な数値計算に慣れたら、
次はFortran 77・Fortran 90/95・Modern Fortranのコードを比較し、
昔のFortranと現在のFortranで何が変わったのか確認してみると、
Fortranという言語の発展も理解しやすくなります。
