作業中のメモ

よく「計算機」を使って作業をする.知らなかったことを中心にまとめるつもり.

ガウスの消去法(Fortran バージョン)

お久しぶりです.筆者です.

アクセス解析のデータをみた

久しぶりに,ブログに戻ってきた.最初に,アクセス解析のデータをみた. すると,多くの人がガウスの消去法関連でアクセスしている事が分かった.その中でも,Fortran 言語でガウスの消去法を探している人が多い事に気付いた.

ガウスの消去法を Fortran 言語に書き直す

筆者は,ほとんど C 言語を利用するが,Fortran も少しは触れた事があるので,色々調べながら,以前書いた記事「ガウスの消去法のプログラム」を Fortran77 の固定形式で書き直す事にした. 何故,Fortran77 の固定形式かというと,筆者がそれしか読んだ事がないためである.この平成の時代に,Fortran77 の固定形式を勉強するとは思わなかったが,昔の方が書いたプログラムを使う以上,仕方ない事である.

workspacememory.hatenablog.com

書き直したもの

という事で,色々調べながら,C 言語のプログラムを Fortran で書き直した.読んだ事あるのは固定形式だが,書いた事はないので,コンパイルが通る程度にしか書けない.90/95 の表現もあるかもしれない.

!----------------------------------------------------------------------!
!----------------------   Gaussian elimination   ----------------------!
!----------------------------------------------------------------------!
!23456789|123456789|123456789|123456789|123456789|123456789|123456789|12

      subroutine gaussSolver(n,matrix,b)
      implicit none

!     === arguments ===
      integer*4 :: n
      double precision :: matrix(n,n),b(n)
!     n:      rank, scalar
!     matrix: coefficient matrix, matrix(row,col)
!     b:      right side vector, b(row)

!     --- local variables ---
      integer*4 :: i,row,col,pivot
      double precision :: tmp,valmax,diag,scl,mineps
      mineps=1e-10

      do i=1,n-1
!       execute partial pivoting
        pivot=i
        valmax=abs(matrix(i,i))

!       search maximum value in a column
        do row=i+1,n
          tmp=abs(matrix(row,i))

          if (valmax.lt.tmp) then
            pivot=row
            valmax=tmp
          end if
        end do

!       replace
        if (pivot.ne.i) then
          do col=1,n
            tmp=matrix(i,col)
            matrix(i,col)=matrix(pivot,col)
            matrix(pivot,col)=tmp
          end do
          tmp=b(i)
          b(i)=b(pivot)
          b(pivot)=tmp
        end if
        diag=matrix(i,i)

        if (abs(diag).lt.mineps) then
          return
        end if

!       ===================
!       forward elimination
!       ===================
        do row=i+1,n
          scl=matrix(row,i)/diag

          do col=i,n
            matrix(row,col)=matrix(row,col)-matrix(i,col)*scl
          end do
          b(row)=b(row)-b(i)*scl
        end do
      end do

!     =====================
!     backward substitution
!     =====================
      b(n)=b(n)/matrix(n,n)
      do row=n-1,1,-1
        tmp=b(row)-sum(matrix(row,(row+1):n)*b((row+1):n))
        b(row)=tmp/matrix(row,row)
!------------------------------------------------------------
!     do row=n,1,-1
!        valsum=b(row)
!
!        do col=row+1,n
!          valsum=valsum-matrix(row,col)*b(col)
!        end do
!        b(row)=valsum/matrix(row,row)
!      end do
!------------------------------------------------------------

      return
      end

main routine

ついでに,main routine の部分も以下に示しておく.

!----------------------------------------------------------------------!
!---------------------------   main  code   ---------------------------!
!----------------------------------------------------------------------!
!23456789|123456789|123456789|123456789|123456789|123456789|123456789|12

      implicit none

      character :: fmtstr*32
      integer*4 :: n,i,j
      parameter(n=3)
      double precision :: matrix(n,n),b(n)

      matrix(1,1)=2.D0
      matrix(1,2)=1.D0
      matrix(1,3)=-2.D0
      matrix(2,1)=1.D0
      matrix(2,2)=1.D0
      matrix(2,3)=-1.D0
      matrix(3,1)=1.D0
      matrix(3,2)=-2.D0
      matrix(3,3)=3.D0
      b(1)=1.D0
      b(2)=4.D0
      b(3)=-1.D0
      write(fmtstr,'("("I0"(G15.7,1X),G15.7)")') n

      do i=1,n
        write(*,fmtstr) (matrix(i,j),j=1,n),b(i)
      end do

      ! output matrix and right side vector
      open(10,file='input.txt',status='replace')
      do i=1,n
        write(10,fmtstr) (matrix(i,j),j=1,n),b(i)
        matrix(i,:)=0.D0
        b(i)=0.D0
      end do
      close(unit=10)

      ! read data from file
      open(11,file='input.txt',status='old')
      do i=1,n
        read(11,*,end=100) (matrix(i,j),j=1,n),b(i)
      end do
100   continue
      close(unit=11)

      ! execute Gaussian elimination
      call gaussSolver(n,matrix,b)

      write(*,'(/ "answer:")')
      write(*,'(G15.7)') (b(i),i=1,n)

      stop

      end

いつも通り,英語が出来ないのはスルーして欲しい.無駄にファイル出力して,ファイル読み込みしているが,これは,単純に,Fortran で読み書きが出来るかどうかを確認しているだけである.たいした意味は無い.

Makefile

上のプログラムは自由に使ってもらってかまわないが,コンパイルに困る可能性もある.そのため,Makefile を作成した.Ubuntu 14.04 LTS でインストールできる gfortran で動作を確認している. Makefile を使った事がない人は,1 つのファイルに全て書いて,それをコンパイルしてもらえればよいと思う. また,この gfortran は,Fortran95 用のコンパイラなので,フリーダムな書き方をしていても文法のミスさえなければ通るみたいだ.以下に,Makefile を示す.

FC := gfortran
PROG := solveGauss
SRCS := gaussSolver.f prog_main.f
OBJS := $(SRCS:%.f=%.o)

all: $(PROG)

$(PROG): $(OBJS)
  $(FC) -o $@ $^

%.o: %.f
  $(FC) -c -O2 $<

.PHONY: clean
clean:
  rm -f $(OBJS) $(PROG)

sub routine 「gaussSolver」を「gaussSolver.f」として,main routine 「main code」を「prog_main.f」として保存し,make とすると,実行ファイル「solveGauss」が出来上がる.後は,実行すると解が得られる. make clean とすると,.o ファイルと solveGauss が削除される.

[hoge@local]$ ls
Makefile gaussSolver.f prog_main.f

[hoge@local]$ make
gfortran -c -O2 gaussSolver.f
gfortran -c -O2 prog_main.f
gfortran -o solveGauss gaussSolver.o prog_main.o

[hoge@local]$ make clean
rm -f gaussSolver.o prog_main.o solveGauss