情報処理Ⅱ 第11回 2004年12月21日(火).

Slides:



Advertisements
Similar presentations
【事例演習5】  字句解析     解 説  “ハッシュを用いた字句解析の方法”.
Advertisements

アルゴリズムとデータ構造 第2回 線形リスト(復習).
Generic programming と STL
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
情報処理Ⅱ 第10回 2004年12月14日(火).
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
ファーストイヤー・セミナーⅡ 第8回 データの入力.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング入門2 第10回 構造体 情報工学科 篠埜 功.
情報処理Ⅱ 2007年12月17日(月).
情報処理Ⅱ 2005年12月9日(金).
情報処理Ⅱ 2005年12月22日(木).
第8回 プログラミングⅡ 第8回
プログラミング実習 1・2 クラス 第 1 週目 担当教員:  渡邊 直樹.
アルゴリズムとデータ構造 2011年6月13日
構造体.
プログラミング演習II 2004年12月 21日(第8回) 理学部数学科・木村巌.
情報処理Ⅱ 2007年12月10日(月).
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
情報処理Ⅱ 第9回 2004年12月7日(火).
二分探索木によるサーチ.
情報処理Ⅱ 2007年11月5日(月).
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
関数の定義.
Cプログラミング演習 第7回 メモリ内でのデータの配置.
精密工学科プログラミング基礎 第10回資料 (12/18実施)
プログラミング 4 記憶の割り付け.
前回の練習問題.
第7回 プログラミングⅡ 第7回
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
第11回 プログラミングⅡ 第11回
P n ポインタの基礎 5 q m 5 7 int* p; int 型の変数を指すポインタ int* q; int 型の変数を指すポインタ int n=5, m=7; int 型の変数 int array[3]; int* pArray[3]; p = &n; ポインタにアドレスを代入しているのでOK.
情報処理Ⅱ 第14回 2006年1月23日(月).
アルゴリズムとデータ構造 2010年7月26日
情報処理Ⅱ 第2回:2003年10月14日(火).
プログラミング 4 木構造とヒープ.
コンパイラ 2011年10月20日
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
精密工学科プログラミング基礎Ⅱ 第5回資料 今回の授業で習得してほしいこと: 構造体 (教科書 91 ページ)
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとデータ構造1 2008年7月24日
情報処理Ⅱ 第2回 2005年10月14日(金).
情報処理Ⅱ 第2回 2006年10月13日(金).
アルゴリズムとデータ構造1 2009年6月15日
第5回 プログラミングⅡ 第5回
情報処理Ⅱ 2006年12月22日(金).
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
情報処理Ⅱ 2006年11月24日(金).
情報処理Ⅱ 第7回 2004年11月16日(火).
情報処理Ⅱ 2005年10月28日(金).
アルゴリズムとデータ構造 2010年6月17日
ソフトウェア工学 知能情報学部 新田直也.
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
情報処理Ⅱ 2006年11月8日(金).
関数と再帰 教科書13章 電子1(木曜クラス) 2005/06/22(Thu.).
情報処理Ⅱ 2007年2月2日(金).
プログラミング演習I 2003年6月11日(第9回) 木村巌.
情報処理Ⅱ 第2回 2004年10月12日(火).
情報処理Ⅱ 2005年11月25日(金).
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
第10回 関数と再帰.
プログラミング基礎a 第5回 C言語によるプログラミング入門 配列と文字列
プログラミング入門2 第5回 配列 変数宣言、初期化について
第4回 配列.
情報処理Ⅱ 小テスト 2005年2月1日(火).
第5回 配列.
情報処理Ⅱ 第8回:2003年12月9日(火).
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング 2 静的変数.
Presentation transcript:

情報処理Ⅱ 第11回 2004年12月21日(火)

本日学ぶこと:プログラミングの例題3つ 行列式の計算 「ぷよ連結問題」の再帰的解法 「ぷよ連結問題」の非再帰的解法 学ぶこと:配列を含む構造体の利用,再帰呼び出し 「ぷよ連結問題」の再帰的解法 学ぶこと:列挙型の利用,多次元配列を引数とする関数 「ぷよ連結問題」の非再帰的解法 学ぶこと:構造体の利用,配列の写像化 1 2 3 4 5 6 7 8 9 = 0

なぜ行列式を必要とするのか? 行列や線形代数は,工学の多くの分野の基礎となる. n行n列の行列Aに関する等価な命題 以下では,行数=列数 行列Aによる線形写像(一次変換)は,n次元の空間全体をそれ自身に移す(全単射となる) 行列式|A|≠0 ⇒プログラミングできるのでは? 以下では,行数=列数 「n次の正方行列(square matrix)」を扱う. nは処理の中で変わり得る.

どのようにして行列式を求めるか? n=1のときは,成分の値が行列式 n=2, 3のときは「たすきがけ」で求められる. 順列と符号から求める. 行列の基本操作で求める. 余因子から求める.⇒採用! 1 2 3 5 6 = -3 5 = 5 4 5 6 = 5 8 9 7 8 9

