―アルゴリズム(コンピュータプログラム)は, 問題が機械的に解けるように,あらゆることを明示した,Step by Step の手順のことである。 どのStepも,散々骨を折って,詳細に説明されねばらなず, 一つとして,偶然や直観,経験や解釈,さらには想像力に委ねられてはならない。 (ポール・ホフマン, 数学の悦楽と罠―アルキメデスから計算機数学まで―, 白揚社) |
―大抵の人は,学校で数学を習った苦い経験から, 数学が細部にこだわる厳しい学問であることを十分承知しているが, あらゆる人間の営みの中で数学が一番自由で創造的であることを理解するに至る人は極めて少ない。 絶対的な厳密性は,意味のある夢を見る自由を手に入れる切符なのだ。 (ドナル・オシア, ポアンカレ予想, 新潮文庫) |
型名 配列名[次元数] ; /* ベクトルを扱いたい場合 */
型名 配列名[行数][列数] ; /* 行列を扱いたい場合 */
例えば,int型(整数型)で次元数が3のベクトルとして,nxという配列名の配列を使いたいときは,
int nx[3] ;
と宣言します。
double dx[2][3] ;
と宣言します。(このような配列を多次元配列と呼びます。)
nx[2] = 17 ; /* 配列nxの2番目の要素に17を代入 */
dx[1][2] = 27.8 ; /* 多次元配列dxの1行2列目の要素に27.8を代入 */
のように書くことによって,自由に数値を代入して記憶させておくことができます。nx
の要素としては,nx[0], nx[1], nx[2]
dx
の要素としては,dx[0][0], dx[0][1], dx[0][2],
dx[1][0], dx[1][1], dx[1][2]
int nx[3] ;
nx[3] = 17 ; /* 恐怖の代入 */
のようなコードを書いた場合,コンパイルはエラーが出ずにとおります(!!!)が,配列 nx
には,三つの要素(nx[0], nx[1], nx[2]
まで)の分のメモリ領域しか確保されていないため,nx[3]
という参照はメモリ中のどこの場所をさす結果となるのかは,プログラムが実際に走るまで全く分かりません。
int n ; /* これは型宣言文 */
n = 3 ; /* これは式文 */
int nx[n] ; /* これは型宣言文。変数nは次元数として使用不可,コンパイルエラーとなる */
ただし,配列の型宣言以外の場面においては,変数を添字として使用することはもちろん可能です。
int n, a ; /* これは型宣言文 */
int nx[3] ; /* これは型宣言文。定数3を次元数として配列を型宣言,これはOK */
n = 2 ; /* これは式文 */
a = nx[n] ; /* これは式文。変数nを要素番号を表す添字として使用,これはOK */
型名 配列名[次元数] = { 0番目の要素の初期値, 1番目の要素の初期値, ... } ;
例えば,
int nx[3] = {1, 2, 3} ;
と宣言すると,nx[0]
の値が 1
nx[1]
の値が 2
nx[2]
の値が 3
int nx[3][2] = { {1, 2}, {10, 20}, {100, 200} } ;
と宣言すると,nx[0][0]
の値が 1
,nx[0][1]
の値が 2
nx[1][0]
の値が 10
,nx[1][1]
の値が 20
nx[2][0]
の値が 100
,nx[2][1]
の値が 200
int nx[3] = {1, 2, 3} ;
int ny[3] ;
ny = nx ; /* 残念ながら,配列nyに配列nxの内容はコピーされない */
また,ベクトル同士の足し算をさせるつもりで,以下のようなコードを書いても,期待した動作はしません。
int nx[3] = {1, 2, 3} ;
int ny[3] = {10, 20, 30} ;
int a[3] ;
a = nx + ny ; /* 残念ながら,ベクトルnxとベクトルnyの足し算はされない */
上のような計算(ベクトル同士の足し算)をさせたい場合には,愚直に,配列の要素をひとつひとつ取り出して,ひとつひとつ足し算をしなければなりません。(Cf. テキスト126ページList5-13)
int nx[3] = {1, 2, 3} ;
int ny[3] = {10, 20, 30} ;
int a[3] ;
a[0] = nx[0] + ny[0] ; /* nxの0番目の要素とnyの0番目の要素を足し算して,結果をaの0番目の要素に代入 */
a[1] = nx[1] + ny[1] ; /* nxの1番目の要素とnyの1番目の要素を足し算して,結果をaの1番目の要素に代入 */
a[2] = nx[2] + ny[2] ; /* nxの2番目の要素とnyの2番目の要素を足し算して,結果をaの2番目の要素に代入 */
/* これで,期待どおり,ベクトルnxとベクトルnyを足し算した結果が,ベクトルaに代入された */
main( )
関数の前に,#
で始まる記述を書くことがあります。
#define PI 3.1416
と書くと,「コンパイルの前に,PI
という綴りがあったら,それを3.1416
に置き換えなさい」という指示になります。
#include <stdio.h>
と書くと,「コンパイルの前に,stdio.h
というファイルを読み込んで,それをソースプログラムに挿入しなさい」という指示になります(詳しくは,テキスト148ページ参照)。stdio.h
はその1例,コンパイラの開発者が作成するもの)が主ですが,自分でオリジナルのヘッダファイルを作成して使用することもできます。
/****************************************************************
Utility Header File for Physical Calculation
この内容のテキストファイルをmyheader.hなどのファイル名でセーブしておき,
別のソースプログラムの冒頭に,
#include <myheader.h>
と書けば,そのプログラム内でmyheader.h内のマクロ定義が使用できる。
****************************************************************/
/* macro definition of constants */
#define PAI 3.14159265358979323846 /* 円周率 */
#define PAI2 6.28318530717958647692 /* 円周率の2倍 */
#define PIO2 1.57079632679490 /* 一般完全楕円積分の数値計算に必要な定数 */
#define RRR 8.314 /* 気体定数[J/mol K] */
#define AIR 28.91 /* 空気の分子量 */
#define GANMA 1.41 /* 空気の比熱比 */
#define ATM 101325 /* 1気圧[N/m2] */
#define KEL 273.15 /* 絶対温度 */
#define METER_INCH 0.02539 /* インチ→メータ変換用 */
#define HTAB 0x09 /* 水平タブのJISコード */
#define SPACE 0x20 /* スペースのJISコード */
#define LF 0x0A /* 改行のJISコード */
#define CR 0x0D /* 復帰のJISコード */
/* macro functions */
#define maxi(X,Y) ( ((X)>(Y)) ? (X) : (Y) ) /* 大きい方の値を返す関数 */
#define mini(X,Y) ( ((X)>(Y)) ? (Y) : (X) ) /* 小さい方の値を返す関数 */
#define abso(X) ( ((X)>0) ? (X) : -(X) ) /* 絶対値を返す関数 */
pwd
| 現在の作業ディレクトリの名前を表示する |
ls
| ファイルやディレクトリの名前の一覧を表示する |
mkdir
| 新しいディレクトリを作成する |
$ mkdir 新ディレクトリ名 と入力すると,新ディレクトリが作成されます。 | |
cp
| ファイル(や ディレクトリ)をコピーする |
$ cp 複写元ファイル名 複写先ファイル名 と入力すると,複写元ファイルの内容が,複写先ファイルにコピーされます。(複写元ファイルと同じ内容のファイルがもう一つ作られます。) | |
mv
| ファイル(や ディレクトリ)を移動する |
$ mv 旧ファイル名 新ファイル名 と入力すると,旧ファイルの名前が,新ファイル名に変更されます。 $ mv 移動元ファイル名 移動先ディレクトリ名 と入力すると,移動元ファイルが,移動先ディレクトリの中に移動します。 | |
cd
| 現在の作業ディレクトリを変更する |
$ cd 新ディレクトリ名 と入力すると,現在の作業ディレクトリが,新ディレクトリに変更されます。 | |
rm
| ファイルを削除する |
$ rm ファイル名 と入力すると,指定したファイルが削除されます。これで削除したファイルは本当に消えてなくなりますので,注意して使って下さい。(MacやWindowsのゴミ箱のように後で取り出すことはできません。) | |
rmdir
| ディレクトリを削除する |
$ rmdir ディレクトリ名 と入力すると,指定したディレクトリが削除されます。このコマンドも,ディレクトリが永久になくなりますので,注意して使って下さい。 |
man
コマンドで調べることができます。ls
コマンドの詳細を調べるためには,以下のように入力します。$ man ls(enter)
ls
コマンドに関するマニュアル(オンラインマニュアル)を読むことができます。man
コマンドを用いて,自分で調べてみて下さい。$ mkdir program(enter)
ls
コマンドで program というディレクトリができているか確認してください。$ ls(enter)
$ mv list1-8.c program(enter)
ls
コマンドで,現在の作業ディレクトリから list1-8.c というファイルがなくなっているか確認してください。$ ls(enter)
cd
コマンドで,現在の作業ディレクトリを新しく作った program という名前のディレクトリに変更します。$ cd program(enter)
ls
コマンドで,現在の作業ディレクトリ(program に変わっています)に list1-8.c というファイルができているか確認してください。$ ls(enter)
cd
コマンドで,現在の作業ディレクトリをホームディレクトリに直しておきます。$ cd ..(enter)
(重要) 出席登録のための実習問題は,授業時間中に必ず提出して下さい。
#define ROW 4 /* A = B*C のとき,行列Aの行数(=行列Bの行数) */
#define COL 4 /* A = B*C のとき,行列Aの列数(=行列Cの列数) */
#define INN 3 /* A = B*C のとき,行列Bの列数(=行列Cの行数) */
int A[ROW][COL], B[ROW][INN], C[INN][COL] ;
int sum ; /* 型宣言文。この段階では,変数sumの値は不定!(0であるとは限らない) */;
.
.
.
for(i = 0 ; i < ROW ; i++){
for(j = 0 ; j < COL ; j++){
sum = 0 ; /* 行列Aの,i行j列の成分の値を一時的に収めるための入れ物を準備。下のforループに入る前に,0を代入して確実に0にしておく! */
for(k = 0 ; k < INN ; k++){
/* ここに,行列Aの,i行j列の成分を,計算する式文を書く */
sum += . . .
}
A[i][j] = sum ;
}
}
.
.
.
プログラムが出来たら,行列の積を求める具体的な問題を友達と出し合って,お互いの計算結果が同じになるかどうか,答え合わせをしてみるとよいでしょう。(重要) 宿題の締切は次の授業が始まる前までとします。きちんと動作をチェックしてから,提出して下さい。