プログラミング論 I 2008年07月03日 2008年07月10日 2008年7月11日 関数,再帰

Slides:



Advertisements
Similar presentations
オブジェクト指向言語・ オブジェクト指向言語演習 中間試験回答例. Jan. 12, 2005 情報処理技術基礎演習 II 2 オブジェクト指向言語 中間試験解説 1  (1) 円柱の体積(円柱の体積 = 底面の円の面積 x 高さ) を求めるプログラムを作成しなさい。ただし、出力結果は、入 力した底面の円の半径.
Advertisements

情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
第13回構造体.
第12回構造体.
プログラミング論 I 行列の演算
基礎プログラミングおよび演習 第9回
プログラミング論 I 講義,テスト C言語復習
プログラミング演習(2組) 第12回
プログラミング論 I 関数の再帰呼び出し
問題 1 フィボナッチ数列 xn は次で定義される。
第6章 2重ループ&配列 2重ループと配列をやります.
構造体.
プログラミング論 II 電卓,逆ポーランド記法電卓
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
関数 関数とスタック.
精密工学科プログラミング基礎Ⅱ 第3回資料 今回の授業で習得してほしいこと: 2次元配列の使い方 (前回の1次元配列の復習もします.)
第7回 条件による繰り返し.
プログラミング論 関数ポインタ と 応用(qsort)
ちょっとした練習問題① 配列iroを['R', 'W', 'R', 'R', 'W' , 'W' , 'W']を宣言して、「W」のときの配列の番号をprintfで表示するようなプログラムを記述しなさい。
プログラミング2 関数
プログラミング論 I 2008年5月22日 講義概要 C言語復習
関数と配列とポインタ 1次元配列 2次元配列 配列を使って結果を返す 演習問題
関数の定義.
第10回関数 Ⅱ (ローカル変数とスコープ).
プログラミング論 II 2008年10月30日 文字列
プログラミング2 関数の再帰呼び出し
今までの練習問題の復習.
前回の練習問題.
indentについて forやifの「中身」を右に寄せる. forやifの「外枠」は右に寄せない. int x; x = 3;
第7回 条件による繰り返し.
第7回 プログラミングⅡ 第7回
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
地域情報学 C言語プログラミング 第5回 ポインタ、関数、ファイル入出力 2017年11月17日
関数の再帰呼び出しとは ハノイの塔 リダイレクト レポート課題
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
演習0 func0, func1, func2を作成せよ. main()関数の中で,func0()を呼び出しを実行せよ.
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
演習07-0 “Hello\n” “World!\n”と
C言語 はじめに 2016年 吉田研究室.
プログラミング論 I 2008年07月03日 関数,再帰
IF文 START もしも宝くじが当たったら 就職活動する 就職活動しない YES END NO.
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
復習 Cにおけるループからの脱出と制御 break ループを強制終了する.if文と組み合わせて利用するのが一般的. continue
Indent.
情報基礎演習B 後半第2回 担当 岩村 TA 谷本君.
プログラミング論 ポインタ
C言語,ソースファイルの作成,コンパイル,実行
C言語復習 来週もこの資料を持参してください.
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
第5回 プログラミングⅡ 第5回
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
ループだよ!難しいよ! 第5章 while(ループ);.
演習00-0 “Hello\n” “World!\n”と
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
プログラミング2 関数の再帰呼び出し
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
高度プログラミング演習 (07).
関数と再帰 教科書13章 電子1(木曜クラス) 2005/06/22(Thu.).
プログラミング演習I 2003年6月11日(第9回) 木村巌.
第10回 関数と再帰.
オブジェクト指向言語論 第三回 知能情報学部 新田直也.
プログラミング入門2 第5回 配列 変数宣言、初期化について
情報処理Ⅱ 小テスト 2005年2月1日(火).
計算技術研究会 第5回 C言語勉強会 関数(function)を使う
プログラミング演習I 補講用課題
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
第1章 文字の表示と計算 printfと演算子をやります 第1章 文字の表示と計算.
第1章 文字の表示と計算 printfと演算子をやります.
Presentation transcript:

プログラミング論 I 2008年07月03日 2008年07月10日 2008年7月11日 関数,再帰 http://www.ns.kogakuin.ac.jp/~ct13140/Prog.2008/

