繰り返し処理のスキップ
do 文によるループ処理の中で,ある条件が成立する場合は,
それ以降の処理を行わずにスキップしたいことがあります.
Fortran 90/95 では cycle 文を使うとループをスキップすることができます.
例として,次のような覆面算を考えてみましょう.
A
+) B
――――
AC
覆面算は,以下のルールにしたがって,計算式が成り立つような数字を求めるパズルです.
- 同じ文字には同じ数字,違う文字には違う数字が入る.
- 数字は0~9までを使用し,最上桁に0は入らない.
この問題をプログラムを利用して解くには,do ループを使って A, B, C の各文字に数字を当てはめ,
数式が正しく成り立つか総当たりでチェックすればよいでしょう.
ただし,A, B, C の中に同じ文字が含まれるかチェックし,同じ文字が含まれない場合にのみ,計算式が成り立つかチェックすればよいことになります.
このような考えで作られたプログラムを以下に示します.
program work0501
implicit none
integer :: a, b, c, ac
do a = 1, 9
do b = 1, 9
if (b == a) cycle ! B == A なら以降をスキップ
do c = 0, 9
if (c == a) cycle
if (c == b) cycle
ac = a * 10 + c
if (a + b == ac) write(*,*) a, ' + ', b, ' = ', ac
end do ! c loop
end do ! b loop
end do ! a loop
stop
end program work0501
プログラムの流れ
if (b == a) cycle
などの部分で,異なる文字に同じ数字が含まれていないかチェックしています.
同じ数字が含まれている場合は cycle 文が実行されます.
cycle 文が実行されると,cycle 文が含まれるループの end do に処理が移され,次の繰り返しが行われます.
例えば,b == a
が .TRUE.
になった場合は,「b loop」というコメントのついた
end do に処理が移され,次の b の値の繰り返しがスタートすることになります.
練習問題
次の覆面算を do ループと cycle 文を利用して解け.
AB
+) A
―――――
BCC
チャレンジ問題
これまでに学習した範囲でかなり本格的な計算をすることができます.
次の問題に挑戦してみましょう!
コラッツの問題 (Collatz problem)
任意の正整数 n に対して次の操作を繰り返すと有限回で 1 に到達すると予想されている。
- n が偶数なら 2 で割る
- n が奇数なら n に 3 をかけて 1 を足す
これを「コラッツの問題」という。
例えば、n が 3 のときは7回の操作で 1 となる。
3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
キーボードから n を入力し 1 になるまでこの操作を行うプログラムを作成せよ。
20 以下の自然数で 1 になるまでの操作回数がもっとも多い数は何か?
クリックして表示
!
! コラッツの問題
!
program work0502
implicit none
integer :: n, nmax ! nmax は n の最大値を記憶
integer :: c = 0 ! n が 1 になるまでの操作の回数
write(*,*) 'Input an integer (>= 2):'
read(*,*) n
nmax = n ! nmax を初期化
do
write(*,*) n
if (n == 1) exit ! 1 になったら do ループから脱出
if (mod(n, 2) == 0) then
n = n / 2
else
n = 3 * n + 1
end if
if (n > nmax) nmax = n ! n > nmax なら nmax を更新
c = c + 1 ! 操作の回数を数える
end do
write(*,*) 'Nmax: ', nmax
write(*,*) 'Total: ', c
stop
end program work0502
素数の判定(単純版)
奇数 n が与えられたとき、それが素数かどうか、次のように判定するプログラムを作成せよ。
- k = 3, 5, 7, ..., n-2 に対して n が k で割り切れるか調べる。
- 途中で割り切れれば素数でないと判定する。
- すべての k に対して割り切れなければ素数と判定する。
クリックして表示
!
! 素数の判定(単純版)
!
program work0503
implicit none
integer :: n, k
! n を入力する
write(*,*) 'Input an odd integer:'
read(*,*) n
! k = 3, 5, 7, ..., n-2 で n が割りきれるかチェック
do k = 3, n-2, 2
! n が k で割りきれたら素数ではない。
! その場合は stop 文でプログラムを終了する。
if (mod(n, k) == 0) then
write(*,*) n, ' is not prime'
stop
end if
end do
! 割りきれなかったら素数
write(*,*) n, ' is prime'
stop
end program work0503
覆面算
次の覆面算の解を求めよ.
みず×みず=飲みみず
クリックして表示
program work0504
implicit none
integer :: mi, zu, no
integer :: mizu, nomimizu
do mi = 1, 9
do zu = 0, 9
if (zu == mi) cycle
do no = 1, 9
if (no == mi) cycle
if (no == zu) cycle
mizu = mi * 10 + zu
nomimizu = no * 1000 + mi * 100 + mi * 10 + zu
if (mizu * mizu == nomimizu) write(*,*) mizu, nomimizu
end do
end do
end do
stop
end program work0504
整数の問題
3桁の整数で、各桁の数字の3乗和がもとの数と等しくなるようなものをすべて求めよ。
クリックして表示
!
! 整数の問題
! 3桁の整数で、各桁の数字の3乗和がもとの数と等しくなるようなもの
!
program work0505
implicit none
integer :: m, n ! 3桁の整数 (234 など)
integer :: i, j, k ! 各桁の数字 (i=2, j=3, k=4 など)
do m = 100, 999
i = m / 100 ! 百の位
j = (m - i * 100) / 10 ! 十の位
k = m - (i * 100 + j * 10) ! 一の位
n = i**3 + j**3 + k**3 ! 3乗和
if (m == n) then
write(*,*) n
end if
end do
stop
end program work0505
公約数(ユークリッドの互除法)
ユークリッドの互除法を用いてキーボードから入力した2整数 a, b
の最大公約数を求めるプログラムを作成せよ。
ユークリッドの互除法のアルゴリズムは次のようなものである。
- step 1: a, b をそれぞれ変数 m, n に代入する。
- step 2: m を n で割ったあまりを k とする(組み込み関数 mod を利用)。
- step 3: k が 0 なら n が求める最大公約数である。k が 0 でなければ step 4 へ。
- step 4: n の値を m に代入し、k の値を n に代入して step 2 へ戻る。
step 4 → step 2 のループには繰り返し回数を指定しない do 文を用いる。
クリックして表示
!------------------------------------------------------------
! 最大公約数
!------------------------------------------------------------
program work0506
implicit none
integer :: a, b
integer :: m, n, k
write(*,*) 'Input two positive integer:'
read(*,*) a, b
! step 1
m = a
n = b
do
! step 2
k = mod(m, n)
! step 3
if (k == 0) then
write(*,*) 'GCM: ', n
exit
end if
! step 4
m = n
n = k
end do
stop
end program work0506