Fortran - 線形計画法(シンプレックス法)!
Updated:
Fortran 95 で線形計画法を「シンプレックス法」で解くアルゴリズムを実装してみました。
0. 前提条件
- LMDE 3 (Linux Mint Debian Edition 3; 64bit) での作業を想定。
- GCC 6.3.0 (GFortran 6.3.0) でのコンパイルを想定。
1. アルゴリズムについて
当ブログ過去記事を参照のこと。
2. ソースコードの作成
File: simplex.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
!****************************************************
! 線形計画法(シンプレックス法)
!
! * 入力はテキストファイルをパイプ処理
! 1行目: 行数 列数 変数の数
! 2行目以降: 1行に列数分の係数 * 行数
!
! date name version
! 2018.12.05 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))
end module const
module simplex
use const
implicit none
private
public :: solve
contains
! 線形計画法
!
! :param(in) integer(4) n_row: 元数
! :param(in) integer(4) n_col: 元数
! :param(inout) real(8) a(n_row,n_col): 係数配列
subroutine solve(n_row, n_col, a)
implicit none
integer(SP), intent(in) :: n_row, n_col
real(DP), intent(inout) :: a(n_row, n_col)
integer(SP) :: x, y ! 最小値の行・列インデックス
real(DP) :: c_min ! 最小値
real(DP) :: a_tmp(n_row - 1) ! 作業用配列
integer(SP) :: i, j
real(DP) :: d
do
! 列選択
c_min = minval(a(n_row, :))
y = minloc(a(n_row, :), 1)
if (c_min >= 0.0_DP) exit
! 行選択
a_tmp = a(1:n_row-1,n_col) / a(1:n_row-1, y)
x = minloc(a_tmp, 1)
! ピボット係数を p で除算
a(x, :) = a(x, :) / a(x, y)
! ピボット列の掃き出し
do i = 1, n_row
if (i == x) cycle
d = a(i, y)
a(i, :) = a(i, :) - d * a(x, :)
end do
end do
end subroutine solve
end module simplex
program linear_programming
use const
use simplex
implicit none
integer(SP) :: n_row, n_col, n_var ! 行数、列数、変数の数
real(DP), allocatable :: a(:, :) ! 係数行列
character(20) :: f ! 書式文字列
integer(SP) :: i, j, flag
real(DP) :: v
! 行数、列数、変数の数の取得
read (*, *) n_row, n_col, n_var
print '(A)', "Number:"
print '(" rows = ", I0)', n_row
print '(" colmuns = ", I0)', n_col
print '(" variables = ", I0)', n_var
! 係数行列メモリ確保
allocate(a(n_row, n_col))
! 係数行列読み込み
do i = 1, n_row
read (*, *) a(i, :)
end do
print '(A)', "Coefficients:"
write (f, '("(", I0, "(F8.4, 2X)", ")")') n_col
print f, a(1:n_row, :)
! 線形計画法で解く
call solve(n_row, n_col, a)
! 結果出力
print '(A)', "Answer:"
do j = 1, n_var
flag = -1
do i = 1, n_row
if (a(i, j) == 1.0_DP) flag = i
end do
if (flag == -1) then
v = 0.0_DP
else
v = a(flag, n_col)
end if
print '(" x", I0, " = ", F8.4)', j, v
end do
print '(" z = ", F8.4)', a(n_row, n_col)
! 係数行列メモリ解放
deallocate(a)
end program linear_programming
3. ソースコードのコンパイル
$ gfortran -o simplex simplex.f95
4. 動作確認
今回のプログラムはデータファイルを読み込んで実行する形式としているので、あらかじめデータファイルを作成しておく。(実行後に手入力してもよいが)
※1行目:行数、列数、変数の数、2行目以降:係数・定数(「C++ - 線形計画法(シンプレックス法)!」参照)
File: data.txt
1
2
3
4
5
4 6 2
1.0 2.0 1.0 0.0 0.0 14.0
1.0 1.0 0.0 1.0 0.0 8.0
3.0 1.0 0.0 0.0 1.0 18.0
-2.0 -3.0 0.0 0.0 0.0 0.0
そして、実行。
$ cat data.txt | ./simplex
Number:
rows = 4
colmuns = 6
variables = 2
Coefficients:
1.0000 2.0000 1.0000 0.0000 0.0000 14.0000
1.0000 1.0000 0.0000 1.0000 0.0000 8.0000
3.0000 1.0000 0.0000 0.0000 1.0000 18.0000
-2.0000 -3.0000 0.0000 0.0000 0.0000 0.0000
Answer:
x1 = 2.0000
x2 = 6.0000
z = 22.0000
以下のように実行しても同じ。
$ ./simplex < data.txt
以上。
Comments