FAQ: #defineとは? 文字列を定義して,それ以降置換する. 置換 置換 置換 #define MAX 10 void main(){ int i=MAX; printf("%d\n", i); } void main(){ int i=10; printf("%d\n", i); } 置換 10/2*5は, 25です. 左から順に 計算 #define MAX 2*5 void main(){ int i=10/MAX; printf("%d\n", i); } void main(){ int i=10/2*5; printf("%d\n", i); } 置換 #define MAX (2*5) void main(){ int i=10/MAX; printf("%d\n", i); } void main(){ int i=10/(2*5); printf("%d\n", i); } 置換

インデントとフリーフォーマット 対応のわかりやすい ソースコードを記述 すること!!!!! プログラム内の 空白,TAB文字,改行は #include <stdio.h> void main(){ int i; for(i=0; i<5; i++){ printf(”i=%d\n”, i); } プログラム内の 空白,TAB文字,改行は 無視される. よって,どのように 書いても問題ない. 当然,見やすく書くことが 好ましい. #include <stdio.h> void main(){ int i; for(i=0; i<5; i++){ printf(”i=%d\n”, i); } #include <stdio.h> void main(){ int i; for(i=0; i<5; i++){ printf(”i=%d\n”, i); } 対応のわかりやすい ソースコードを記述 すること!!!!! ↑対応関係がわかりづらい. ↑対応関係が誤り. #include <stdio.h> void main(){ int i; for(i=0; i<5; i++){ printf(”i=%d\n”, i); }} ↑理解が困難.対応関係が分かりづらい.

見やすい例 #include <stdio.h> void main(){ int i, j; for(i=0; i<10; i++){ for(j=0; j<10; j++){ if( (i + j) % 10 == 0 ){ printf("***\n"); } else { printf("%d %d\n", i, j); }

見づらい例 これは,私(山口)には 理解できません. #include <stdio.h> void main(){ int i, j; for(i=0; i<10; i++){ for(j=0; j<10; j++){ if( (i + j) % 10 == 0 ){ printf("***\n"); } else { printf("%d %d\n", i, j); } これは,私(山口)には 理解できません. 詳しくは,下記を参照. http://www.ns.kogakuin.ac.jp/~ct13140/Prog.2008/ProgEn1/etc.ppt プログラミング演習のページから「その他,C言語注意事項」のリンクでたどれる.

概要 関数の作り方を学ぶ 関数を再帰的に使用する 結構難しいです.

関数

関数の概要 1/3 関数とは,“複数の処理をひとまとめにしたもの”. 関数は“引数”を受け取り,“戻り値”を返すことができる. 同じ処理/似た処理を 何度も記述するような場合に便利. 関数は“引数”を受け取り,“戻り値”を返すことができる. 実は,printf() も main() も関数.

関数の概要 2/3 この3行まとめて, 1個のグループに したい. 実行結果 1 #include <stdio.h> 2 void main(){ 3 printf("Hello,"); 4 printf("World"); 5 printf("!!!!\n"); 6 printf("------------\n"); 7 printf("Hello,"); 8 printf("World"); 9 printf("!!!!\n"); 10 printf("############\n"); 11 printf("Hello,"); 12 printf("World"); 13 printf("!!!!\n"); 14 } この3行まとめて, 1個のグループに したい. Hello,World!!!! ------------ ############ 実行結果

関数の概要 3/3 正しくは,グループ ではなく“関数”と呼ぶ 関数を“呼び出す”と言う. この3行が1個の グループになった. 1 #include <stdio.h> 2 3 void printHW(){ 4 printf("Hello,"); 5 printf("World"); 6 printf("!!!!\n"); 7 } 8 9 void main(){ 10 printHW(); 11 printf("------------\n"); 12 printHW(); 13 printf("############\n"); 14 printHW(); 15 } この3行が1個の グループになった. グループ名は printHW printHW();で, printHWグループを 一括して実行できる. プログラムはmain()から始まる. 正しくは,グループ ではなく“関数”と呼ぶ 関数を“呼び出す”と言う.

関数の動作 1/2 3 void printHW(){ 4 printf("Hello,"); ④ 5 printf("World"); 6 printf("!!!!\n"); 7 } ④ ① ⑤ 9 void main(){ 10 printHW(); 11 printf("------------\n"); 12 printHW(); 13 printf("############\n"); 14 printHW(); 15 } ③ ② ⑥

