今回はベクトル・複素数の扱い方の実習を行う.
複素数については,フーリエ変換の基礎となる部分なので,しっかりと理解しておく必要がある.
ベクトルとは,力や速度など,2つ以上の数の組み合わせで一つの物理量などを表す概念である.
一般的には,「大きさ」と「向き」を表す場合に用いられる.
これに対して,温度や圧力など,一つの値のみで表す量を「スカラー」と言う.
ベクトル量の例
スカラー量の例
複素数はベクトルとは異なる概念であるが,複数の実数(実部と虚部)からなり,一般的に
z = re + j im
と表され,プログラミングとしてはベクトルと似た扱いが可能である.
ここで,reを複素数の「実部, real part」,imを複素数の「虚部, imaginary part」という.
虚数単位は,数学では i で示すが,工学の分野では j が使われることも多い.
さて,Cの文法規則中には,組み込み変数型として「ベクトル」や「複素数」の概念があるわけではない.
したがって,ベクトルの各成分は配列として,また,複素数は実部と虚部の2つの実数の組み合わせとして扱う必要がある.
C言語でこのようなデータ形式を扱うためには,いままでの情報処理科目等で学んだ「配列」や「構造体」を使えば簡単に表現することができる.
ベクトルは複数の同一の型の成分が複数まとまったものである.
例えば,二次元座標であれば ( x, y ) の2つの実数,三次元の速度ベクトルは ( u, v, w ) の三つの実数からなる.
もちろん,成分が複素数であるベクトルも考えられる.
したがって,ベクトルをプログラム中で表現するには,配列または構造体を使う方法が考えられる.
例1:配列でベクトル (u, v, w)を表現double Vec[3]; /* Vec[0]がu, Vec[1]がv, Vec[2]がw */
例2:構造体でベクトル (u, v, w)を表現struct Vec { double u; double v; double w; };
複素数も同様に,例えば以下のようなパターンが考えられる.
例1:配列で複素数を表現double z[2]; /* z[0]が実部, z[1]が虚部 */
例2:構造体で複素数を表現struct Complex { double re; /* 実部 */ double im; /* 虚部 */ };
となる.
どちらも有効な方法であるが,それぞれ一長一短がある.
配列使用の長所
for, while
文などで各成分に順次アクセスでき,多成分のベクトル処理に不可欠.構造体では不可.構造体使用の長所
である.
情報処理実習2では,練習も兼ねて,ベクトルは配列で,複素数は構造体を用いてプログラミングを行う.
以下の手順に従って,プログラミングを行ってみよう.
いずれも計算結果が画面で確認できるようにすること.
まず,実数2つの配列からなるVec型を定義する.
(要素数は後から変更する可能性あり)
const int dim = 2;
typedef double Vec[dim];
以下の手順に従って,ベクトルを操作する各種関数を作成してみよう.
printVec()
関数を作成,main
関数も作成して動作確認を行う.
#include <stdio.h>
const int dim = 2; /* ベクトルの次元(=要素数), dimensionの略 */
typedef double Vec[dim]; /* typedefにより別名を定義.Vecはdouble型がdim個 = dim 次元の配列 */
void printVec(const Vec v)
{
printf("%lf , %lf\n", v[0], v[1]); /* %lfに関しては%fとも記述可能 */
}
int main()
{
Vec v1, v2;
/* 以下の値は一例.いろいろと変化させよ.*/
/* Vec型は,配列のtypedefなので,このような初期化が可能 */
Vec v1 = { 1.0, 3.0}; /* 初期化,初期値代入は初めの宣言でも可能 */
Vec v2 = {-2.0, 1.0}; /* 初期化,初期値代入は初めの宣言でも可能 */
printVec(v1);
printVec(v2);
return 0;
}
Vadd
を作成せよ.作成した関数が正しく動作するか,動作確認すること.Vsub
も作成せよ.
void Vadd(Vec p, const Vec v1, const Vec v2)
/*
* p : 計算結果を入れる.(アドレス参照であることに注意)
* v1,v2 : 計算するベクトル.変更しないので,const 指定
*/
{
p[0] = ...
p[1] = ...
}
int main()
{
Vec v1, v2, v3;
...
Vadd(v3, v1, v2); /* v3 = v1 + v2 の計算 */
print(v3); /* 計算結果の表示 */
return 0;
}
実行例
v1 : 1.000000 , 3.000000
v2 : -2.000000 , 1.000000
和 = -1.000000 , 4.000000
差 = 3.000000 , 2.000000
double Vabs(const Vec v)
およびプログラムを作成しよう.sqrt()
関数を用いる.要ヘッダーファイル:math.h
実行例 v1 : 1.000000 , 3.000000 abs = 3.162278
double inner_product(const Vec v1, const Vec v2)
およびプログラムを作成しよう.
実行例
v1 : 1.000000 , 3.000000
v2 : -2.000000 , 1.000000
内積 = 1.000000
上記,各関数および関数のテストコードは,単一のソースコードに順次まとめて記述すれば良い.
ソースファイル名を 153R000000-05-1.cpp とする.(000000の部分は各自の学生番号)
前問で作成した関数群を利用して,(正規化された)相互相関係数の計算を行ってみよう.
2つのベクトル x,y の相互相関係数は,上記の内積および絶対値を組み合わせて,
R = x・y / (|x||y|)
と表すことができる.
相互相関は,信号の類似度合いを決定したり,画像中から特定のパターンを検索する処理に非常に広く用いられている.
相互相関係数は,2つのベクトルの類似度を表し,完全に一致するときは 1.0,全く異なる(=直交する)ときは 0.0,ベクトルの向きが真逆の場合は -1.0 となるような係数である.
相互相関係数は,幾何的には,2つのベクトルのなす角 θ の余弦 cosθを表す.
相互相関係数=0(即ち,内積=0)となる2ベクトルは,「直交する」という.( cosθ=0 即ち θ=90度であるため)
それでは,実際にベクトルの相互相関係数を計算してみよう.
以下の4本のベクトルの中から任意の2つのベクトルの相互相関係数を計算し,
プログラムを作成せよ.
ここでは簡単のために,全ての組み合わせの相関係数を計算し,表として表示してみよう.
相関関数を計算する関数を double corr(const Vec v1, const Vec v2)
とせよ.
上に示した式の通り,inner_product()
および Vabs()
を用いれば,簡単である.
const int dim = 8;
typedef double Vec[dim];
int main(void)
{
Vec v1 = { 1, 2, 4, 5, 4, 1, -1, -2 };
Vec v2 = { 3, 3, 4, 6, 3, 2, 1, 1 };
Vec v3 = {-1, 2, 0, 2, -2, -1, 3, 1 };
Vec v4 = { 2, 1, 3, -9, 2, 8, 2, 8 };
....
return 0;
}
実行例.相関値のテーブルを全て計算. この実行例では,printf
関数の書式指定文字列に,%+2.2lf
を用いている.(符号付,小数点第2位まで表示) | v1 v2 v3 v4 ---+------------------------- v1 | +1.00 +0.87 -0.02 -0.25 v2 | +0.87 +1.00 +0.24 -0.01 v3 | -0.02 +0.24 +1.00 -0.21 v4 | -0.25 -0.01 -0.21 +1.00
図1:ベクトル v1-v4までの視覚的表示.
ソースコードを提出すること.ファイル名は,153R000000-05-2.cpp とする.