【レポート】配列や多次元配列の理解

【レポート】配列や多次元配列を理解して使えるように

目的

配列や多次元配列を理解して使えるようにすること。

内容

①教科書p195 演習6-7

②3×3の行列を入力させ、逆行列を表示するプログラム

なお、他人とは違う何かをプログラムの中に機能として組込む。

ソースコード

以下に内容の①、②のプログラムのソースコードを示す。

①のソースコード

package lesson4;

import java.util.Random;

import java.util.Scanner;

public class Kadai4の1 {

public static void main(String[] args) {

Random rand = new Random();

Scanner stdIn = new Scanner(System.in);

final int n = 12;       // 要素数・ n変えられない!

int[] a = new int[n];           // 配列を宣言

 for (int j = 0; j < n; j++)//jはこのfor文だけで使われる!

a[j] = rand.nextInt(10);//0~9の数字を12個、配列に確保!

//配列aの全要素の値を表示

System.out.print(“配列aの全要素の値\n{“);

for (int j = 0; j < n; j++){//jはこのfor文だけで使われる!

System.out.print(a[j] + ” “);

}     

System.out.println(” }”);

//keyに入力した値が配列aの要素の値の中にあるかを確かめあった場合は配列の何番目かをだすプログラム。重複があった場合は最も末尾側に位置する場所。

System.out.print(“探す数値”);

int  key= stdIn.nextInt();

                      //線形

int i;for(i=n-1;i>=0;i–){   

                        if(a[i]==key){

                                break;

                        }

}

if(i<n){

                        System.out.println(“それはa[“+i+”]にあります。”); 

}

else{

                    System.out.println(“それはありません。”);

}

//配列aを配列bにコピーするプログラム

int[] b=new int[n];//配列bの宣言

for(int j=0;j<n;j++){  //配列aの全要素を配列bにコピー

                                        b[j]=a[j];

}

System.out.println(“aの全要素をbにコピーしました!”);

for(int j=0;j<n;j++){

                                  System.out.println(“b[“+j+”]=”+b[j]);                

            }

//配列aの中の一番大きい要素を求めるプログラム

for(int j=0;j<n/2;j++){

                        int tmp=a[j];

                        a[j]=a[n-j-1];

                        a[n-j-1]=tmp;

}

System.out.println(“配列aの中で一番大きい要素は”+max+”です”);

//配列aを逆順に並び変えるプログラム

for(int j=0;j<n/2;j++){

                        int tmp=a[j];

                        a[j]=a[n-j-1];

                        a[n-j-1]=tmp;

}

System.out.println(“要素の並びを逆転しました!”);

for(int j=0;j<n;j++){

                        System.out.println(“a[“+j+”]=”+a[j]);

                }

        }

}

②のソースコード

package lesson4;

import java.util.Scanner;

public class kadai4の2 {

        public static void main(String[] args){

                Scanner stdIn=new Scanner(System.in);

                double[][]a=new double[3][3];//配列の宣言と生成

                double[][]b=new double[3][3];//配列の宣言と生成

                //int i,j,k;//iとjはa行列とbの行列の行と列、iとkはc行列の行と列

                double det;

         for(int i=0;i<3;i++){ 

                for(int j=0;j<3;j++){ 

                                System.out.println(“x[“+i+”][“+j+”]=”);

                                a[i][j]=stdIn.nextInt();

            }

         }

         //サラスの公式

         det=a[0][0]*a[1][1]*a[2][2];

         det+=a[1][0]*a[2][1]*a[0][2];

         det+=a[2][0]*a[0][1]*a[1][2];

         det-=a[2][0]*a[1][1]*a[0][2];

         det-=a[1][0]*a[0][1]*a[2][2];

         det-=a[0][0]*a[2][1]*a[1][2];

                //逆行列もつかもたないか

         if(det==0){

                        System.out.println(“逆行列もたない”);

         }

         else

                        System.out.println(“逆行列もつ”);

                        //随伴行列

            for(int i=0;i<3;i++){

                 for(int j=0;j<3;j++){

                                if(i==0){

                                        if(j==0){

                                                b[i][j]=(a[1][1]*a[2][2]-a[1][2]*a[2][1]);

                                        }

                                        if(j==1){

                                                b[i][j]=(a[0][2]*a[2][1]-a[0][1]*a[2][2]);

                                        }

                                        if(j==2){

                                                b[i][j]=(a[0][1]*a[1][2]-a[0][2]*a[1][1]);

                                        }

                                }//if(i==0)

                                if(i==1){

                                        if(j==0){

                                           b[i][j]=(a[1][2]*a[2][0]-a[1][0]*a[2][2]);

                                        }

                                        if(j==1){

                                           b[i][j]=(a[0][0]*a[2][2]-a[0][2]*a[2][0]);

                                        }

                                        if(j==2){

                                                b[i][j]=(a[0][2]*a[1][0]-a[0][0]*a[1][2]);

                                        }

                                }//if(i==1)

                                if(i==2){

                                        if(j==0){

                                                b[i][j]=(a[1][0]*a[2][1]-a[1][1]*a[2][0]);

                                        }

                                        if(j==1){

                                                b[i][j]=(a[0][1]*a[2][0]-a[0][0]*a[2][1]);

                                        }

                                        if(j==2){

                                                b[i][j]=(a[0][0]*a[1][1]-a[0][1]*a[1][0]);

                                        }

                                }//if(i==2)

                                }//for(y=0;y<3;y++)

                        }//for(x=0;x<3;x++)

                System.out.println(“逆行列は”);

                for(int i=0;i<3;i++){

                        for(int j=0;j<3;j++){

                      System.out.printf(“%5.1f”,b[i][j]/det);

                      System.out.print(” “);//空白

                        }//for(y=0;y<b[x].length;y++)

            System.out.println();//改行

            }

         }//else

         //単位行列をだして逆行列が正しいこと確認するプログラム 

     double[][]c=new double[3][3];//配列の宣言と生成

         System.out.println(“行列*逆行列=単位行列となることを確認する”);

         for(int i=0;i<3;i++){

           for(int k=0;k<3;k++){

                 for(int j=0;j<3;j++){

                         c[i][k]+=a[i][j]*b[j][k]/det;

                 }

                 System.out.printf(“%5.1f”,c[i][k]);

                 System.out.print(” “);//空白

           }

           System.out.println();//改行

         }

        }      

}

結果

3のソースコード①、②の実行結果の画面キャプチャを以下の図1,2、に示す。

図1 3の①のソースコードの結果
図2 3の②のソースコードの結果

考察

 内容①のプログラムにおいて、まず配列の要素数を確保するためにint n=12と書き、さらに以下プログラムを書いていく中で配列の要素数nを間違って書き変えても大丈夫なようにintの前にfinalをつけておいた。次のint[] a=new int a[n]のint[] aは配列変数の宣言であり、この場合int型を構成要素型とする配列の宣言であり、この宣言によって作られるaは、配列変数と呼ばれる特殊な変数であり、配列の本体では無い。new int a[n]が配列の本体であり、この場合int型を構成要素型とする構成要素がn個の配列本体を生成した。また、構成要素がn個の配列の構成要素はa[0]a[1]・・・・a[n-1]であり、a[n]という構成要素は存在し無い。また、newによる配列本体の生成時に構成要素数として負の値を指定したり、存在しない要素にアクセスしたりするとプログラムの実行時にエラーなどが発生する。

 配列の生成をしたためfor文によってjを0からn-1まで、つまりこの場合、0~11までインクリメントしながら、配列の要素a[j]に乱数を利用して0~9の値をランダムに読み込んだ。読み込みが終わったらfor文を抜けて、次のfor文に行き、配列aの全要素の値を表示した。表示し終わってfor文を抜けた後は、keyという変数に数を入力して、for文とその中にあるif文を使ってkeyに入力した数が、配列aの要素の値の中にあるかを確かめ、あった場合はそれは何番目のなのか、またない場合「ありませんでした」と出力し、さらにもし配列aの中に同じ数があった場合は、最も末尾側に位置する数の番地をだせるようにfor文をfor(i=n-1;i>=0;i–)と書いた。このプログラムは線形アルゴリズムとは逆のプログラムである。

 for(i=n-1;i>=0;i–)のfor文での繰り返しを制御するための変数はiだが、それ以外のfor文(乱数の生成によって要素に値を代入for文と、全要素の値のを表示したりするfor文など)ではjとなっているが、もし変数名jをiに変更すると、コンパイルエラーとなってしまった。おそらく、原因はfor文で宣言する変数名と同じ名前の変数を、同じメソッド中のfor文以外の箇所で宣言することはできないだからだと推察している。

 また、以下に配列aを配列bにコピーするプログラム、配列aの中で一番大きい要素を求めるプログラム、配列aを逆順に並び変えるプログラムを書いた。まず、配列aを配列bにコピーするプログラムにおいて、要素数nの配列bを生成した。この時、要素数がコピーする配列aと違った要素数ではコピーできなかった。あとは繰返し文のfor文によって全要素をコピーして終わりだ。ただし、代入演算子=による配列変数の代入は、参照先のコピーであって要素のコピーではない。次に配列aの中で一番大きい要素を求めるプログラムにおいて、まず配列aの先頭要素a[0]の値をmaxに代入する作業を、配列の要素数とは無関係に行う。その後、for文で走査をしていきます。走査の過程では、if文によって、着目した要素の値が、それまでの最大値maxより大きいときにa[i]の値をmaxに代入する。走査が終了したときには、配列aの最大要素の値がmaxに入っていることになる。最後に配列aを逆順に並び変えるプログラムにおいて、まず配列aの先頭要素a[0]と末尾要素a[11]の値を交換する。次にa[1]とa[10]の値を交換していく。交換回数はn/2、つまり交換回数/2なる。今回は交換回数が偶数だったが、奇数のときも同じ。余った1が要素の中央なり、そのままで大丈夫だからだ。このことをfor文に書いていけば配列が逆順になる。

 また、このプログラム全体においてfor文の制御式の部分は「配列変数名.length」という配列の要素数を取得するための式で書くことも可能だ。

 内容②のプログラムにおいて、まず行列を作りそこから課題である逆行列をだす為、プログラムの最初にdouble[][]a=new double[3][3]、double[][]b=new double[3][3]の2次元配列の宣言し、次の2つのfor文と2次元配列aと値の読込みにより行列を作った。2次元配列の最初の[3]は行を2つ目の[3]は列を指している。つまり、1つめのfor文は行をさし、その中にある2つめのfor文は列を指している。次に、できた2次元配列aは逆行列をもつかどうかを調べるために、サラスの公式 