関数の動作 2/2 3 void printHW(){ 4 printf("Hello,"); ⑨ 5 printf("World"); 6 printf("!!!!\n"); 7 } ⑨ ⑩ 9 void main(){ 10 printHW(); 11 printf("------------\n"); 12 printHW(); 13 printf("############\n"); 14 printHW(); 15 } ⑦ ⑧ ⑪

関数の作り方 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); 戻り値(後述) この例では, “void”である. 関数名(後述) この例では, “printHW”である. 引数(後述) この例では, 空である. 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); 6 printf("!!!!\n"); 7 } 関数の中身 main() の書き方と同じである.main() も関数である.

関数の作り方 (例) 関数名に数字も使用可. ただし,数字から 始まる関数名はNG. 例:“10pHW”はNG 関数の中で #include <stdio.h> void printHW(){ printf("Hello,"); printf("World"); printf("!!!!\n"); } void pHW10(){ int i; for(i=0; i<10; i++){ printf("Hello,World!\n"); void main(){ printHW(); pHW10(); 関数名に数字も使用可. ただし,数字から 始まる関数名はNG. 例:“10pHW”はNG 関数の中で 変数を使うことも可能. この変数はpHW10の 中でしか使えない. これはローカル変数. (後述)

関数の作り方 (例) 実行結果 この1行で, sankaku() の 全てが一括して 実行される. 1 #include <stdio.h> 2 void sankaku(){ 3 printf("*\n"); 4 printf("**\n"); 5 printf("***\n"); 6 printf("****\n"); 7 } 8 void gyaku_sankaku(){ 9 printf("****\n"); 10 printf("***\n"); 11 printf("**\n"); 12 printf("*\n"); 13 } 14 void main(){ 15 sankaku(); 16 gyaku_sankaku(); 17 sankaku(); 18 sankaku(); 19 gyaku_sankaku(); 20 } 関数の作り方 (例) * ** *** **** 実行結果 この1行で, sankaku() の 全てが一括して 実行される.

練習 G-0 printf("Hello,\n"); printf("World!\n"); を行う関数を作成し, それをmain関数から呼び出すプログラムを記述せよ. 関数名はpr_hl_wld()とせよ.

解答 G-0 #include <stdio.h> void pr_hl_wld(){ printf("Hello,\n"); printf("World!\n"); } void main(){ pr_hl_wld();

関数の作り方 (引数) 引数とは,関数に与える情報. 関数に情報を与えることが可能. 関数では,与えられた情報を使用して処理することが可能. “情報”とは,int型の値,double型の値などである. 引数は,何個でも用意できる.

関数の作り方 (引数) 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); この部分に,引数について記述する. この例では引数は なし(空,void). 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); 6 printf("!!!!\n"); 7 }

関数の作り方 (引数) 引数を受け取る関数. void print_int (int i){ printf("i=%d\n", i); } 戻り値(後述) この例では, “void”である. 引数. この例では,int型変数1個. それを i とする. 厳密には仮引数と言う. 関数名. この例では, “print_int”. void print_int (int i){ printf("i=%d\n", i); }

関数の作り方 (引数) 関数に引数を渡す. void main(){ print_int(3); } 与えて関数呼び出し. void main(){ print_int(3); } void print_int (int i){ printf("i=%d\n", i); } 変数 i が宣言 されており, i を使用可能. i = 3 として関数が 始まる. 実行結果 i=3

関数の作り方 (引数) 呼び出される関数と,呼出し処理をまとめると, #include <stdio.h> print_int()を 呼び出すには, 呼び出すよりも前に print_int()が 宣言(記述)されて いなくてはならない. (あるいは,宣言されている ことが推奨される. C言語では推奨. C++では必須.) ただし,プロトタイプ宣言を 行えばこの限りでない. #include <stdio.h> void print_int (int i){ printf("i=%d\n", i); } void main(){ print_int(3); プログラムは,必ずmain() から始まる.

