連載
バグ検出ドリル(3)それでもプログラマーは数学を知っておくべきだ:山浦恒央の“くみこみ”な話(103)(2/3 ページ)
「バグ検出ドリル」の第3回で出題するのは、前回に引き続き数学に関係する問題。理系とはいえ必ずしも数学が得意ではないプログラマーですが、やはり数学を知るメリットは絶大。これまでと比べてかなり歯ごたえ十分な問題を用意したので、じっくり取り組んで数学に対する免疫力をつけてみよう!
4.今回の問題
新人エンジニアA君は、先輩からリスト1-1に示す3次元座標の回転アルゴリズムを実装するよう依頼された。A君は、数学が得意でなかったが、数式通りに実装すれば問題ないと考えた。実装は、C++で作成した(リスト1-2)。実行結果(リスト1-3)を見ると、リスト1-1に示す予想結果が合致しないことが分かった。上記から、実行結果が異なる理由を推察せよ。
仕様:3次元の物体をZ軸に60度、X軸に180度、Y軸に90度回転する組み合わせ行列を作成し、その式から頂点A座標(200, 0, -30)、B座標(0, 50, -150)、C座標(40, 20, -100)の三角形を回転し、コンソールに出力する。
組み合わせ行列アルゴリズムは、以下に示す。
θx:X軸回りの回転角度[rad]、θy:Y軸回りの回転角度[rad]、θz:Z軸回りの回転角度[rad]
実行結果は、下記となる。
座標A(30.00, -173.21, -100.00)
座標B(150.00, -25.00, 43.30)
座標C(100.00, -44.64, -2.68)
リスト1-1 座標回転プログラムのアルゴリズム
#include <stdio.h>
#include <math.h>
#define PI 3.14159265359
//3*3の行列クラス
class Matrix33 {
private:
int i,j; //ループ変数
double index[3][3]; //行列の要素
public:
//3行3列の行列の値を0で初期化する
Matrix33(){
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
index[i][j] = 0.0;
}
}
}
~Matrix33(){}
//指定した要素に値を代入
void SetVal(int x, int y, double value){
index[x][y] = value;
}
//指定した要素の値を出力
double GetIndex(int i, int j) {
return index[i][j];
}
//X軸、Y軸、Z軸の回転行列を算出する
Matrix33 Rotate(double xrad, double yrad, double zrad){
Matrix33 res_zx;
Matrix33 res_zxy;
Matrix33 mx, my, mz;
//X軸回りの回転行列
mx.SetVal(0, 0, 1.0);
mx.SetVal(1, 1, cos(xrad));
mx.SetVal(1, 2, -1.0 * sin(xrad));
mx.SetVal(2, 1, sin(xrad));
mx.SetVal(2, 2, cos(xrad));
//Y軸回りの回転行列
my.SetVal(0, 0, cos(yrad));
my.SetVal(0, 2, sin(yrad));
my.SetVal(1, 1, 1);
my.SetVal(2, 0, -1.0 * sin(yrad));
my.SetVal(2, 2, cos(yrad));
//Z軸回りの回転行列
mz.SetVal(0, 0, cos(zrad));
mz.SetVal(0, 1, -1.0 * sin(zrad));
mz.SetVal(1, 0, sin(zrad));
mz.SetVal(1, 1, cos(zrad));
mz.SetVal(2, 2, 1);
res_zx = Multiply33(mz, mx);
res_zxy = Multiply33(res_zx,my);
return res_zxy;
}
//3行3列の行列の乗算をする
Matrix33 Multiply33(Matrix33 m1, Matrix33 m2){
Matrix33 res;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
res.index[i][j] += (m1.index[i][k] * m2.index[k][j]);
}
}
}
return res;
}
};
//3*3の行列クラス
class Matrix31{
private:
int i,j;
double index[3];
public:
//3行1列の行列を初期化
Matrix31(){
for (i = 0; i < 3; i++) {
index[i] = 0;
}
}
~Matrix31(){}
//3行1列の行列に値を代入
void SetIndex(double val1, double val2, double val3){
index[0] = val1;
index[1] = val2;
index[2] = val3;
}
//3行3列の行列×3行1列の行列
Matrix31 Multiply31by33(Matrix33 m1, Matrix31 m2){
Matrix31 res;
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
res.index[i] += (m1.GetIndex(i,j) * m2.index[j]);
}
}
return res;
}
//3行1列の行列の値をコンソールに出力
void PrintConsole(Matrix31 m){
for (i = 0; i < 3; i++) {
printf("%.2lf ",m.index[i]);
}
printf("\n");
}
};
int main(void)
{
Matrix33 RotateMatrix; //3*3の行列クラスの宣言
Matrix31 A, B, C; //3*1の行列クラスの宣言
double xdeg = 180.0; //X軸回りの角度
double ydeg = 90.0; //Y軸回りの角度
double zdeg = 60.0; //Z軸回りの角度
//回転行列の作成
RotateMatrix = RotateMatrix.Rotate(xdeg * PI / 180.0 , ydeg * PI / 180.0, zdeg * PI / 180.0);
A.SetIndex(200.0, 0.0, -30.0); //座標Aの作成
B.SetIndex(0.0, 50.0, -150.0); //座標Bの作成
C.SetIndex(40.0, 20.0, -100.0); //座標Cの作成
A = A.Multiply31by33(RotateMatrix, A); //座標Aの回転
B = B.Multiply31by33(RotateMatrix, B); //座標Bの回転
C = C.Multiply31by33(RotateMatrix, C); //座標Cの回転
printf("実行結果\n");
printf("座標A\n");
A.PrintConsole(A); //座標Aの値を出力
printf("座標B\n");
B.PrintConsole(B); //座標Bの値を出力
printf("座標C\n");
C.PrintConsole(C); //座標Cの値を出力
return 0;
}
リスト1-2 座標回転プログラム
実行結果 座標A -15.00 -25.98 200.00 座標B -31.70 -154.90 -0.00 座標C -32.68 -96.60 40.0
リスト1-3 実行結果
Copyright © ITmedia, Inc. All Rights Reserved.