1 C 言語入門 第 7 週 プログラミング言語 Ⅰ( 実習を含む ), 計算機言語 Ⅰ 計算機言語演習 Ⅰ, 情報処理言語 Ⅰ( 実習を含む )
2 吐き出し法 ( ガウスの消去法 ) のピボッティング 前回の復習
3 連立一次方程式を行列で計算する 吐き出し法 ( ガウスの消去法 ) ステップ 1: 前進消去 ( 上三角行列の作成 ) gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) { a[i][j] = a[i][j] - a[k][j] * x; b[i] = b[i] - b[k] * x; まずどこでピボッティング処理するのが良いのか考える少なくとも a[k][k] で割る前でないと意味がないので候補はこの 3 か所
4 連立一次方程式を行列で計算する 吐き出し法 ( ガウスの消去法 ) ステップ 1: 前進消去 ( 上三角行列の作成 ) gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) { a[i][j] = a[i][j] - a[k][j] * x; b[i] = b[i] - b[k] * x; k 毎に処理しないといけないので一番上は除外 k 毎に処理しておけば i 毎に処理する必要はない従って一番下も除外結果 真ん中が最適と言うことになる
5 ピボッティング a[k][k] が 0 か調べる gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { // この場合にピボッティングする必要がある for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) { a[i][j] = a[i][j] - a[k][j] * x; b[i] = b[i] - b[k] * x;
6 ピボッティング k+1 行目以降で k 列目が 0 でない行を探す gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { // ここで a[i][k] が 0 でない行を探す //...
7 ピボッティング 目的の行を見つけた際の処理を書く gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k]!= 0) { // 目的の行を見つけた際の処理を書く 授業では最初ここを a[i][k] == 0 としていたので 上手く動いていませんでした //...
8 ピボッティング k 行と i 行の各列で値を交換する gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k]!= 0) { for (j = 0; j < N; j++) { // k 行と i 行の各列で a の値を交換する処理 // k 行と i 行で b の値を交換する処理 //...
9 ピボッティング 暫定的に完成したピボッティング gaussian_elimination1.c if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k]!= 0) { float tmp; for (j = 0; j < N; j++) { tmp = a[k][j]; a[k][j] = a[i][j]; a[i][j] = tmp; tmp = b[k]; b[k] = b[i]; b[i] = tmp; なぜ暫定的かと言うとこのままだと 二重にピボッティングする事もある 実害はないが無駄な処理なのでピボッティングに成功したらそこでピボッティングの処理を打ち切るほうが効率的
10 continue 文 break 文によるループの再開と脱出 繰り返し ( ループ ) の復習
11 ループの再開と脱出 continue 文 while, do while, for 文内で使用可能 以降の処理を中断してループを再開する for 文では後処理 ( 第 3パラメータ ) も実行する break 文 continue 文の while, 2do つ目の説明が微妙に間違ってました while, for, switch 文内で使用可能 以降の処理を中断してループを脱出する正しくはループ末尾から再開 8 週資料参照 switch 文の場合はswitch 文から脱出する
教科書 p.123. 12 後判定ループ (do while 文 ) 真偽値による繰り返し do { // 条件式が真の場合の処理 while ( 条件式 ); continue 処理 break do while 文内の continue 文で飛ぶ先が間違ってました正しくは次項または 8 週資料参照 真 条件式 偽 do while 文は ループ内の処理を最低 1 回は実行する
教科書 p.123. 13 後判定ループ (do while 文 ) 真偽値による繰り返し do { // 条件式が真の場合の処理 while ( 条件式 ); continue 処理 break 真 条件式 偽 do while 文は ループ内の処理を最低 1 回は実行する
教科書 pp.124-129. 初期化 更新処理付きループ (for 文 ) 14 真偽値による繰り返し 式 1 for ( 式 1; 式 2; 式 3) { // 式 2 が真の場合の処理 ; 式 2 偽 前判定ループだが式 1 による初期化と式 3 による更新処理をひとまとめにして書ける continue 真 処理 式 3 break
教科書 pp.123-129. 15 for 文と while 文 ( 前判定ループ ) 以下のループは等価 continue 時の式 3 の扱いに注意 式 1 for ( 式 1; 式 2; 式 3) { // 式 2 が真の場合の処理 ; 式 1; while ( 式 2) { // 式 2 が真の場合の処理式 3; ; while 文の continue for 文の continue 式 2 真処理式 3 偽 break
教科書 pp.123-129. 16 for 文と while 文 ( 前判定ループ ) 以下のループは等価 continue 時の式 3 の扱いに注意 i = 0 for (i = 0; i < 10; i++) { // ループ内の処理 ; i = 0; while (i < 10) { // ループ内の処理 i++; ; while 文の continue for 文の continue i < 10 真処理 i++ 偽 break
教科書 pp.119-122. 17 後判定ループ (do while 文 ) continue, break 後の処理 (i の値 ) に注目 looptest_dowhile.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); do { j++; printf("%d, %d: 1st", i, j); if (j == 2) {printf(" continue n"); continue; printf(" 2nd"); if (j == 4) {printf(" break n"); break; printf(" 3rd n"); i++; while (i < n); mintty + bash $./looptest_dowhile n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $./looptest_dowhile n = 0 0, 1: 1st 2nd 3rd do while 文は ループ内の処理を最低 1 回は実行する
教科書 p.123. 18 前判定ループ (while 文 ) continue, break 後の処理 (i の値 ) に注目 looptest_while.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); while (i < n) { j++; printf("%d, %d: 1st", i, j); if (j == 2) {printf(" continue n"); continue; printf(" 2nd"); if (j == 4) {printf(" break n"); break; printf(" 3rd n"); i++; mintty + bash $./looptest_while n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $./looptest_while n = 0
教科書 pp.124-129. 19 初期化 更新処理付きループ (for 文 ) continue, break 後の処理 (i の値 ) に注目 looptest_for.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); for (i = 0; i < n; i++) { j++; printf("%d, %d: 1st", i, j); if (j == 2) {printf(" continue n"); continue; printf(" 2nd"); if (j == 4) {printf(" break n"); break; printf(" 3rd n"); mintty + bash $./looptest_for n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 2, 3: 1st 2nd 3rd 3, 4: 1st 2nd break mintty + bash $./looptest_for n = 0
余談 20
教科書 pp.265-272. [1] pp.139-144. コマンドライン引数 21 main 関数の引数として取得出来る argtest.c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int i; main の引数名は自由につけて良いが以下の名前を使うのが慣例になっている argc: ARGument Count argv: ARGument Vector printf("argc = %d n", argc); for (i = 0; i < argc; i++) { printf("argv[%d] = "%s " n", i, argv[i]); return EXIT_SUCCESS; argument は英語で引数を意味する
教科書 pp.265-272. [1] pp.139-144. コマンドライン引数 22 main 関数の引数として取得出来る コマンドプロンプト >argtest a b "c d" "e "f" argc = 5 argv[0] = "C: Users kou Desktop CLangI2014S1 argtest.exe" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" mintty + bash $./argtest a b "c d" "e "f" argc = 5 argv[0] = "./argtest" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" コマンドライン空白で分割されて argv に格納される argv に空白を含めたい場合は " ( ダブルクォーテーション ) で囲む " を含めたい場合は でエスケープする argv[0] は実行中のコマンド名
[1] pp.196, 199, 218. 23 標準入出力と標準エラー出力 以下の入出力が利用できる stdin: STanDard INput: stdout: STanDard OUTput: 標準入力 標準出力 stderr: STanDard ERRor output: 標準エラー出力 scanf や getchar 等は stdin から入力している printf や putchar 等は stdout へ出力している stdin, stdout はパイプやリダイレクトの対象だが stderr は標準では対象外
[1] pp.196, 199, 218. 24 標準入出力と標準エラー出力 パイプやリダイレクトで処理されたくない内容は stderr へ出力する fscanf や fprintf を使うと 入出力先を自由に選択出来る stdiotest.c mintty + bash printf("output to stdout with printf n"); fprintf(stdout, "output to stdout with fprintf n"); fprintf(stderr, "output to stderr with fprintf n"); $./stdiotest > redirect.txt output to stderr with fprintf $ cat redirect.txt output to stdout with printf output to stdout with fprintf
[1] pp.196, 199, 218. 25 標準入出力と標準エラー出力 stdin,stdout,stderr は stdio.h で定義されている stdio.h は standard input / output header
教科書 pp.61, 64-66, 98, 300. 26 fprintf 関数 int fprintf(file *fp, const char *FORMAT,...); printf の結果を fp へ書き出す 引数 : fp: FORMAT:...: 戻り値 : 書き出された文字数 エラーの場合負の数 FILE 構造体へのポインタ書式任意の数の引数 参考 : [1] pp.305-306.
教科書 pp.80-83, 254. 27 fscanf 関数 int fscanf(file *fp, const char *FORMAT,...); fp からデータを読み込む 引数 : fp: FORMAT:...: FILE 構造体へのポインタ書式任意の数の引数値を格納する変数へのポインタ 戻り値 : 変換され代入された入力項目の数 ファイル終端またはエラーの場合 EOF 参考 : [1] pp.307-309.
教科書 pp.298-305. 28 fopen 関数 FILE *fopen(const char *filename, const char *mode); ファイルを開き FILE 構造体へのポインタを得る 引数 : filename: ファイル名 ( パス ) の文字列 mode: 戻り値 : ファイルを開くモード FILE 構造体へのポインタ エラーの場合 NULL 参考 : [1] pp.194-198.
教科書 pp.298-305. 29 fopen 関数の mode mode 読み込み書き込み動作 "r" 任意の位置 ファイルを開く 存在しない場合エラー "w" 任意の位置ファイルを作成し 前の内容は消去する "a" ファイル末尾ファイルを開く または作成 "r+" 任意の位置任意の位置ファイルを開く 存在しない場合エラー "w+" 任意の位置任意の位置ファイルを作成し 前の内容は消去する "a+" 任意の位置ファイル末尾ファイルを開く または作成 "r", "w", "a", "r+", "w+", "a+" はテキストモードで読み書きするテキストモードでは改行コード ( n) の扱いが環境によって異なる Windows: CR LF (0xd 0xa) Mac: CR (0xd) UNIX: LF (0xa) バイナリモードにするには "rb", "wb", "ab", "r+b", "w+b", "a+b" のように "b" を追加する 参考 : [1] pp.194-198.
教科書 pp.298-305. 30 fclose 関数 int fclose(file *fp); ファイルを閉じます 引数 : fp: FILE 構造体へのポインタ 戻り値 : 0 を返す エラーの場合 EOF を返す 参考 : [1] pp.194-198.
演習 31
32 演習 : print_0.c 0 を表示せよ 数値の直後で改行すること コマンドプロンプト >print0 0 >
第 3 週資料 pp.23-33, 34-42. 33 演習 : print_dec.c scanf 関数を用いてキーボードから入力された整数をint 型の変数 iに読み込み10 進数として表示せよ i を入力する前に i = と標準エラー出力に表示する事 mintty + bash $./print_dec i = 123 123
第 3 週資料 pp.23-33, 34-42. 34 演習 : print_octdechex.c scanf 関数を用いてキーボードから入力された 8,10,16 進数の整数を int 型の変数 i に読み込み 8,10,16 進数の 3 種類の表示せよ i を入力する前に i = と標準エラー出力に表示する事 8 進数表示の前には 0 を表示せよ 16 進数表示の前には 0x を表示せよ 各数値は幅 6 桁で右寄せで表示し末尾は改行すること 8,16 進数頭の 0,0x は 6 桁に含める mintty + bash $./print_octdechex i = 0123 0123 83 0x53 $./print_octdechex i = 123 0173 123 0x7b $./print_octdechex i = 0x123 0443 291 0x123
35 演習 : print_evenodd.c i が偶数か奇数か調べ以下の文字列を printf 関数に与えて表示せよ 偶数の場合 : "even number n" 奇数の場合 : "odd number n" print_evenodd_tmp.c を print_evenodd.c にコピーし指定の場所に作成する事 mintty + bash $./print_evenodd i = 1 odd number mintty + bash $./print_evenodd i = 2 even number
36 演習 : print_natural_lt.c n 未満の非負整数 (0 を含む自然数 ) を小さい順に全て表示せよ 各数値の直後で改行すること 変数は i,n のみを使うこと print_natural_lt_tmp.c を元に 指定の場所に作成する事 mintty + bash $./print_natual_lt n = 10 0 1 2 3 4 5 6 7 8 9
37 演習 : print_even_lt.c 0 以上 n 未満の偶数を小さい順に全て表示せよ 各数値の直後で改行すること 変数は i,n のみを使うこと print_even_lt_tmp.c を元に 指定の場所に作成する事 mintty + bash $./print_even_lt n = 20 0 2 4 6 8 10 12 14 16 18
38 素数 1 と自分以外に正の約数を持たない自然数 ( 正整数 ) で 1 でない数 調べたい数を i とすると 2 以上 i/2 以下の整数 j の全てについて i が割り切れないことを確認すれば良い
39 演習 : print_isprime.c i が素数かどうか調べ以下の文字列を printf 関数に与えて表示せよ 非素数 : "not prime number n" 素数 : "prime number n" 変数は i,j,prime のみを使う事 変数は以下の目的で使う事 i: 素数かどうか調べたい数 j: 2 以上 i/2 以下の正数 prime: 素数判定用のフラグ 0= 非素数,1= 素数 print_isprime_tmp.c を元に指定の位置に作成せよ mintty + bash $./print_prime_lt n = 2 $./print_isprime i = 2 prime number $./print_isprime i = 3 prime number $./print_isprime i = 5 prime number $./print_isprime i = 7 prime number $./print_isprime i = 11 prime number
40 演習 : print_isprime.c 非素数の場合も確認 mintty + bash mintty + bash $./print_isprime i = 4 not prime number $./print_isprime i = 6 not prime number $./print_isprime i = 8 not prime number $./print_isprime i = 9 not prime number $./print_isprime i = 10 not prime number 合成数の場合 $./print_isprime i = -1 not prime number $./print_isprime i = 0 not prime number $./print_isprime i = 1 not prime number 負の場合 0 及び 1 の場合
41 演習 : print_prime_lt.c n 未満の素数を小さい順に全て表示せよ 各数値の直後で改行すること 変数は i,j,n,prime のみを使う事 変数は以下の目的で使う事 i: 素数かどうか調べたい数 j: 2 以上 i/2 以下の数 prime: 素数判定用のフラグ print_prime_lt_tmp.c を元に指定の位置に作成せよ mintty + bash $./print_prime_lt n = 30 2 3 5 7 11 13 17 19 23 29
42 フィボナッチ数列 いわゆる黄金比の数列 F 0 = 0 F 1 = 1 F n+2 = F n + F n+1 (n 0)
43 演習 : print_fibonacci_n.c フィボナッチ数列のうち最小の n 個を小さい順に表示せよ 各数値の直後で改行すること 変数は f0, f1, f2, i, n のみを使うこと 各変数は以下の用途で用いる事 f0 = F i f1 = F i+1 f2 = F i+2 i は上記 F i の添え字の意味で 0~n-1 までの整数 mintty + bash $./print_fibonacci_n n = 10 0 1 1 2 3 5 8 13 21 34
44 演習 : print_fibonacci_lt.c n 未満のフィボナッチ数列を小さい順に全て表示せよ 各数値の直後で改行すること 変数は f0, f1, f2, n のみを使うこと 各変数は以下の用途で用いる事 f0 = F i f1 = F i+1 f2 = F i+2 mintty + bash $./print_fibonacci_lt n = 40 0 1 1 2 3 5 8 13 21 34
45 参考文献 [1] B.W. カーニハン /D.M. リッチー著石田晴久訳 プログラミング言語 C 第 2 版 ANSI 規格準拠 共立出版 (1989)