関数の作り方 (引数の例) 実行結果 Hello,World!!!! Hello,World!!!!!!! for文を使う例 #include <stdio.h> void printHW_n(int n){ int i; printf("Hello,World"); for(i=0; i<n; i++){ printf("!"); } /* n個の ! を表示 */ printf("\n"); } void main(){ printHW_n(4); printHW_n(7); for文を使う例 実行結果 Hello,World!!!! Hello,World!!!!!!!

関数の作り方 (引数の例) 実行結果 3 + 7 = 10 引数2個の例 引数2個の例. ,(カンマ)で区切る #include <stdio.h> void print_sum(int a, int b){ printf("%d + %d = %d\n", a, b, (a+b)); } void main(){ int x=3, y=7; print_sum(x,y); 実引数に, 変数を使った例. 実行結果 3 + 7 = 10

関数の作り方 (引数の例) 実行結果 ******* #include <stdio.h> void print_nchar(char c, int n){ int i=0; for(i=0; i<n; i++){ printf("%c", c); } printf("\n"); void main(){ char ch = '*'; int x = 7; print_nchar(ch,x); char型とint型を 受け取った例. char型は「文字」型. 文字1個を表す. 引数は,char型と,int型なので, char型の値と,int型の値を入れて 呼び出す 実行結果 *******

関数の作り方 (戻り値) 戻り値とは,「関数が呼び出しもとに返す情報」. 関数から,関数の呼び出しもとに情報を返すことが可能. 戻り値は,関数の終了時に返される. “情報”とは,int型の値,double型の値などである. 戻り値は,最大でも1個. void なら,0個.

関数の作り方 (戻り値) 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); この部分に,戻り値について記述する. この例では引数は なし(空,void). 3 void printHW (){ 4 printf("Hello,"); 5 printf("World"); 6 printf("!!!!\n"); 7 }

関数の作り方 (戻り値) 戻り値を返す関数. 戻り値. この例では, “int”である. 関数名. この例では, “square”. 引数. int square(int n){ int sq; sq = n*n; printf("%d^2 = %d\n", n, sq); return sq; }

関数の作り方 (戻り値) 戻り値を返す関数. ②i=5 int square(int n){ int sq; sq = n*n; printf("%d^2 = %d\n", n, sq); return sq; } ③sq=25 ① 引数 5 ④ 戻り値 25 戻り値が"25"なので, square(5) が 25となる. よって, s = 25; void main(){ int s; s = square(5); printf("square=%d\n", s); }

関数の作り方 (戻り値の例) 実行結果 5^2 = 25 square=25 1 #include <stdio.h> 2 int square(int n){ 3 int sq; 4 sq = n*n; 5 printf("%d^2 = %d\n", n, sq); 6 return sq; 7 } 8 9 void main(){ 10 int s; 11 s = square(5); 12 printf("square=%d\n", s); 13 } 実行結果 5^2 = 25 square=25

関数の作り方 (戻り値の例) 実行結果 1 #include <stdio.h> 2 double double_square(double d){ 3 double sq; 4 sq = d*d; 5 printf("%lf^2 = %lf\n", d, sq); 6 return sq; 7 } 8 9 void main(){ 10 double s; 11 s = double_square(1.5); 12 printf("square=%lf\n", s); 13 } 引数がdouble型1個なら, double型の値1個を 入れて,呼び出す. 実行結果 1.500000^2 = 2.250000 square=2.250000

関数の作り方 (戻り値の例) 実行結果 1 #include <stdio.h> 2 double double_square(double d){ 3 double sq; 4 sq = d*d; 5 printf("%lf^2 = %lf\n", d, sq); 6 return sq; 7 } 8 9 void main(){ 10 double s; 11 s = double_square(1.5); 12 printf("square=%lf\n", s); 13 } 戻り値がdouble型なら, double型の値を return する. 実行結果 1.500000^2 = 2.250000 square=2.250000

関数の作り方 (戻り値の例) 実行結果 i=1, j=2, k=3 1 #include <stdio.h> 2 int one(){ 3 return 1; 4 } 5 int two(){ 6 return 2; 7 } 8 int three(){ 9 return 3; 10 } 11 void main(){ 12 int i, j, k; 13 i = one(); 14 j = two(); 15 k = three(); 16 printf("i=%d, j=%d, k=%d\n", i, j, k); 17 } 実行結果 i=1, j=2, k=3