余因子を用いた行列式の求め方 = × + (-1) × × … + + (-1)n-1 × × … … … n次の行列式は, 積と和で求められる. ⇒ 再帰的な定義! … 符号 各行の1列目の 要素 各行と1列目を 除いた行列の行列式

sqmatrix.cで定義した構造体とその利用方法 最大で n=5 までの次数の行列を格納できる…非効率な上,柔軟性に欠ける 「オブジェクト」と「それを指すポインタ」を定義するのは面倒だ.なんとかできないか… typedef struct sqmatrix {  int size;  double element[5][5];  } sqmatrix; matrix m, *pm = &m; element [0][0] [0][1] [0][2] [0][3] [0][4] [1][0] [1][1] [1][2] [1][3] [1][4] [2][0] [2][1] [2][2] [2][3] [2][4] [3][0] [3][1] [3][2] [3][3] [3][4] [4][0] [4][1] [4][2] [4][3] [4][4] 2次元の添字は 先に y 座標 後に x 座標 ~列目 (x座標) ~行目 (y座標)

sqmatrix.cで定義した関数(1) sqmatrix *init_sqmatrix(sqmatrix *pm, int size) 行列の次数を指定し,各成分の値を0にする. sqmatrix *set_row(sqmatrix *pm, int y, double *vector) 行列の特定の行の値(行ベクトル)を設定する. void print_sqmatrix(sqmatrix *pm, double *det) 行列の内容を出力する. detがNULLでなければ,行列式の値として*detも出力する. 「init」は 「initialize」の略

sqmatrix.cで定義した関数(2) sqmatrix *remove_row_column(sqmatrix *pm, int y, int x) 指定された行と列を取り除いた行列にする. pmが参照するsqmatrixオブジェクトが書き換えられる. 次数(行数,列数)は1減る. double calc_determinant(sqmatrix *pm) 行列式を求める. 再帰呼び出しを使用している. 次数の小さい行列は,関数内のローカル変数(auto変数)としてオブジェクトが生成される.

実行して確認 1 2 3 4 5 6 7 8 9 = 0 1 2 3 4 5 6 7 8 = ? 1/2 2/2 3/2 4/2 5/2 6/2 7/2 8/2 0/2 = ?

ぷよ連結問題 ぷよが配置された2次元空間(フィールド)と,その中の座標を入力に取り,そこと連結する「ぷよ」の個数を求めよ.

フィールドの表現(1) フィールドは2次元 ⇒2次元配列を使用 色は赤・青・黄と空白 ⇒char型で表現 2次元の添字は 先に y 座標 後に x 座標 char field[高さ][幅] = { 0, 0, 3, 0, 0, 0, 3, 3, 2, 1, 0, 1, 1, 3, 2, 2, 2, 2, 1, 1, 2, 3, 3, 2, 1, 2, 2, 3, 1, 3, 2, 2, 1, 3, 3, 3, }; …1 …2 …3 空白…0

フィールドの表現(2) しかし… マスに入る値を列挙型で表現する. マスに入る値はいろいろあるのでは? 色の番号(数値)は重要か? enum puyo { P_EMPTY, P_RED, P_BLUE, P_YELLOW, P_GREEN, P_PURPLE, P_OJAMA, P_MAX }; enum puyo型の値(もしくは整数値)が色ぷよか否かは,自作関数is_coloredで判定する. 7

ぷよ連結問題・再帰版(1) 考え方 着目する地点の上下左右に探索を 広げる. 連結するぷよの個数は, 1 + 「上に伸ばして連結する個数」 + 「下に伸ばして連結する個数」 + 「左に伸ばして連結する個数」 + 「右に伸ばして連結する個数」 探索済の情報を格納しておく. フィールドの値の最上位ビットで 無限ループ回避のため 1 探索済? ぷよの種類

ぷよ連結問題・再帰版(2) 2次元フィールドfieldと,その中の座標(x,y)を入力に取り,そこと色ぷよcolorで連結するぷよの個数calc_puyo(field, x, y, color)は, 座標がフィールド外なら,0 色ぷよでないか,色がcolorと異なっていれば,0 探索済なら,0 以上のいずれでもないなら,そこを探索済とした上で, 1 + calc_puyo(field, x, y-1, color) + calc_puyo(field, x, y+1, color) + calc_puyo(field, x-1, y, color) + calc_puyo(field, x+1, y, color) 自作関数 is_connected 再帰呼び出し!

バックトラック 後戻り法ともいう. 木など,枝分かれする対象をすべて探索するのに有用. 再帰呼び出しを使用し,戻る地点の情報をスタックに乗せる. 」が 戻る地点の情報 「

ぷよ連結問題とバックトラック

再帰は効率的? 6×6のフィールドで全部同じ色ぷよのとき,calc_puyoは 合計36回呼び出される. や などのトータル個数     や   などのトータル個数 最大36段の深さ(level)の呼び出しになる. 図で表示される    や   などの最大の個数

