6. 配列データ 目次へ 6.1. 1次元配列 6.1.1. 配列の宣言 6.2. 配列の演算 6.2.1. 部分配列と添字配列 6.2.2. 配列代入文 6.2.3. DO型定数 6.2.4. 初期値設定 6.2.5. 配列演算式 [90] 6.2.6. 配列の入出力 6.3. 配列の動的割付け [90] 6.4. 多次元配列 6.4.1. 宣言 6.4.2. 多次元配列要素の引用 6.4.3. 多次元配列要素の順序 6.4.4. 多次元配列の定数代入文 6.4.5. WHERE文 [90] 6.5. 配列関数[(付)セルオートマトン] 添え字付きのデータを扱うには配列を用いる。配列は必ず名前と大きさを宣言して おかなければならない。 6.1. 1次元配列 6.1.1. 配列の宣言 型,DIMENSION([下限:] 上限):: 配列名のならび [90] 型:: 配列名([下限:] 上限),... [90] DIMENSION([下限:] 上限) 配列名の並び 例 添え字が1から100までの値をとる整数データの配列の宣言 INTEGER, DIMENSION(1:100):: num [90] INTEGER :: abc(100) [90] INTEGER kind(1:100) DIMENSION(100) mov (mov はINTEGER宣言がされているとする) 添え字の範囲は(下限:上限)で表し,下限が1のときだけは省略して(上限)と書 くことができる。配列の大きさ(寸法)を決める上限,下限は,一般には定数(または 値の決められた定数名)で与える。配列の大きさを,プログラム実行の途中に変数で与 えることができるのは以下の場合である。 1)ALLOCATABLE属性を宣言しておき,ALLOCATE文で与える(動的割付け→6-3) 2)サブルーチンなどの副プログラムの引数となっている場合(整合配列→8章) 3)副プログラム中でのみ使われる局所的な配列の大きさを仮引数で与える場合 (自動割付配列→8章) 注)ただ一つの要素だけをもつ「寸法1の1次元配列」も,スカラ変数とは区別して 配列として扱わなければならない場合がある。(配列関数→6.5.) 例 INTEGER, DIMENSION(1):: a, b, c 例題 6_1「100個以下の正の整数データを次々に読み込んで,これを大きい 順に並べ替えて出力せよ。データ入力は0の入力で終了とせよ。」[ex6_1.f90] ! --- Sorting Array Elements --- PROGRAM Ex6_1_Sorting INTEGER :: m, mark(100), n = 0, max, maxi, i, j DO WHILE( n <= 99 ) PRINT*, "Input integer, or negative one to stop:" READ*, m ; IF( m <= 0 ) EXIT n = n + 1; mark(n) = m END DO ! --- Sorting DO i = 1, n-1 max = mark(i) maxi = i DO j = i+1, n IF( mark(j) > max ) THEN max = mark(j) maxi = j END IF END DO IF( maxi /= i ) THEN mark(maxi) = mark(i) mark(i) = max END IF END DO ! --- Output DO i = 1, n PRINT "( ' Rank', I3, ' =', I10 )", i, mark(i) END DO END PROGRAM Ex6_1_Sorting
6.2. 配列の演算 6.2.1. 部分配列と添字配列 すでに定義されている配列の一部分を配列として定義できる。[90] 配列名(範囲の下限:範囲の上限) 範囲の上限,下限を省略すれば,元の配列の上限,下限と解釈される。 例 INTEGER :: a(5) と宣言されているとき a(4:) は a(4:5) と同じ a(:3) は a(1:3) と同じ, a(:) は a(1:5) または a と同じ。 a(1:5:2) は,奇数番目の要素 a(5:1:-1) は,要素を逆に並べた配列 <添字配列> 整数配列で添字列を指定することができる: [90] 例 INTEGER :: a(4) = (/ 0, 1, 2, 3 /), b(3) = (/ 1, 4, 3 /) のとき a(b) は,データ型は a と同じで,形状は b と同じ(1次元で寸法3) の (/ 0, 3, 2 /) となる。 部分配列である必要はなく,大きくなってもよい: b(6)=(/ 2, 3, 2, 3, 2, 3 /) のとき a(b)=(/ 1, 2, 1, 2, 1, 2 /) となる。 例 CHARACTER(1) :: symbol(0:1)=(/ 'F', 'M' /) INTEGER :: bit(100) のとき symbol(bit) は,bitの要素列[例] 0001101100111... 等に応じて {F,M}の文字を同じく100個並べた文字型配列 FFFMMFMMFFMMM... 等になる。 6.2.2. 配列代入文 配列要素に一つずつ値を代入する以外にも,配列名(または部分配列名)に,まと めて同じ大きさの配列値または定数を代入することができる。[90] 例 INTEGER :: a(10), b(5), c(15) a = (/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 /) ←「配列定数」(1章) b = a(1:5) c(1:10) = a c(11:15) = a(6:10) a = 0 ← 全要素が0となる a(1:9:2)=1 ← 奇数項が1となる a(2:10:2)=0 ← 偶数項が0となる 6.2.3. DO型定数 上の例の配列 a の定義は,DO型出力(5章)と同じ形式で書くことができる。 [90] 例 a = (/ ( i, i = 0, 9 ) /) 6.2.4. 初期値設定 配列宣言文またはDATA文で初期値を設定できる。例 REAL, DIMENSION(5):: x =(/ 1.0,0.5,0.25,0.125,0.0625 /) [90] INTEGER :: a(1:10)=(/ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 /) [90] INTEGER :: a(1:10) = (/ ( n, n = 0, 18, 2) /) [90] INTEGER :: a(10) = (/ (2*n, n = 0, 9) /) [90] INTEGER :: b(-2:2)=(/ ( 128, i = 1, 5) /) [90] INTEGER b(5)/ 128, 128, 128, 128, 128/ INTEGER b(5)/5*128/ ← 5* は繰り返し数 CHARACTER*1 c(0:15)/ 8*'T', 8*'F'/ LOGICAL cond(0:7) DATA cond /4*.TRUE., 4*.FALSE. / ← 4* は繰り返し数
6.2.5. 配列演算式 配列名,部分配列名のままで,各要素の間の演算をまとめて書くことができる。 [90] 例 INTEGER, DIMENSION(100):: even, odd, wa, sa, seki, sho, beki even = (/ (2*i, i=0, 49) /); odd = (/ (2*i+1, i=0,49) /) と定義されているときwa = even + odd sa = even - odd seki = even * odd sho = even * odd beki = even **2
により,対応する各要素の間の演算結果を要素とする配列が作られる。すでに 出てきた 添字配列 も同じ性格の使い方である。これは組込み関数の引数にも 適用できる: 例 a = ABS(a) ← 各要素の絶対値を要素とする配列に置き替わる。 s = SQRT(ABS(a)) ← 各要素の絶対値の平方根を要素とする配列。 この機能は並列計算機を意識して [90] で新たに追加されたもので,並列計算機で ない場合でも形の上では並列計算が行われる。 例 INTEGER :: a(0:9)=(/ 0,1,2,3,4,5,6,7,8,9 /) のとき, a( 1:9 ) = a( 0:8 ) と, DO i = 1, 9 a(i) = a(i-1) END DO は結果が異なる。前者では |a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)| ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ |a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)| の代入が並列,すなわち同時に行われるのに対して,後者では |a(0)|a(1)|a(2)|a(3)|a(4)|a(5)|a(6)|a(7)|a(8)|a(9)| → → → → → → → → → @ A B C D E F G H と,逐次的に代入される。この結果,前者では (/ 0,0,1,2,3,4,5,6,7,8 /) と,値がシフトされるのに対して,後者では,(/ 0,0,0,0,0,0,0,0,0 /) とな る。前者と同じ結果をDOループで実現するには,別の「コピー配列」を用意し ておき INTEGER :: a(0:9)=(/ 0,1,2,3,4,5,6,7,8,9 /), b(0:9) b = a DO i = 1, 9 a(i) = b(i-1) END DO としなければならない。並列計算機でない場合には,これと同等の処理が内部 で行われていると理解すればよいであろう。 例題 6_2 「 配列xと,xの要素を逆に並べた配列の各要素間の積を,新た にxの要素とする。」[ex6_2.f90] ! --- Array Product --- REAL :: x(0:4) x = (/ ( 2.0**n, n = 0, 4) /) x = x(0:4) * x(4:0:-1) PRINT *, x END
(出力) 16.00000 16.00000 16.00000 16.00000 16.00000 =============================================================================== マスク式 a が数値型配列のとき,配列式「a>0」は「aの要素が正の位置では真, そうでない位置では偽」を要素として持つ,配列aと同じ形状の「論理データ配列」 である。このような配列のことを,その使途を考慮してマスク式と呼ぶ。⇒ WHERE文 =============================================================================== 6.2.6. 配列の入出力 各配列要素を変数の場合と同じように指定する以外に,配列全体あるいは部分配列を まとめて指定することもできる。 例 INTEGER :: a(10) のとき PRINT '(10I5)', ( a(i), i=1, 10) ← 1行に10個 PRINT '(10I5)', a ← 同上 PRINT '(5I5)', a(1:5) ← 1行に5個 PRINT '(10I5)', ( a(i), i= 10, 1, -1) ← 逆順に並ぶ これに対して DO i = 1, 10 PRINT '(10I5)', a(i) END DO では,1行に一つずつ書かれ,10行にわたることになる。 READ *, a READ*, (a(i), i=1,10) では,入力は1行に10個並べて書いてもよいし,1行に1個ずつでもよいが, DO i = 1, 10 READ*, a(i) ENDDO では,1行に一つずつしか入力できず,2つ以上入力しても無視される。 例題 6_3「次のようなパスカルの三角形(2項係数を積み重ねたもの)を描け。」 (出力) 1: 1 2: 1 1 3: 1 2 1 4: 1 3 3 1 5: 1 4 6 4 1 6: 1 5 10 10 5 1 7: 1 6 15 20 15 6 1 8: 1 7 21 35 35 21 7 1 9: 1 8 28 56 70 56 28 8 1 10: 1 9 36 84 126 126 84 36 9 1 11: 1 10 45 120 210 252 210 120 45 10 1 12: 1 11 55 165 330 462 462 330 165 55 11 1 (注 ピラミッド形に出力するには,もう少し書式に工夫がいる。)[ex6_3.f90] ! --- Pascal's Triangle --- PROGRAM Ex6_3_Pascal_Triangle INTEGER, PARAMETER :: max = 12 INTEGER :: i, c(0:max) = 0 c(1) = 1 DO i = 1, max c(1:i) = c(1:i) + c(0:i-1) ! ※ PRINT '(i2, ":", 50I5)', i, c(1:i) END DO END PROGRAM Ex6_3_Pascal_Triangle
問 ※の部分を普通のDOループで書くにはどうしたらよいか? 例題 6_4 「上限 lim( < 10000)を入力し,エラトステネスのふるい法により, lim以下の素数表を求めよ。1行に10個ずつ並べること。」 [(エラトステネスのふるい法)2の倍数に印をつける,3の倍数に印をつける,4は 既に印がついているのでスキップ,5の倍数に印をつける, ...を繰り返して残った ものが素数となる。][ex6_4.f90] ! --- Eratosthenes Sieve --- PROGRAM Ex6_4 INTEGER :: index(10000), prime(10000), lim = 0, n, m = 0 DO WHILE( lim <= 1 .OR. lim > 10000 ) ! 1 <= lim <= 10000 PRINT '(A, $)', 'Upper limit(<10000): ' ! となるまで待つ。 READ*, lim END DO index = 1 DO n = 2, INT( lim**0.5 ) ! 注1 IF( index(n) == 0 ) CYCLE ! IF(...)THEN で書いてもよい index( n**2 : lim : n ) = 0 ! n の倍数を消す:注2 END DO DO n = 2, lim IF( index(n) == 1 ) THEN ! IF(...)CYCLE で書いてもよい m = m+ 1 prime(m) = n END IF END DO PRINT '(I5, A)', m, "個見つかりました。" PRINT '(10I5)', prime(1:m) END PROGRAM Ex6_4
注1)整数mの約数は,√mまで調べればよい。それより大きい約数は,既に消されて いるはずである。 注2)整数nの倍数のうち,n**2 より小さいものは,nより小さい整数の倍数として 既に消されている。 6.3. 配列の動的割付け [90] ここまでの例では,配列の大きさは予め定まっているか,そうでない場合には十分 大きくとっておくようになっていた。後者では,実際には使われない分までメモリを 占有することになり,無駄である。逆に予定より大きくなることがあると危険を伴う。 これを回避するために,[90]では動的割付けの方法が加えられた。 動的割付け: 配列にALLOCATABLE属性を宣言しておき,あとで実行時に変数を用いて ALLOCATE文 で大きさを決める。不要になった配列は DEALLOCATE文に より割付けを解除して,メモリを節約する。 例 INTEGER, DIMENSION(:), ALLOCATABLE :: freq .......... READ*, limit ALLOCATE( freq(1:limit) ) .......... DEALLOCATE( freq ) ← 不要になれば解除しておく。 例 REAL, ALLOCATABLE :: x(:), y(:) ← 配列であることを示すため (:) がいる。 例題 6_5「Napier数(自然対数の底)eの値を,以下の公式を用いて任意桁まで 求めよ: e = 1 + 1/1! + 1/2! + 1/3! + 1/4! + ... 求めたい桁数をKとして,M > K*log(10.0) となる整数 M まで計算 すれば十分であり, 1 1 1 1 1 e = 1+ -(1 + -(1 + -(1 + .....+ --- ( 1 + - )))...))) 1 2 3 M-1 M と書くことができる。」 0.0043... 230 )1.00432900... 例えばk=100桁の計算がしたいときには,M=231となる。 920 最初は1/231,これに1を足して1.00432900.../230, ------------ と繰り返していく。わり算は筆算でやるとおりのことを 843 プログラムに書けばよい。小数点以下各位の数字を整数 690 配列で用意する。上の位のわり算で繰り越された余りを ------------ 10倍して現在の位の数に加えたものを割り,その商を 1532 その位に戻しておく.....[ex6_5.f90] ! --- Napier Number --- INTEGER :: k, max, m, n, r, j INTEGER, ALLOCATABLE :: keta(:) ! --- Input the digits --- PRINT*,"How many digits do you want to calculate?" READ*, k ALLOCATE( keta(k) ); keta = 0 ! --- Finding upper limit for M --- max = k*log(10.0) ! --- Main Loop --- DO m = max, 2, -1 r = 1 ! 1.0043... の整数部の1 DO n = 1, k j = 10*r + keta(n) keta(n) = j/m r = j - m * keta(n) ! 余りを繰り越していく END DO END DO PRINT "(//' e = 2.', 10(1X, 5I1)/(7X, 10(1X, 5I1)))", keta END
(出力) How many digits do you want to calculate? 100 e = 2. 71828 18284 59045 23536 02874 71352 66249 77572 47093 69995 95749 66967 62772 40766 30353 54759 45713 82178 52516 64274 6.4. 多次元配列 6.4.1. 宣言 添え字は複数あってもよい。各次元の上下限を並べて宣言する: 型,DIMENSION(下限:上限,下限:上限,...):: 配列名のリスト 型 :: 配列名(下限:上限,下限:上限,...) 型 配列名(下限:上限,下限:上限,...),... 例 8×8の2次元配列aを縦横2等分して4つに分割し,左上,右上,左下, 右下の順に積み重ねた4×4×4の3次元配列bを作る: b(:,:,1) = a(1:4,1:4) b(:,:,2) = a(1:4,5:8) @ | A b(:,:,3) = a(5:8,1:4) −−−|−−− b(:,:,4) = a(5:8,5:8) B | C 配列の次元数(rank)と寸法(extent 各次元の大きさ)をあわせて 形状 (shape)と いい,配列aのshapeは組込み関数 SHAPE(a) で得ることができる。 例 REAL :: a(-1:3,-4:4) のとき SHAPE(a) は,各次元の寸法を要素とする 1次元配列 (/ 5, 9 /)を与える。 6.4.2. 多次元配列要素の引用 1次元配列と同様に各次元の位置を整数変数または定数で指定して引用するが,次元 のうちの一部だけを位置指定して,より低次元の配列として引用することも可能である。 例 INTEGER :: a(1:3, 1:3), x(1:3), y(1:3), z(1:3) x = a(1,:); y=a(2,:); z=a(3,:) ←この : は 1:3 を意味する 6.4.3. 多次元配列要素の順序 例えば3×3の配列 a(1:3,1:3) の場合 |a(1,1)|a(2,1)|a(3,1)|a(1,2)|a(2,2)|a(3,2)|a(1,3)|a(2,3)|a(3,3)| である。初期値設定や一括代入,一括入出力のときには注意が必要である。 例 INTEGER A(3,3)/1,2,3,4,5,6,7,8,9/ 1 4 7 は,行列形式ではAのようになる。 A = 2 5 8 3 6 9 6.4.4. 多次元配列の定数代入文 a = (/ ( i, i=1, 9) /) の形の代入文は多次元では許されていない。1次元配列を多次元に配置しなおす組込み 関数 RESHAPE を使う(→6-5): a = RESHAPE( (/ (i, i=1,9) /), SHAPE(a) ) またはFortran95で新設されたFORALL文を使う。 しかしながら,Bのように行列を定義したいときには,明確に行ごとに B(1,1:3) = (/ 1, 2, 3 /) 1 2 3 B(2,1:3) = (/ 4, 5, 6 /) B = 4 5 6 B(3,1:3) = (/ 7, 8, 9 /) 7 8 9 と書く方が安全である。 入出力も,規定の順序に従わないときには以下のように,DOループまたはDO型リスト にする。 例題 6_6「m×n,n×k の二つの行列の積行列を求めよ。」 (この計算は組込み関数 MUTMUL で計算できる→6-5 配列関数)[ex6_6.f90] ! --- Matrix Product --- INTEGER, DIMENSION(:,:), ALLOCATABLE :: a, b, c INTEGER :: m=0, n=0, k=0, i, j, h ! DO WHILE( m <= 0 .OR. n <= 0 .OR. k <= 0 ) PRINT*, "Input Array Size m, n, k ( > 0) :" READ*, m, n, k END DO ALLOCATE( a(m,n), b(n,k), c(m,k) ) ! PRINT*, "Input Elements of A in a Matrix Form:" READ*, ( ( a(i,j), j=1, n), i=1, m ) PRINT*, "Input Elements of B in a Matrix Form:" READ*, ( ( b(i,j), j=1, k), i=1, n) ! DO i = 1, m DO j = 1, k c(i,j) = 0 DO h = 1, n c(i,j) = c(i,j) + a(i,h) * b(h,j) END DO END DO END DO DO i = 1, m PRINT "(10I5)", ( c( i, j), j = 1, k ) END DO END
注)2行目の DIMENSION(:,:) は,2次元配列を宣言していることに注意。 注)a,b,cの入出力リストはそれぞれ次のように書いてもよい: ( a(i, 1:n), i=1, m) または ( a(i,:), i=1, m) ( b(i, 1:k), i=1, n) または ( b(i,:), i=1, n) c(i, 1:k) または c(i,:) 6.4.5. WHERE文 (単純 WHERE文) ある条件が成立する配列要素だけを選んで,内容に変更を加えることができる。 WHERE(配列マスク式) 配列名 = 式 ⇒ マスク式 例 ある科目の成績の一次元整数配列aを「40点以下はそのまま,40点以上は, 40点が60点,100点が100点となる一次式で変換」して修正するゲタ博士: WHERE( a >= 40 ) a = NINT( 2.0*(a+50)/3.0 ) (注:四捨五入) (ブロックWHERE構文) ブロックIF構文と同様の構文を構成できる。 例 WHERE(配列マスク式) ← THEN は不要 配列名 = 式 ELSEWHERE(配列マスク式) ← ELSE WHERE とは書けない 配列名 = 式 ELSEWHERE 配列名 = 式 END WHERE 実行文として書くことができるのは論理式に書かれた配列(またはそれと同じ形状の 配列)に対する代入文だけであり,例えば「配列要素が0でないものだけPRINTする」 ような目的には使用できない。なお,IF文では上記の論理配列式の形は使えない。 例題 6_7「 m×n の実数行列で,要素の値が負のときは0.0に置き換えた 行列を出力する。」[ex6_7.f90] ! --- Trancation --- INTEGER :: m = 0, n = 0 REAL, ALLOCATABLE:: a( : , : ) DO WHILE( m <= 0 .OR. n <=0 ) PRINT*, "Input Array Size m, n ( > 0) :" READ*, m, n END DO ALLOCATE( a(1:m, 1:n) ) PRINT*, "Input Elements of A:" READ*, ( a(i, 1:n), i = 1, m ) ! WHERE( a < 0) a = 0.0 DO i = 1, m PRINT "(10F8.5)", a(i, 1:n) END DO END
6.5. 配列関数 [90]は並列計算機利用のプログラミングを意識して改善されたものであって,配列 要素の並列演算以外に多くの配列関数が用意されている。各機能については使用して 確かめてみよ。 (注) [ ]で囲まれた省略可能な引数がある場合,途中の引数を省略するとき には,それよりあとに出てくる引数は大文字で書かれた引数名を指定して 「引数名=引数」の形で書かなければならない(→8.5.)。例 SUM(a, MASK=m) DOT_PRODUCT(u,v) 同じ大きさの1次元配列u,v(ベクトル)のスカラー積 MATMUL(a,b) 2つの行列の積(要素の積ではなく,行列の積演算) PRODUCT(a[,DIM,MASK]) 配列aの(論理型配列MASKの要素が真の位置の)すべての要素 の積 SUM(a[,DIM,MASK] 同じく和 ALL(m[,DIM]) 論理型配列mの(DIM次元方向の)要素が全て真のとき真,それ 以外は偽となるような論理型配列(mが1次元あるいはDIM指定 がないときはスカラ値) 例 m =|T F F| のとき, ALL(m,1) は |T F F| |T T T| ALL(m,2) は |F T| ANY(m[,DIM]) 同上の要素の一つでも真のとき真,全て偽のとき偽 COUNT(m[,DIM]) 同上の要素のうち真の要素の数 MAXVAL(a[,DIM,MASK]) 配列aの(DIM次元方向に)(論理型配列MASKの要素が真の位置 の)要素の最大値を要素とする配列(aが1次元ならスカラ) MINVAL(a[,DIM,MASK]) 同じく最小値を要素とする配列 MAXLOC(a[,MASK]) 配列aの(論理型配列mの要素が真の位置の)要素の最大値の 位置を表す各次元の添字のベクトル配列が返される MINLOC(a[,MASK]) 同じく最小値の位置 ALLOCATED(a) ALLOCATABLE配列が現在,実際にALLOCATEされておれば真,さ れていなければ偽 LBOUND(a[,DIM]) 配列aの(DIM次元の)添字の下限値 UBOUND(a[,DIM]) 同じく上限値 SHAPE(a) 配列aの寸法(多次元の場合,各次元の寸法)を要素とする配列 SIZE(a[,DIM]) 配列aの全体の要素数(またはDIM次元の寸法) MERGE(t,f,m) 2つの配列t,fから,論理型配列mの要素の値T,Fに応じて要素を 選択し(.TRUE.ならtから,.FALSE.ならfから)配列を作る PACK(a,m) 論理型配列mにしたがい配列aの要素を集めて1次元配列を作る UNPACK(v,m,f) 論理型配列mにしたがい1次元配列vの要素を順番に配置し,偽 の位置には対応するfの要素を配置する。 SPREAD(a,d,n) 配列aを,d次元方向にn回コピーして,1次元高い配列を作る RESHAPE(a,s) 配列aの要素をsで定義されるSHAPEにしたがって組み替える CSHIFT(a,s[,DIM]) 1次元配列(または多次元配列のDIM次元に沿って)を循環的に (添え字をsだけ)シフトする EOSHIFT(a,s[,DIM]) はみ出した分は切り捨ててシフトする TRANSPOSE(a) 2次元行列の転置行列 注)ALLやCOUNT は引数配列が1次元のときには,引数の型に対応する型のスカラ値を 1個返すのに対し,MAXLOC,MINLOCは引数配列が1次元であっても「要素数1の 配列」を返す。混乱しているように思えるかもしれないが,後者の場合に返され るのは,添字,すなわち座標であり,1次元であってもスカラ量ではなく「1次 元ベクトル」であると理解すれば,理屈はちゃんと通っている。 関数使用例 実数配列aの負でない要素の数 : COUNT( a >= 0 ) 実数配列aの要素の平均値 : SUM(a)/SIZE(a) 整数配列kの0でない要素を出力: PRINT*, PACK( k, k/=0 ) 例題 6_8 「エラトステネスのふるい法(改良版)」[ex6_8.f90] ! --- Eratosthenes Sieve (with use of Array Functions) --- INTEGER, ALLOCATABLE :: index(:) INTEGER :: lim = 0, n DO WHILE( lim <= 1 ) PRINT*, 'Upper limit?'; READ*, lim END DO ALLOCATE( index(lim) ) index = (/ 0, (n, n= 2, lim) /) DO n = 2, INT( lim**0.5 ) IF( index(n) == 0 ) CYCLE index( n**2 : lim : n ) = 0 END DO PRINT '(I5, A)', COUNT(index/=0)," prime numbers are found." PRINT '(10I6)', PACK( index, index/=0 ) END
注)動的割付けを用いてあるので lim の値の上限は必要ないが,最後の出力の編集 記述子 I6 で5桁以下の整数を想定している。この数値6を臨機応変に変数で指定する には,文字型配列を用いて書式を構成するなどの工夫がいる: CHARACTER(LEN=7) :: fmt(1:10) fmt=(/ '(10I1 )','(10I2 )','(10I3 )','(10I4 )','(10I5)', & '(10I6 )','(10I7 )','(10I8 )','(10I9 )','(10I10)' /) 例題 6_9「20人の投票者が独立に,両隣の2人の意見(0 or 1)を見て多数に従う。 2人の意見が分かれているときには自分の意見を変えない。これを 5回繰り返す。ただし端は周期的とする。」[ex6_9.f90] ! --- Voter Rule--- INTEGER :: voter(1:20), neighbors(1:20), i = 0 PRINT*,"Input initial bits for 20 members:" READ*, voter(1:20) PRINT '(I3, ":", 20I2)', i, voter(1:20) DO i = 1, 5 neighbors = CSHIFT( voter, 1) + CSHIFT( voter, -1) WHERE( neighbors /= 1 ) voter = neighbors/2 PRINT '(I3, ":", 20I2)', i, voter(1:20) END DO END
(実行例) Input initial bits for 20 members: 0: 1 0 1 0 1 0 0 1 0 1 0 1 0 1 1 0 1 0 1 0 1: 0 1 0 1 0 0 0 0 1 0 1 0 1 1 1 1 0 1 0 1 2: 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 1 1 0 1 0 3: 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 1 4: 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 5: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 ============================================================================== (セル・オートマトン)この問題をビット列 {b(i)} から次の時刻の {b(i)}への写像 とみれば,「セルオートマトンモデル」と呼ばれているものの一種で,現在の自分と 両隣の状態(ビット値)の組み合わせから,次の自分の値が決定される。これを端か ら順番にではなく,いっせいに,すなわち並列的に行う。 この問題の場合,変換の 論理値表は以下のようになる: 現在の[左隣 自分 右隣] → 次の[自分] [ 1 1 1 ] (= 7) → 1 両隣も賛成だから安心して変えない [ 1 1 0 ] (= 6) → 1 両隣が割れているから変えず日和見 [ 1 0 1 ] (= 5) → 1 両隣が賛成だから即,賛成にまわる [ 1 0 0 ] (= 4) → 0 両隣が割れているから変えず日和見 [ 0 1 1 ] (= 3) → 1 同 上 [ 0 1 0 ] (= 2) → 0 両隣が反対だから即,反対にまわる [ 0 0 1 ] (= 1) → 0 両隣が割れているから変えず日和見 [ 0 0 0 ] (= 0) → 0 両隣も反対だから安心して変えない [b(i-1) b(i) b(i+1)] (= m) → b(i)= f(m) [b(i-1) b(i) b(i+1)] を3桁の2進数とみなせば,整数 m ={7,6,5,4,3,2,1,0} から次の自分の値 {0,1} への写像として定義することができる。 (命名法) {f(m)} を2進数の m=7,6,5,4,3,2,1,0 桁目にあてはめれば [ 1 1 1 0 1 0 0 0 ]= 128 + 64 + 32 + 8 = 10進数の232 この最後の数字を与えることで変換規則が定義できるので,「ルール232」のように 名付ける。例えば「ルール184」なら 184 = 128+32+16+8 = [1 0 1 1 1 0 0 0] だから, 上の表で「次の自分」の値は,上から 1 0 1 1 1 0 0 0 となる。これは「交通渋滞 モデル」と呼ばれている。プログラムとしては,与えられたルール番号を2進数化し, 各位の数字(ビット)を配列 map(0:7) にして写像 m→f(m) を定義しておけばよい:[ex6_9_1.f90] ! --- Voter Rule as Rule-232 CA model --- INTEGER :: voter(1:20), i = 0, rule = 232, map(0:7), n map = (/ ( IBITS(rule, n, 1), n=0, 7) /) PRINT*,"Input initial bits for 20 members:" READ '(20I1)', voter(1:20) PRINT '(I3, ":", 20I2)', i, voter(1:20) DO i = 1, 5 voter = map( 4*CSHIFT(voter,-1)+2*voter+CSHIFT(voter,1) ) PRINT '(I3, ":", 20I2)', i, voter(1:20) END DO END
=============================================================================== ⇒6章先頭へ ⇒7章へ