関数の作り方 (戻り値の例) 実行結果 i=1, j=2, k=3 printf()の中に直接記述してもOK. 1 #include <stdio.h> 2 int one(){ 3 return 1; 4 } 5 int two(){ 6 return 2; 7 } 8 int three(){ 9 return 3; 10 } 11 void main(){ 12 printf("i=%d, j=%d, k=%d\n", one(), two(), three()); 13 } 実行結果 i=1, j=2, k=3 printf()の中に直接記述してもOK. one() が"1",two()が"2"と思えば良い.

関数の作り方 (まとめ) 戻り値の型 関数名(引数の型 変数名,…){ 関数の中身... (変数の宣言なども可能) return ○; (←戻り値がvoidなら,省略可) }

関数:戻り値を捨てる double d_sq(double d){ double sq = d*d; printf("%lf^2 = %lf\n", d, sq); return sq; } void main(){ double x; x = d_sq(1.2); d_sq(3.4); 実行結果 1.200000^2 = 1.440000 3.400000^2 = 11.560000 戻り値を受け止めて,xに代入. 戻り値を受け止めずに, 捨てる.特に問題ない. 戻り値情報は失われるが, 関数は正しく実行される.

関数:複数のreturn 関数内に return が 何個あってもよい. 実行結果 x=3 |x|=3 x=-4 |x|=4 1 #include <stdio.h> 2 int zettaichi(int n){ 3 if( n<0 ){ 4 return (-n); 5 } else { 6 return n; 7 } 8 } 9 void main(){ 10 int x, z; 11 x = 3; 12 z = zettaichi(x); 13 printf("x=%d |x|=%d\n", x, z); 14 x = -4; 15 z = zettaichi(x); 16 printf("x=%d |x|=%d\n", x, z); 17 } 関数内に return が 何個あってもよい. 実行結果 x=3 |x|=3 x=-4 |x|=4

関数:return return により, 関数は強制的に 終了する. 実行結果 1 #include <stdio.h> 2 3 void funct(){ 4 printf("AAAA\n"); 5 printf("BBBB\n"); 6 return; 7 printf("CCCC\n"); 8 } 9 10 void main(){ 11 funct(); 12 } 実行結果 AAAA BBBB 戻り値が無いときは, 単に return; と記述

関数:ローカル変数 1 void funct(){ 2 int x; 3 x = 3; 4 abcd = 4; 5 } ブロックの外で 使おうとしている. これは,NG. コンパイルエラーとなる. 1 void funct(){ 2 int x; 3 x = 3; 4 abcd = 4; 5 } 6 void main(){ 7 int abcd; 8 abcd = 5; 9 funct(); 10 } int abcd は, このブロック内で 宣言されているため, このブロックの中でしか 使用できない. ブロック内の変数を ローカル変数という. ブロックの範囲

関数:仮引数と実引数 実行結果 i=5 mainのiと,func0のxは 別の変数. mainのiの値が,func0のxに 代入(コピー)される. func0のxを変更しても, mainのiには影響なし. 1 void func0(int x){ 2 x = 3; 3 } 4 void func1(int i){ 5 i = 3; 6 } 7 void main(){ 8 int i = 5; 9 printf("i=%d\n", i); 10 func0(i); 11 printf("i=%d\n", i); 12 func1(i); 13 printf("i=%d\n", i); 14 } xが仮引数 mainのiと,func1のiは 別の変数. 名前が同じ別の変数. mainのiの値が,func1のiに 代入(コピー)される. func1のiを変更しても, mainのiには影響なし. iが実引数 i=5 実行結果

関数の多段呼び出し 1/3 実行結果 main() func0() func0() func0 start! func0 end! 1 void func0(){ 2 printf("func0 start!\n"); 3 printf("func0 end!\n"); 4 } 10 void main(){ 11 func0(); 12 } main() func0() 実行結果 func0() func0 start! func0 end!

関数の多段呼び出し 2/3 実行結果 1 void func0(){ 2 printf("func0 start!\n"); 3 printf("func0 end!\n"); 4 } 5 void func1(){ 6 printf("func1 start!\n"); 7 func0(); 8 printf("func1 end!\n"); 9 } 10 void main(){ 11 func1(); 12 } 実行結果 func1 start! func0 start! func0 end! func1 end!

関数の多段呼び出し 3/3 func0() func0() func1() func1() main()

関数の再帰呼び出し ある関数から,自分自身を呼び出すことを再帰呼び出しという. 例えば,関数funct() の中で,funct() を呼び出すなど. 使い方を間違えると,永遠に終了しないプログラムになりやすいので注意.

関数の再帰呼び出し 1 void pr_hl(){ 2 printf("Hello!start\n"); 3 pr_hl(); 4 printf("Hello!end\n"); 5 } 6 void main(){ 7 pr_hl(); 8 } 動作の解説は次ページ 注意!これは失敗例です.

呼び出しを永遠に続け, このプログラムは 終わらない. main() pr_hl()が 呼ばれたので, pr_hl()に移動.

再帰:nの階乗 1/5 nの階乗を,n!と記述する. n! は,n×(n-1)×(n-2)×…×2×1 n! は,n × (n-1)! 5! は,5×4×3×2×1 5! は,5×4!

再帰:nの階乗 2/5 nの階乗は もし n == 1 なら, 答えは,1 もし n > 1 なら, 答えは,n×(n-1)!

再帰:nの階乗 3/5 1 int kaijoh(int x){ 2 int r; 3 if( x == 1 ){ 4 return 1; 5 } else { 6 r = x * kaijoh(x-1); 7 return r; 8 } 9 } 10 void main(){ 11 int n, k; 12 n = 5; 13 k = kaijoh( n ); 14 printf("%d! = %d\n", n, k); 15 }

再帰:nの階乗 4/5 main() kaijoh(3) kaijoh(3)は,3*kaijoh(2) kaijoh(2) これ以上再帰呼び出ししない. これがないと 無限に続く kaijoh(2)=2 kaijoh(3)は,3*2 kaijoh(3)=6

再帰:nの階乗 5/5 kaijoh( 4 ) =4* kaijoh( 3 ) =4* (3* kaijoh( 2 ) ) =4* (3* (2* 1 ) ) =4* (3* ( 2 ) ) =4* ( 6 ) =24

失敗例 1 int kaijoh(int x){ 2 int r; 3 r = x * kaijoh(x-1); 4 return r; ↓ kaijoh(4)を呼び出し kaijoh(3)を呼び出し kaijoh(2)を呼び出し kaijoh(1)を呼び出し kaijoh(0)を呼び出し kaijoh(-1)を呼び出し kaijoh(-2)を呼び出し kaijoh(-3)を呼び出し 無限に続く 1 int kaijoh(int x){ 2 int r; 3 r = x * kaijoh(x-1); 4 return r; 5 } 6 void main(){ 7 int n, k; 8 n = 5; 9 k = kaijoh( n ); 10 printf("%d! = %d\n", n, k); 11 }

再帰:組み合わせ nCr 組み合わせ(Conbination) nCr は, r>1なら, nCr=n-1Cr-1 * n / r

練習 G-1 3次元配列の int x[3][4][5]; を全て表示するプログラムを作成せよ.

解答 G-1 int i, j, k; for(i=0; i<3; i++){ for(j=0; j<4; j++){ for(k=0; k<5; k++){ printf("%d\n", x[i][j][k]); } x[i][j][k]の全ての場合を 網羅して printf を行えば良い. i=0~2 j=0~3 k=0~4

再帰:ハノイの塔 ハノイの塔というパズル 引用 奥野かるた店より http://www.okunokaruta.com/goods/goods12e10.jpg

再帰:ハノイの塔 ルール 円盤置き場が3カ所ある. 円盤がn枚あり,全て大きさが異なる. 小さい円盤の上に大きな円盤は置けない. 通常,円盤の中心に穴があり, 円盤置き場には杭が立っている. 円盤がn枚あり,全て大きさが異なる. 小さい円盤の上に大きな円盤は置けない. 円盤置き場の1番上の1枚を別の円盤置き場に移動することができる.

再帰:ハノイの塔 目的 解法 n枚の円盤を,ある円盤置き場から別の円盤置き場に移動する. できるだけ少ない円盤移動が好ましい. 最適解は, 円盤がn枚の場合,2n-1回の円盤移動. プログラミングでは,再帰的手法で解ける.

ハノイの塔:2枚の例 円盤 1 2 円盤置き場 ↓開始!(円盤2枚を左から中央に) 1 2 1回目の移動. 円盤1を左から右に. 2 1

ハノイの塔:2枚の例 2 1 2回目の移動. 円盤2を左から中央に. 2 1 2 1 3回目の移動. 円盤1を右から中央に. 22-1=3回で終了. 1 2 完了!

ハノイの塔:3枚の例 1 2 1回目の移動. 円盤1を左から中央に. 3 2 2回目の移動. 円盤2を左から右に. 円盤3の上が空に. 3 3回目の移動. 円盤1を中央から右に. 中央の円盤置き場が 空いた.円盤3を 中央に移動可能. 1 3 2

ハノイの塔:3枚の例 4回目の移動. 円盤3を左から中央に. ついに円盤3が移動! 1 3 2 1 5回目の移動. 円盤1を左から右に. 3 6回目の移動. 円盤2を右から中央に. 1 3 2 2 7回目の移動. 円盤1を左から中央に. 1 3 1 2 3

ハノイの塔:4枚の例 24-1=15回の移動. 円盤3枚の移動方法(7回)は分かっている... ↓ 円盤3枚の移動方法を使えば, 円盤4枚の移動方法を導き出せる.

ハノイの塔:4枚の例 合計 7+1+7=15回 1 先ほどの方法で, 円盤3枚(1~3)を 左から右に移動. 7回の円盤移動. 2 3 4 円盤4を左から中央に. 1回の円盤移動. 4 3 1 先ほどの方法で, 円盤3枚(1~3)を 右から中央に移動. 7回の円盤移動. 2 4 3 1 2 3 合計 7+1+7=15回 4

ハノイの塔:n枚 n枚の移動方法が分かれば, それを用いて n+1枚の移動方法が分かる. 1枚の移動方法は分かる. ↓ 任意の枚数の移動方法が分かる

ハノイの塔:n枚 合計 (2n-1-1)+1+(2n-1-1)=2n-1回 1 : n-1枚を左から右に 移動する. 移動回数1回. : n n-1 1 : n-1枚を右から中央に 移動する. 移動回数2n-1-1回. n n-1 1 : n-1 合計 (2n-1-1)+1+(2n-1-1)=2n-1回 n

ハノイの塔を解く関数 ハノイの塔の移動(移動枚数,移動元の円盤置場,移動先の円盤置場,待避円盤置場) もし,移動枚数が1なら, 1枚を「移動元」から「移動先」に移動する. もし,移動枚数が1より大きければ, (n-1)枚を「移動元」から「待避」に移動. n枚目を「移動元」から「移動先」に移動. (n-1)枚を「待避」から「移動先」に移動.

ハノイの塔を解く関数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> void hanoi(int n, int plFrom, int plTo, int plOther){ if( n == 1 ){ printf("move disk.%d ", n); printf("[%d -> %d]\n", plFrom, plTo); } else { hanoi(n-1, plFrom, plOther, plTo); hanoi(n-1, plOther, plTo, plFrom); } void main(){ hanoi(5, 0, 1, 2);

フィボナッチ数列 以下の数列がフィボナッチ数列 1,1,2,3,5,8,13,21,34,… an+an+1=an+2 となる. ただし,a0=1,a1=1 fibo(n)=fibo(n-1)+fibo(n-2) という再帰的な発想で記述可能. ただし,for文で加算した方が短時間で計算可能

フィボナッチ数列 fibo(5) fibo(4) fibo(3) fibo(1) =1 fibo(2) fibo(3) fibo(2) 同じ処理を何度も 行っている. 大変に非効率的 fibo(0) =1 fibo(1) =1 fibo(0) =1 fibo(1) =1

計算量/計算時間 n枚のハノイの塔 n個の数字をバブルソートで並び替える. n個の数字をクイックソートで並び替える n×log(n) に比例する計算量/時間 n個の数字をバケツソートで並び替える n に比例する計算量/時間

練習 G-2 fibo(n)=fibo(n-1)+fibo(n-2) という 再帰的な考え方で,

解答 G-2 #include <stdio.h> int fibo(int n){ if( n <= 1 ){ return 1; } else { return fibo(n-1)+fibo(n-2); } void main(){ printf("%d\n", fibo(7)); これは例