関数の仮引数が配列の形に見えても,それはポインタである. 多次元配列を引数とする関数 (1次元)配列を引数とする関数 char a[] = "wakayama"; len = strlen(a); int strlen(const char *s) 多次元配列を引数とする関数 char field[6][6]; color = get_puyo(field, 0, 0); int get_puyo(char field[][6], int x, int y) aは char [9]型の 配列変数 実引数のaは char *型の ポインタ値 仮引数のsは const char *型の ポインタ変数 fieldは char [6][6]型の 配列変数 実引数の fieldは char (*)[6]型 のポインタ値 仮引数のfieldは char [][6]型 (char (*)[6]型)の ポインタ変数 関数の仮引数が配列の形に見えても,それはポインタである.

名前重要(1) 「名前」とは,プログラムで使用できる識別子やラベルのこと. なぜ名前重要? 規格として使用できる文字や重複宣言できる条件が決まっているが,それと別に(読みやすさの観点で)気を配りたい. なぜ名前重要? 他の人が読みやすくなる. 明日の自分も「他の人」. 効率よくプログラムを書ける. タイプ数を減らすために1~2文字の変数ばかりにしてみても,バグが入りやすく,結局効率は悪くなる. sqmatrix color get_puyo calc_determinant is_colored P_RED

名前重要(2) 識別子やラベルの名称には一貫性を持たせる. 略語も適度に取り入れる. 関数 print_sqmatrix, calc_puyo, is_colored gethostbyname, isdigit 変数 ローカル変数は短く 広い範囲で用いられるものは長く,もしくはわかりやすく 略語も適度に取り入れる. まずは適当な名前をつけ,あとでテキストエディタの一括置換機能で修正していくのもよい. 置換すべきでないものを置換しないように 「動詞_名詞(_修飾語)」 が基本

ぷよ連結問題・非再帰版(1) データ構造を改善しておきたい. 構造体で定義する. 「一つの2次元配列」のみでは 得点や連鎖数などが保存できない. 関数の仮引数が長くなってしまう. 構造体で定義する. 構造体のポインタ渡しで値を参照・書き換えしてもらう.

ぷよ連結問題・非再帰版(2) 考え方 それぞれのマスに領域番号をつける. 連結していれば同じ領域番号になるよう,フィールドを何度も調査して値を変えていく. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 3 7 9 10 12 13 22 29 33

ぷよ連結問題・非再帰版(3) 毎回フィールドを全部調査するのは手間がかかる. ⇒最初に領域番号を簡単に設定し,あとで調整する. 2値画像の連結領域解析の手法を利用 領域番号の求め方 3 8 ? 3 8 9 着目するマスの 上と左の色・領域番号 を見て値を決める. 16 22 ? 16 22 20 21 26 ? 20 21 26 21 26 =

ppb->parentregion[26] = 21 領域番号間の関係 各領域番号について,それよりも小さい領域番号と同じ領域にするための情報を,配列で保持する. そのような領域がなければ,値は-1とする. 要素数は? ⇒マスの個数を用意しておく. この配列は,領域番号から領域番号への 写像となる. 21 26 = ppb->parentregion[26] = 21 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 すべてのマスが異なる領域になる例

配列の写像化(1) 簡易設定が終わった ところ 同一視できる 領域番号 領域番号 個数 ppb->region count[ ] ppb->parent region[ ] 1 1 -1 1 2 3 4 5 6 7 8 9 10 11 12 3 2 -1 8 3 -1 1 4 -1 1 5 -1 4 6 -1 6 7 -1 2 8 3 1 9 -1 1 10 7 1 11 8 1 12 -1

配列の写像化(2) 最終状態 同一視できる 領域番号 領域番号 個数 ppb->region count[ ] ppb->parent region[ ] 1 1 -1 1 2 3 4 5 6 7 9 12 3 2 -1 11 3 -1 1 4 -1 1 5 -1 4 6 -1 7 7 -1 8 3 1 9 -1 10 7 11 3 1 12 -1

関数定義の注意点 関数init_fieldは,フィールド構造体を生成し,そのポインタを返す.このオブジェクトは 『「オブジェクト」と「それを指すポインタ」を定義するのは面倒だ.なんとかできないか…』の解答 関数init_fieldは,フィールド構造体を生成し,そのポインタを返す.このオブジェクトは ライブラリ関数mallocを使用して,実行時に生成している. 生成時の値は不定. auto変数の領域(スタック)やstatic領域とは別の(「ヒープ」と呼ばれる)領域で確保される. ブロックを抜けたり関数を終了したりしても破棄されない.プログラム終了時か,ライブラリ関数のfreeを呼び出したときに破棄される. puyoboard オブジェクト ヒープ スタック static領域 ppb

まとめ プログラムを作るときは まずデータ構造(配列,構造体,列挙型など)を検討し, 次に目的ごとに処理(関数)を定める. 再帰呼び出しを使用できるか,使用してよいか検討する. 「一つの問題に一つの解法」ではないことに留意する.

今後の日程 1月18日,25日,2月1日は通常通り授業 2月8日に試験 毎回小テストを実施.出題範囲は,授業で話した内容全部 2月1日は,おさらい問題を実施 2月8日に試験 75点満点 自筆ノート1冊と,C言語の教科書のみ持込可