Javaのオブジェクト指向の基礎
前期中間前レポート課題(選択2)
4年の情報構造論で、リスト構造などの内容を進める前に、3年プログラミング応用でクラスなどに自信がない人向けの簡単レクチャ。
クラスは、データ構造と手続き
例えば、名前と年齢のデータをクラスで扱うのであれば、以下のようなコードが基本となるだろう。
import java.util.*; class NameAge { String name ; // インスタンス変数 int age ; // インスタンス変数 static int count = 0 ; // クラス変数 // コンストラクタ NameAge( String s , int a ) { this.name = s ; this.age = a ; count++ ; } // メソッド void print() { System.out.println( this.name + "," + this.age ) ; System.out.println( "member = " + count ) ; } } ; public class Main { public static void main(String[] args) throws Exception { NameAge tsaitoh = new NameAge( "tsaitoh" , 59 ) ; tsaitoh.print() ; System.out.println( "age = " + tsaitoh.age ) ; NameAge tomoko = new NameAge( "tomoko" , 48 ) ; tomoko.print() ; } } 実行結果 tsaitoh,59 member = 1 age = 59 tomoko,48 member = 2
- オブジェクト指向の基礎(Paiza.io)
クラスとは、データ構造(オブジェクト)とそのデータ構造を扱うための関数(メソッド)をまとめて扱う。
クラス NameAge の中で宣言されている、NameAge() の関数は、オブジェクトを初期化するための関数(メソッド)であり、特にコンストラクタと呼ばれる。
実際にデータを保存するための tsaitoh や tomoko とよばれる変数に NameAge オブジェクトの実体(インスタンス)を作る時には 「new クラス名」 とやることで、初期化ができる。
イメージでは、下図のようなデータ構造ができあがる。
でも、年齢の覚え方は、将来的に誕生日を覚えるように変化するかもしれない。この際に、Main 関数の中で age を使うと後で混乱の元になるかもしれない。こういう時は、NameAge クラス以外では中身を勝手に使わせないために、インスタンス変数などに public / private といったアクセス制限を加える。
import java.util.*; class NameAge { private String name ; // インスタンス変数 private int age ; // インスタンス変数 public static int count = 0 ; // クラス変数 // コンストラクタ public NameAge( String s , int a ) { this.name = s ; this.age = a ; count++ ; } // メソッド public void print() { System.out.println( this.name + "," + this.age ) ; System.out.println( "member = " + count ) ; } } ; public class Main { public static void main(String[] args) throws Exception { NameAge tsaitoh = new NameAge( "tsaitoh" , 59 ) ; tsaitoh.print() ; System.out.println( "age = " + tsaitoh.age ) ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ここがエラーになる。NameAge::age は private NameAge tomoko = new NameAge( "tomoko" , 48 ) ; tomoko.print() ; } }
クラス自体も、public class NameAge … のように宣言することもあるが、public なクラスは 1つ の *.java ファイルの中に1つしか書けないというルールがあるので要注意。
練習問題
科目(Subject)と学生(Student)の情報があり、科目を受講した成績(Result)で成績を管理している。
このデータを管理するためのクラスを宣言し、下に示すようなデータSubject,Result,Studentの配列を作り、下に示したような出力が得られるプログラムを作成せよ。
科目: Subject id name teacher // Subject[] subject_table = { 10010 情報構造論 t-saitoh // new Subject( 10010 , "情報構造論" , "t-saitoh" ) , 10020 電気磁気学 takaku // new Subject( .... 10030 電気回路 komatsu // } ; 成績: Result s_id id point // Result[] result_table = { 58563 10020 83 // new Result( 16213 , 10020 , 83 ) , 58564 10010 95 // new Result( ... 58573 10030 64 // } ; 58563 10010 89 学生: Student s_id name age // Student[] student_table = { 58563 斉藤太郎 18 // new Student( 16213 , "斉藤太郎" , 18 ) , 58564 山田次郎 19 // new Student( ... 58573 渡辺花子 18 // } ; 以下のようなデータが出力されること 斉藤太郎 電気磁気学 83 渡辺花子 情報構造論 95 山田次郎 電気回路 64 斉藤太郎 情報構造論 89
- 課題のひな形(Paiza.io)
処理速度を計測
前期中間前レポート課題(選択1)
例年であれば、プログラム作成中心のレポート課題をやってもらっているけど、前期中間試験は今回早めに行われるので、プログラム作成か、処理速度のオーダを実際に計測実験のいずれかとする。
現在時間を ミリ秒の精度で求める System.currentTimeMills() を使って、様々なプログラムの処理時間を計測してみよう。ただし、OS によって 100ミリ秒未満はあまり正確に測れない。このため、処理時間は繰り返し処理などを入れることで、10秒程度になるようにすること。また、動作例で示した Paiza.io では、長い処理時間のプログラムは、途中で強制終了させられるので、自分のパソコンにインストールしてある環境で動作させること。また、並列処理しているプログラムの影響を受ける可能性もあることから、他の処理の影響がでないように工夫すること。
様々なプログラムの実行時間を計測してみよう
import java.util.*; public class Main { // 乱数を配列にセット public static void array_set_random( int[] array ) { Random rnd = new Random() ; for( int i = 0 ; i < array.length ; i++ ) array[ i ] = rnd.nextInt( array.length ) ; } // 配列を選択法でソート public static void array_sort( int[] array ) { for( int i = 0 ; i < array.length - 1 ; i++ ) { int min = i , j ; for( j = i + 1 ; j < array.length ; j++ ) { if ( array[ min ] > array[ j ] ) min = j ; } int tmp = array[ i ] ; array[ i ] = array[ min ] ; array[ min ] = tmp ; } } public static void main(String[] args) throws Exception { // 変化させるデータ件数 int[] data_n = { 2500 , 5000 , 7500 , 10000 } ; for( int i = 0 ; i < data_n.length ; i++ ) { int[] array = new int[ data_n[ i ] ] ; // 配列を乱数で埋める時間は測りたくない array_set_random( array ) ; long start = System.currentTimeMillis() ; array_sort( array ) ; // ソート結果の表示時間は測りたくない //for( int x = 0 ; x < array.length ; x++ ) // System.out.print( array[ x ] + " " ) ; //System.out.println() ; long end = System.currentTimeMillis() ; System.out.println( "Time = " + (end - start) ) ; } } }
- データ件数と処理時間の計測(Paiza.io)
プログラムによっては、処理時間が短すぎる場合があるので、下記のように 1000 回ループさせるなどで、一定時間以上の処理となるように工夫すること。
public static int fact( int x ) { if ( x == 1 ) return 1 ; else return x * fact( x - 1 ) ; } public static void main(String[] args) throws Exception { int[] data_n = { 2 , 4 , 6 , 8 , 10 } ; for( int i = 0 ; i < data_n.length ; i++ ) { long start = System.currentTimeMillis() ; for( int j = 0 ; j < 1000 ; j++ ) { int ans = fact( data_n[ i ] ) ; } long end = System.currentTimeMillis() ; System.out.println( "Time = " + ( end - start ) ) ; } }
レポート内容
データ件数や 引数に与える数によって、処理時間が変化するプログラムを記述し、そのプログラムを N を変化させながら上記のプログラムなどを参考に処理時間を計測する。時間計測には誤差が大きく含まれる可能性があることから、複数回実行して平均をとるなどの工夫もすること。
授業中に示したプログラムなどの計測を行う場合は、ループのプログラムの変化と、別プログラムの再帰の場合の変化の2つについて結果を示すこと。創造工学演習の再帰問題の課題の整数比の直角三角形探索、辺の組み合わせ問題、N Queen、ハノイの塔など、自分で興味のあるテーマを選んだ場合は1つでも良い。
この上で、(1) プログラムリスト, (2) 時間計測にあたり工夫したこと, (3) 実際の実行結果のグラフ, (4) その結果の考察した結果をレポートにまとめて提出せよ。
コンピュータとN進数
3年の情報制御基礎の授業の一回目。この授業では、情報系以外の学生も受講することから、基礎的な共通的な話題を中心に説明を行う。
情報制御基礎のシラバス
情報制御基礎では、ここに上げたシラバス(2025)に沿って授業を行う。
基本的に、センサーから読み取ったデータを使って動く制御系システムを作る場合の基礎知識ということで、アナログ量・デジタル量の話から、移動平均やデータ差分といった数値処理や、そこで求まった値を制御に用いるための基礎的な話を行う。
コンピュータと組み込み系
最近では、コンピュータといっても様々な所で使われている。
- 科学技術計算用の大型コンピュータ(最近なら富岳や京が有名)や
- インターネットの処理を行うサーバ群
(必要に応じてサービスとして提供されるものはクラウドコンピューティングと呼ぶ)、 - デスクトップパソコン・ノートパソコン
- タブレットPCやスマートフォンのような端末、
- 電化製品の中に収まるようなワンチップコンピュータ
などがある。(3)のパソコンでもグラフィックス表示機能のために GPU(Graphics Processing Unit) を搭載したものは(ゲームでの3次元表示用)、高速計算に使われることも多い。最近では、GPU をAIの処理における神経細胞を真似するニューラルネットワークの計算に使うことも増えてきて、GPU を NPU(Neural Processing Unit)と呼ぶこともある。

(5) ワンチップコンピュータ:PIC 12F675
身近で使われている情報制御という点では、(5)のような小型のコンピュータも多く、こういったものは組み込み型コンピュータとも呼ばれる。しかし、こういったコンピュータは、小さく機能も限られているので、
- 組み込み系では、扱える数値が整数で 8bit や 16bit といった精度しかなかったり、
- 実数を伴う複雑な計算をするには、処理時間がかかったりする
ため、注意が必要である。
この情報制御基礎の授業では、組み込み系のコンピュータでも数値を正しく扱うための知識や、こういった小さいコンピュータで制御を行うことを踏まえたプログラミングの基礎となる知識を中心に説明を行う。
2進数と10進数
コンピュータの中では、電圧が高い/低いといった状態で0と1の2通りの状態を表し、その 0/1 を組み合わせて、大きな数字を表す(2進数)。
練習として、2進数を10進数で表したり、10進数を2進数に直してみよう。
N進数を10進数に変換
N進数で “abcde” があったとする。(2進数で”10101)2“とか、10進数で”12345)10“とか)
この値は、以下のような式で表せる。
(例1)
(例2)
10進数をN進数に変換
N進数のは、前式を変形すると、以下のような式で表せることから、
値をNで割った余りを求めると、N進数の最下位桁eを取り出せる。
このため、10進数で与えられた35を2進数に変換するのであれば、35を2で次々と割った余りを、下の桁から書きならべれば2進数100011)2が得られる。
実数の場合
途中に小数点を含むN進数のab.cde)Nであれば、以下の値を意味する。
ここで、小数点以下だけを取り出した、0.cde)Nを考えると、
の値に、Nをかけると、次のように変形できる。
この式を見ると、小数部にNをかけると、整数部分に小数点以下1桁目が取り出せる。
このため、10進数で与えられた、0.625を2進数に変換するのであれば、0.625に次々と2をかけて、その整数部を上の桁から書きならべれば、2進数0.101)2が得られる。
ただし、10進数で0.1という値で、上記の計算を行うと、延々と繰り返しが発生する。つまり、無限小数になるので注意せよ。
2の補数と負の数
コンピュータの中で引き算を行う場合には、2の補数がよく使われる。2の補数とは、2進数の0と1を入替えた結果(1の補数)に、1を加えた数である。
元の数に2の補数
を加えると(2進数が8bitの数であれば)、どのような数でも1,0000,0000という値になる。この先頭の9bit目が必ずはみ出し、この値を覚えないのであれば、元の数+2の補数=0とみなすことができる。このことから、2の補数= (-元の数) であり、負の数を扱うのに都合が良い。
練習問題
(1) 自分の誕生日で、整数部を誕生日の日、小数点以下を誕生日の月とした値について、2進数に変換せよ。(例えば、2月7日の場合は、”7.02″という小数点を含む10進数とする。)
変換の際には、上の説明の中にあるような計算手順を示すこと。また、その2進数を10進数に直し、元の値と同じか確認すること。(ただし、結果の2進数が無限小数になる場合最大7桁まで求めれば良い。同じ値か確認する際には無限少数の場合は近い値になるか確認すること)
(2) 自分の誕生日の日と、自分の学籍番号の下2桁の値を加えた値について、8bitの2進数で表わせ。(2月7日生まれの出席番号13番なら7+13=21)
その後、8bitの2進数として、2の補数を求めよ。また、元の数と2の補数を加えた値についても検証すること。
レポートの提出先は、こちらのリンクを参照(この中にレポート書式のひな型を置いてあります)
クイックソートと選択ソート
データ数 N = 10 件でソート処理の時間を計測したら、選択ソートで 10msec 、クイックソートで 20msec であった。
- データ件数 N= 100 件では、選択法,クイックソートは、それぞれどの程度の時間がかかるか答えよ。
- TS(10)=Ts x 100 = 10msec よって Ts = 0.1msec
- 0.1msec * 100 * 100 = 1000 msec
- TQ(10)=Tq x 10 x log10 10 = 20msec よって Tq=2msec
- 2msec * 100 * log10 100 = 400 msec
- TS(10)=Ts x 100 = 10msec よって Ts = 0.1msec
- データ件数何件以上なら、クイックソートの方が高速になるか答えよ。
-
- 2025-05-01-qsort-sel.xlsx より 30件以上
設問2 は、通常の関数電卓では求まらないので、数値的に方程式を解く機能を持った電卓が必要。
上記の計算時間は、計算しやすい値を例に示したが、一般的なプロセッサであれば、10件~30件程度で選択ソートとクイックソートの処理時間が入れ替わる。
これらを踏まえ、ライブラリとして使われるクイックソートプログラムでは、データ件数が20件ほど未満になったら、ソート方法を選択ソートに切り替えるように作られている。