Python
Fortran
情報処理

情報処理の問題をfortranとpythonで書く!#08  0~1まで0.1区分でそれぞれの個数を求める

はじめに

大学の授業でfortranを使った情報処理の授業があり、その課題をpythonとfortranの両方で書いていきたいと思います。

プログラミングについては初心者で、手探りで書いているので誤りや改善点などあると思います。もし、気づいた方はコメントで指摘していただけると助かります。

以下に環境を記しておきます。

fortran90

windows10
cygwin2.8.1

python

windows10
python3.6.2

課題内容 toi4-12

0 以上 1 未満の多数の実数を読み込み,0 以上 0.1 未満, 0.1 以上 0.2 未満, 0.2 以上 0.3 未満, …の区間に入る個数を数えるプログラムを作れ.かつ各区間に入る個数を区間とともに表として出力するプログラムとせよ.ただし,実数の数は 100 以下としてよい.

出力結果

$ ./a.exe 4_12v2.txt
from 0.00 to 0.10   0
from 0.10 to 0.20   2
from 0.20 to 0.30   1
from 0.30 to 0.40   5
from 0.40 to 0.50   0
from 0.50 to 0.60   1
from 0.60 to 0.70   0
from 0.70 to 0.80   2
from 0.80 to 0.90   0
from 0.90 to 1.00   1

課題内容の表というのがどこまでいっているのかよくわかりませんが、とりあえずこういう形で出力させました。

入力ファイル

0.1
0.3
0.5
0.2
0.32
0.33
0.76
0.98
0.12
0.32
0.33
0.71

入力ファイルは問題に沿うように適当に作りました、個数が少ないのは縦に続くため、この記事の幅をとりすぎないようにする配慮です。

toi4_12.f90
program toi4_12
implicit none
integer,parameter::UNIT_NUM=14
integer,parameter::MAX_n=100
integer,parameter::MAX_01list=10
integer::list01(10)=0
character(100) arg
integer iost,k,j,h
real m,p

    call getarg(1,arg)

    open(UNIT_NUM,file=arg,status="old",iostat=iost)
    if(iost/=0) stop "<ERROR> Input file"

    DO k=1,MAX_n
        read(UNIT_NUM,*,iostat=iost) m
        if (iost<0) exit
        if (iost>0) stop "<ERROR>Input Data"

        DO j=1,MAX_01list
            if (m*10>=10 .or. m*10<0) write(*,'(A)') "There is a number that is out of range (0~1)"
            if(m*10>=j-1 .and. m*10<j) then
                list01(j)=list01(j)+1
            end if
        end do
    end do

    close(UNIT_NUM)

    DO h=1,MAX_01list
        p=real(h)/10-0.1  !整数の割り算は整数となる
        write(*,'(A,f4.2,A,f4.2,I4)') "from ", p, " to ", p+0.1, list01(h)
    end do


stop
end program toi4_12

今回のコードはgetargで入力ファイルを引数で読み込み、データを1つずつ読み込み、DO文で繰り返し処理をしています。
 今回の工夫は、以前は入力データをそのまま配列に入れていましたが、変数mに代入するだけにとどめておくことで、配列1つ分のデータを保持せずに済むようにしたことです。
 念のため、入力データに0未満もしくは1以上のデータが含まれていた場合は、その数字が出てきたときには忠告文を表示させるようにしました。

python

pythonについては3/21更新
@shohirose さんにコメントでnumpyのhistogram関数というものがあると教えていただき、教えてもらったコードの通りに実行するととても簡単で、pythonらしい形となったため、最初に紹介したいと思います。

histogram.py
import numpy as np

data = np.loadtxt('4_12v2.txt')
bin_edges = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
hist, bin_edges= np.histogram(data, bins=bin_edges)

for i in range(0,10):
    print ("from ",i/10,"to ",(i+1)/10,":  ",hist[i])

出力結果

from  0.0 to  0.1 :   0
from  0.1 to  0.2 :   2
from  0.2 to  0.3 :   1
from  0.3 to  0.4 :   5
from  0.4 to  0.5 :   0
from  0.5 to  0.6 :   1
from  0.6 to  0.7 :   0
from  0.7 to  0.8 :   2
from  0.8 to  0.9 :   0
from  0.9 to  1.0 :   1

この、histogram関数というものとmatplotlibを組み合わせると簡単にヒストグラムを描画できるらしく、pythonのhistogramで検索するとその情報ばかり出てきましたが、表を作成するときにも使えるみたいです。また、ヒストグラムのそれぞれの区間の分割の仕方は分割数で指定するのが多いみたいですが、@shohirose さんのコードのように、区間の橋の値を配列で指定できるみたいです。あっぱれですね


次に自分のpythonのコードを書いていきます。エラー処理など全く施していませんが、とりあえず動きます。

出力結果

from 0.00 to 0.10 :  0
from 0.10 to 0.20 :  2
from 0.20 to 0.30 :  1
from 0.30 to 0.40 :  5
from 0.40 to 0.50 :  0
from 0.50 to 0.60 :  1
from 0.60 to 0.70 :  0
from 0.70 to 0.80 :  2
from 0.80 to 0.90 :  0
from 0.90 to 1.00 :  1

コード

4-12.py
f=open("4_12v2.txt","r")

numlist=f.read()
numlist1=numlist.split("\n")
numlist1.pop(-1)
numlist2=list(map(float,numlist1))
#print (numlist2)
f.close()

for i in range(0,10):
    num=0
    for k in numlist2:
        if i/10 <= k < (i+1)/10:
            num=num+1


    print("from {0:.2f} to {1:.2f} :  {2:}".format(i/10,(i+1)/10,num))

今回はそれぞれの範囲の個数を表で出力しなさいということなので個数を配列に入れ込もうと思いましたが、配列を作らずに出力させた方が早いのかなと思って上のように書きました。かなり、短いとは思いますがファイルオープンの例外処理など全く考えていないためこれでいいのかはわかりません:bow_tone2:


追記
@tenfu2tea さんにコメントでbreakとfloor関数を用いてみたらどうかというアドバイスをいただいたので、この2つを使ってpythonでコードを書いてみました。

出力結果

from 0.00 to 0.10 :  0
from 0.10 to 0.20 :  2
from 0.20 to 0.30 :  1
from 0.30 to 0.40 :  5
from 0.40 to 0.50 :  0
from 0.50 to 0.60 :  1
from 0.60 to 0.70 :  0
from 0.70 to 0.80 :  2
from 0.80 to 0.90 :  0
from 0.90 to 1.00 :  1

コード内容

4-12-ano.py
import math

f=open("4_12v2.txt","r")

numlist=f.read()
numlist1=numlist.split("\n")
numlist1.pop(-1)
numlist2=list(map(float,numlist1))
#print (numlist2)
f.close()

numlist=[0 for h in range(10)]
for k in numlist2:
    for i1 in range(0,10):
        if (math.floor(10*k)==i1):
            numlist[i1]=numlist[i1]+1
            break

for i2 in range(0,10):
    print("from {0:.2f} to {1:.2f} :  {2:}".format(i2/10,(i2+1)/10,numlist[i2]))

配列を使わない方がいいと思うのですが、思いつきませんでした。プログラミングの勉強としては新しい関数を使いこなせた気がします:older_man: