はじめMath! Javaでコンピュータ数学

第38回行列の数学 ベクトルとベクトルの加減算

ヤスリがけの習得は、比較的軟らかく薄い板の端面を一直線に仕上げることから始めます。例えば、厚み1mm、長さ100mm、幅50mmのアルミ板を用意しましょう。これを万力にはさみます。端から1mm程度のところにマジックで直線を引きます。この1mmをヤスリで削り落とす練習をするのです。一通り削ったら、まっすぐな物差しに当ててみます。蛍光灯の下に行って透かしてみると、引っ込んでいるところから光が漏れます。出っ張っているところに見当をつけてヤスリがけをします。また物差しに当てて光に透かします。光が漏れなくなったら完成です。いかがでしょう。簡単そうでしょう?でも、これがなかなか大変なんですよ。これが熟達してきたら、厚い板の端面に取り組みます。今度は厚みがありますから大変です。薄い板のうちは厚みを無視できましたが、厚くなると厚み方向の傾斜が問題になります。

行列の数学の学習も、シンプルなところから始めましょう。そしてだんだん複雑なものへステップアップしていきましょう。今回の演習問題はきっと拍子抜けするほど簡単だと思います。しかし、行列の演算は、先々とっても難しいものになります。言わば今回は「修行のスタート」なのです。一つ一つを丁寧に見ておいてください。

図38.1 ヤスリがけの初歩練習
図38.1 ヤスリがけの初歩練習

一行だけ、一列だけの行列=ベクトル

行列の中でも一行だけ、あるいは一列だけのもののことをベクトルといいます。ベクトルは「意味的に異なる数値」の集合です。ベクトルを記号で表す際には大文字のアルファベットを用います[1]⁠。式38.1のようなベクトルAを、1行2列のベクトルといいます。また、ベクトルAを構成しているそれぞれの数値のことを、ベクトルの成分成分がふたつありますので、このベクトルAの次元は2である、あるいは2次元のベクトルだ、といいます。

具体的な例にあてはめてみましょう。例えば、式38.2は、グラフ用紙の上の点の座標値を表すかもしれません。あるいは、今日私が持っている、10円玉と100円玉の数かも知れません。要は、それぞれの成分は異なる存在なのだということを意識してください。

例えばあるベクトルXがリンゴの数とミカンの数を表すものだとします。

仮にX=(1,2)だったとします。リンゴの数が1、ミカンの数が2です。もし、このベクトルをX=(2,1)と書いてしまうと、全く意味が変わります。

リンゴが2、ミカンが1となってしまうのです。成分の順番にははっきりと意味があることを覚えてください。

空間ベクトル(これが、あの矢印で表される方のベクトル)でも、そうだったでしょう。ある点Pの位置ベクトルの成分が(2,3)だとしましょう。このとき、2はX座標、3はY座標を表していますから、2と3を足したり引いたりすることは無意味です。

式38.3式38.4は次元が3のベクトルの例です。

ベクトルの加算・減算

さて、そんなベクトルの有効性はどこで表れるでしょうか。次の式のような、硬貨の数を表すベクトルを考えてみましょう。

この式は、例えば「Aさんは10円玉を3枚、100円玉を5枚持っています。Bさんは10円玉を2枚、100円玉を4枚持っています」という状況です。もし、ベクトルの計算式の書き方を知らなければ、各硬貨の合計枚数を知りたい場合に次のように書くことでしょう。

もちろん、これだって立派に役に立つ、正しい式なのです。ですが、ベクトルの計算式の表現方法を知っていれば、次のようなシンプルな書き方が出来るのです。

加算はこのとおりです。減算も同様に行うことが出来ます。

ベクトルの加算・減算は同じ次元数のもの同士であれば可能ですが、次元数の異なるベクトル同士の加算・減算は出来ませんので注意が必要です。なぜ出来ないのかは、先ほどの具体的な例(AさんとBさんの持っている硬貨の数)を思い浮かべれば、納得出来ることでしょう。

ベクトルをJava言語で表現しよう

ここまでのところを、Java言語で表現してみましょう。既にお気づきのことと思いますが、これまでの内容を表現するために、Java言語は便利な仕組みを持っています。それは配列やArrayListです。データを配列やArrayListに格納し、同じ添え字を持つデータを加減算すればよいのです。

さあ、単純な操作なので、いきなりこれを演習問題としてみましょう。

問題:ベクトルの加算を行う関数を作りましょう。

整数を成分として持つベクトルの加算メソッドを、配列版とArrayList版それぞれで作ってください。次元数はあらかじめわからないものとします。

引数として渡された配列やArrayListの要素数から次元数を判断し、次元の異なるもの同士の加減算を行おうとした場合は標準出力に「演算できません」と表示し、強制終了(※2するようにしましょう。

関数はAddMatrix(A,B,C)のように呼び出しましょう。行列Aと行列Bの和を行列Cに格納してください。

配列版はvAddMatrix(A,B,C)ArrayList版はaAddMatrix(A,B,C)という関数にしてください。

解説

先に配列版を示します。ベクトルの加減算は最もシンプルで考えやすい題材です。プログラミングを始めたばかりの人にとっては、ほどよい難易度の問題だったことでしょう。プログラミングを始めてしばらく経った人は、データの取り扱いに注意を払って実用的なメソッド作りを心がけると良いでしょう。

ソースコード:HairetsuDeMatrixAdd.java
///サンプルコード
//行列(ベクトル) の和をとる。配列版。
//filename : HairetsuDeMatrixAdd.java

class HairetsuDeMatrixAdd {

  static final int MAX_LENGTH = 255; //配列長の最大値

  public static void main(String[] args) {

    int a[] = {1,2,3,5,8}; //ベクトルa
    int b[] = {2,4,5,7,9}; //ベクトルb
// int b[] = {2,5}; //要素数が異なると加算できない
    int c[] = new int[MAX_LENGTH]; //ベクトルc

    //C = A + B
    vAddMatrix(a,b,c);
    //結果表示
    for( int i = 0 ; i < a.length ; ++i ){
      System.out.println("a["+i+"] + b["+i+"] = c["+i+"]");
      System.out.println(a[i] + " + " + b[i] + " = "+ c[i]);
    }
  }// end of main


  static void vAddMatrix(int a[], int b[], int c[]){
    if (a.length == b.length){
      //加算可能
      for( int i = 0; i < a.length ; i++){
        c[i] = a[i] + b[i];
      }
    } else {
      //加算不能
      System.out.println("加算できません");
      System.exit(0);
    }
  }

}// end of class HairetsuDeMatrixAdd

次にArrayList版を示します。ベクトルの加算と言うことに関しては、配列版と大きな差がありません。ArrayListクラスの使用練習として、やさしい課題だったはずです。

ソースコード:ArrayListDeMatrixAdd.java
//サンプルコード
//行列(ベクトル)の和をとる。ArrayList 版。
//filename : ArrayListDeMatrixAdd.java

import java.util.ArrayList;

class ArrayListDeMatrixAdd {

  public static void main(String[] args) {

    //ベクトルA の宣言と初期化
    ArrayList<Integer> A = new ArrayList<Integer>();
    int a[] = {1,2,3,5,8};
    for (int i=0 ; i < a.length ; ++i ) A.add( a[i] );
    //ベクトルB の宣言と初期化
    ArrayList<Integer> B = new ArrayList<Integer>();
    int b[] = {2,4,5,7,9};
// int b[] = {2,5}; //要素数が異なる場合は加算できない
    for (int i=0 ; i < b.length ; ++i ) B.add( b[i] );
    //ベクトルC の宣言
    ArrayList<Integer> C = new ArrayList<Integer>();

    //C = A + B
    aAddMatrix(A,B,C);
    //結果表示
    for( int i=0 ; i < A.size() ; ++i ){
      System.out.println("A("+i+") + B("+i+") = C("+i+")");
      System.out.println(A.get(i)+" + "+B.get(i)+" = "+C.get(i));
    }
  }// end of main


  static void aAddMatrix(ArrayList<Integer> A, ArrayList<Integer> B,
                         ArrayList<Integer> C){
    if (A.size() == B.size()){
      //加算可能
      for( int i = 0; i < A.size() ; i++){
        C.add( A.get(i) + B.get(i) );
      }
    } else {
      //加算不能
      System.out.println("加算できません");
      System.exit(0);
    }
  }

}// end of class HairetsuDeMatrixAdd

今回はここまで

Java言語を用いて、ベクトルの加減算が容易に実装できることを学びました。実用に耐えるものにするためには、計算結果がIntegerの許容範囲からオーバーしないか(オーバーフローしないか)のチェックをしたり、メソッドそのものがIntegerだけでなく、LongやDouble等の型(クラス)に対応したものであるべきでしょう。そのようなことについては、今後の皆さんの発展課題として記憶しておかれると良いでしょう。

おすすめ記事

記事・ニュース一覧