detA=a11a22a33+a21a32a13+a31a12a23-a11a32a23-a31a22a13-a21a12a33
を使い、もし≠0のときAの逆行列が存在して=0の時は逆行列を持たないというプログラムをfor文の前までに書いた。もし、逆行列を持たなかったらプログラムは終了である。逆行列をもつ場合は次に進み、次に随伴行列を出さなくてはならない。随伴行列の公式は

説明: http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/image/eq23-04.gif

の行列の部分であり、この計算をしてその結果をプログラムの始めに宣言した2次元配列bに代入した。1回1回上記の公式をそのまま書いたプログラムにせず、for文やif文などを使い簡単にかつスマートになるプログラムに使用した結果、できず、普通に公式で書いただけのプログラムより処理が長いプログラムになってしまった。出た随伴行列bをサラスの公式で出たdetで割ったのが逆行列である。

 このあとは行列×逆行列が単位行列になったら、出した逆行列は正しいというプログラムを書いた。単位行列でなるであろう2次元配列をcとおいた。2次元配列a×2次元配列b/detの計算つまり、行列×逆行列の計算を少し書きだしてみると

C[0][0]=a[0][0]+b[0][0]/det

                     +

C[0][0]=a[0][1]+b[1][0]/det

                   +

C[0][0]=a[0][2]+b[2][0]/det

C[0][1]=a[0][0]+b[1][1]/det

                   +

C[0][1]=a[0][1]+b[1][1]/det

                     +

C[0][1]=a[0][2]+b[2][1]/det

C[0][2]=a[0][0]+b[0][2]/det

                     +

C[0][2]=a[0][1]+b[1][2]/det

                     +

C[0][2]=a[0][2]+b[2][2]/det

とこの後もこのような計算していく。ここで上記の計算の式を見ていくと、cの行とaの行、cの列とbの列、aの列とbの行の数の変化が同じだと分かり、for文を回す時の変数が一緒になる。for文は数の変化は小さい順に外になる。つまりこの場合cの行aの行の変数iを回すfor文が3つの中のfor文で一番外のなる。このfor文で計算すると単位行列がでる。

まとめ

 配列は、同一型の変数の集合である。個々の変数が構成要素であり、その型が構成要素型である。構成要素自体が配列である配列は、多次元配列と呼ばれる。また、配列本体はnew演算子によって動的に生成しなければならないオブジェクトである。その配列本体を参照するのが、配列変数である。配列内の個々の構成要素は、明示的に初期化されなければ、既定値0で初期化される。配列の構成要素数は、“配列変数名.length”によって取得できる。要素の走査は、基本for文や拡張for文によって行える。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です