第5週 関数の再帰呼び出しを利用した応用プログラミング
INDEX

目標

  • 再帰を応用して簡潔なプログラムを記述することができる。

予習・復習

 以下のスライドを利用して、予習と復習をしよう。復習では、自分の理解度を確認するために、実際にプログラムを作成し、意図する結果が得られるか確認しよう。
  1. 再帰処理
  2. 再帰処理の考え方

本日の講義・演習予定

  1. ハノイの塔
  2. 順列
  3. Nクィーン問題
  4. 演習問題
  5. 提出課題
内 容
  1. ハノイの塔
  2. 順列
  3. Nクィーン問題

ハノイの塔

 ハノイの塔とは三本の支柱と複数枚の中央に穴の空いた円盤からなり、ルールに従ってすべての円盤を左端の支柱から右端の支柱に移動するパズルです。円盤はすべて大きさが異なり、最初は小さい円盤が上になるように左端の支柱に収められています。円盤は一度に1枚だけ、残る2本の支柱の何れかに移動することができます。ただし、小さな円盤の上に大きな円盤を重ねることはできません。
 下図の左側は3枚の円盤を移動する手順です。右側は最下段の一枚を除く上段の2枚を一組の円盤として移動するとして考えた手順です。
  • 1)支柱Aの上段2枚一組を支柱Bへ移動
  • 2)支柱Aの最下段の円盤を支柱Cへ移動
  • 3)支柱Bの2枚一組を支柱Cへ移動することで完了します。

上段2枚一組についても実際には1枚ずつ移動しなければなりませんが、図の左側の手順に注目すると、
  • 1)支柱Aの上段1枚を支柱Cへ移動
  • 2)支柱Aの中段の円盤を支柱Bへ移動
  • 3)支柱Cの1枚を支柱Bへ移動
することで、支柱Bへの移動を完了しています。先の説明では移動が支柱Bを経由地として支柱Aから支柱Cへでしたが、ここでは支柱Cを経由地として支柱Aから支柱Bへと移動先と経由地と移動先を交換しただけで、その手順は変わりません。

hanoi
Fig.1 ハノイの塔
このことから、円盤がn枚の時の手順を考えると、
  • 1) 経由地と移動先を交換して、円盤n-1枚を移動する。
  • 2) n番目の円盤を現在位置から移動先へ移動する。(1枚だけなので再帰せず画面表示する)
  • 3) 現在位置と移動先を交換して、円盤n-1枚を移動する。
ただし、1)と3)では円盤組を一枚一枚移動しなければならないため、再帰呼び出しによって移動を実現します。

例 hanoi.c
#include <stdio.h>

void move(int n, char from, char via, char to);

int main(void)
{
	int n;
	
	printf("円盤の枚数> ");
	scanf("%d", &n);

	move(n,'A','B','C');

	return 0;
}

/*
*  n枚の円盤をfromからvia経由でtoへ移動する
*/
void move(int n, char from, char via, char to)
{
	if(n>1)
		move([[空欄ア]]);		//支柱Aから支柱Bへの移動

	printf("円盤%dを、%cから%cへ移動する。\n",n,from,to);

	if(n>1)
		move([[空欄イ]]);		//支柱Bから支柱cへの移動
}
OPEN HINT
hanoi2


順列

 n個からr個を取り出す順列のパターン数はnpr個で求められます。例えば、3個の数字1,2,3から3個取り出す順列のパターンは、[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]の6個になります。 配列を用意して各数字を要素に格納するものとして、順列を表示するプログラムを考えていきます。

すべてのパターンを生成する<重複順列>

 はじめに、無条件に数字の組み合わせパターンを表示するプログラムを考えてみます。先頭の要素を決定したら、次に2つ目の数字を決定し、・・・、と繰り返し数字を決定することで実現します。

再帰呼び出しを使わない繰り返しによる順列パターンの生成
 3個の数字1,2,3から3個取り出す順列パターンに限定した場合のプログラム例です。取り出す数字の個数によってfor文のネストはより深くなっていきます。
例 perm_overlap_norec.c
#include <stdio.h>
int n=3,r=3;    // nPr
int p[3];       //パターンを格納する配列

int main(void)
{
    int i,j,k,l;

    for(i=1; i<=n; i++){
        p[0] = i;               //1つ目の数字
        for(j=1; j<=n; j++){
            p[1] = j;           //2つ目の数字
            for(k=1; k<=n; k++){
                p[2] = k;       //3つ目の数字
                for(l=0; l<r; l++) printf("%d",p[l]);   //画面出力
                printf("\n");
            }
        }
    }

    return 0;
}

再帰呼び出しによる順列パターンの生成
 先のプログラム例のfor文によるネストを再帰呼び出しによって実現した例です。下図に3個の数字から3個取り出す場合を例に順列生成の考え方を図示します。k番目の数字を格納する配列をp[k]として、k番目を再帰呼び出しによって実現し、その数字p[k]はループ処理によって順に決定していきます。
permu_view
Fig.2 再帰呼び出しによる順列生成の考え方

例 perm_overlap.c
#include <stdio.h>
#define MAX 8		//1から最大8までの数字 

void perm(int k);	//プロトタイプ宣言

int p[MAX];	//順列[何番目の文字]
int n,r;	//nPr

int main(void)
{
	printf("異なるn個のものからr個を取ってできる順列nPrを求めます。\n");
	do {
		printf("n>> ");	scanf("%d", &n);
		printf("r>> ");		scanf("%d", &r);
	} while(n<0 || n>=MAX || n<r);

	perm(0);
	return 0;
}

/*
 * 重複順列 
 * @k番目の数字 
 */
void perm(int k)
{
	int i;

	if(k == r){	//最後の数字まで決定したら
		for(i=0; i<r; i++) printf("%d ",p[i]);	//表示
		printf("\n");
		return;
	}
	else {
		for(i=1; i<=n; i++){	//数字 1,2,... ,n
			p[k] = i;	//数字の設定
			perm(k+1);	//隣の数字を決める
		}
	}
}

 上記の再帰関数を使って3個の数字1、2、3から2つの数字を取り出す場合をトレースした結果を下図に示します。 permutation

重複をチェックして順列パターンを表示する

 次に先のプログラムにおいて、重複がない時だけパターンを表示するよう、重複をチェックする機能を関数として追加します。
例 perm_check.c
#include <stdio.h>
#define MAX 16		//1から最大16までの数字 

void perm(int k);	//プロトタイプ宣言

int p[MAX];	//順列[何番目の文字]
int n,r;	//nPr

int main(void)
{
	printf("異なるn個のものからr個を取ってできる順列nPrを求めます。\n");
	do {
		printf("n>> ");	scanf("%d", &n);
		printf("r>> ");	scanf("%d", &r);
	} while(n<0 || n>=MAX || n<r);

	perm(0);
	return 0;
}
/*
* 重複チェック
*/
int check(void)
{
	int i,j;

	for(i=0; i<r-1; i++){
		for(j=i+1; j<r; j++)
			if(p[j]==p[i]) return 0;
	}
	return 1;
}
/*
 * 重複順列 
 * @k番目の数字 
 */
void perm(int k)
{
	int i;

	if(k == r){	//最後の数字まで決定したら
		if(check()){		//重複がなければ
			for(i=0; i<r; i++) printf("%d ",p[i]);	//表示
			printf("\n");
		}
		return;
	}
	else {
		for(i=1; i<=n; i++){	//数字 1,2,... ,n
			p[k] = i;	//数字の設定
			perm(k+1);	//隣の数字を決める
		}
	}
}	

重複パターンは生成を打ち切る

 先のプログラムでは、重複を含む数字のパターンを作成したのちチェックすることで、順列を表示しました。しかし、パターン数が増えると処理に対する負荷が大きくなり、結果を得るため時間がかかってきます。そこで、パターンの作成過程で、同じ数字が出てきた場合にはそのパターンの作成を中止し、次のパターンの生成に進むように改良します。各数字ごとに使用済みかそうでないかを示すためのフラグを配列をとして用意することにします。
例 perm.c
#include <stdio.h>
#define MAX 16 

void perm(int k);

int p[MAX];	//順列[何番目の文字]
int f[MAX];	//使用済数字フラグ[使用済数字] 0:未使用、1:使用済み
int n,r;	//nPr

int main(void)
{
	printf("異なるn個のものからr個を取ってできる順列nPrを求めます。\n");
	do {
		printf("n>> ");	scanf("%d", &n);
		printf("r>> ");	scanf("%d", &r);
	} while(n<0 || n>=MAX || n<r);

	perm(0);
	return 0;
}

/*
 * nPr
 * @k 番目の数字 
 */
void perm(int k)
{
	int i;

	if(k == r){	//揃ったら
		for(i=0; i<r; i++) printf("%d ",p[i]);	//表示
		printf("\n");
		return;
	}
	else {
		for(i=1; i<=n; i++){	//数字 1,2,... ,n
			if(f[i]) continue;		//使用済みなら次の数字を
			p[k] = i;	//数字の設定
			f[i] = 1;	//iを使用済み数字に
			perm(k+1);
			f[i] = 0;	//使用済みを解除
		}
	}
}

組み合わせ

 n個からr個取り出す組みわせの数は、次式で求めることができます。 $$ \ _{n}C_{r} $$  この式を含む組み合わせ数は次式のように書き表すこともできます。 $$ \ _{n}C_{r} = \ _{n-1}C_r + \ _{n-1}C_{r-1}$$ $$ r=0の時\ \ _{n}C_0 = 1 $$ 最初の項$\ _{n-1}C_r$は、特定の数字を選択した時の残りの$n-1$個の数字からr個を選択したときの組み合わせ数を表し、二つ目の項$\ _{n-1}C_{r-1}$は特定の数字を含めたとき、残り$n-1$個から$r-1$個の数字を選択したときの組み合わせ数と考えることができます。
 例えば、1から5の数字の中から3個取り出す組み合わせは、
    [5,4,3],[5,4,2],[5,4,1],[5,3,2],[5,3,1],[5,2,1],
    [4,3,2],[4,3,1],[4,2,1],
    [3,2,1]
の10通りになります。
 特定の数字として5を選択した場合について考えます。5を含まない残りの4,3,2,1の4つの数字から3つの数字を選択する組み合わせは、上記の組み合わせの2、3行目に相当する4パターンがあります。1つ目の数字を5として、残りの2つの数字を残りの数字4,3,2,1の中から2つ選択するパターンは上記の1行目の6パターンがあります。これで合計の組み合わせ数が10になります。
 以下はこの考え方に基づいて再帰呼び出しを使って実現したプログラム例です。
例 comb.c
#include <stdio.h>
#define MAX 16

void comb(int k, int n, int r);

int c[MAX];

int main(void)
{
	int n,r;

	printf("n個からr個を取り出す組み合わせのパターンを求めます\n");
	do{
		printf("n>> ");
		scanf("%d",&n);
		printf("r>> ");
		scanf("%d",&r);
	}while(n>=MAX || r>n);

	comb(0,n,r);

	return 0;
}

/*
 *  組み合わせ
 *	nC0 = 1, nCr =  n-1Cr + n-1Cr-1
 */
void comb(int k, int n, int r)
{
	int i;

	if(r==0){	//組み合わせ数は1
		for(i=0; i<k; i++) printf("%d ",c[i]);
		printf("\n");
	}
	else if(n==r){	//すべての数字
		for(i=r; i>0; i--) c[k++]=i;
		for(i=0; i<k; i++) printf("%d ",c[i]);
		printf("\n");
	}
	else{
		comb(k,n-1,r);			//数字nを除く数字からr個取り出す組み合わせ
		c[k] = n;				//k番目に特定される数字n
		comb(k+1,n-1,r-1);		//数字nを含み、残りのn-1個からr-1個を取り出す組み合わせ
	}
}


Nクィーン問題

8クィーン問題

queen8 8x8のマス目のあるチェス盤に8個のクィーンをどう配置したら互いに一手で取り合うことのないかを考える問題です。クィーンは縦横斜めのいずれかの方向に移動することができます。ただし、他のクイーンを飛び越えて移動することはできません。
 この問題を解く方法の一つは、8x8(=64)個のマス目に8個のクィーンを配置するすべてのパターンを調べてみることです。その組合せ数は
64C8 = 64 x 63 x ... x 2 x 1 = 4426165368 通
りあります。
 次に、1列もしくは1行にはクィーンは一つしか置けないことから、調べるべきパターン数は、
88 = 8 x 8 x ... x 8 x 8 = 16777216 通り
に減らすことができます。また、コードに置き換える場合も、2次元配列ではなく、r列のc行目への配置を1次元配列として、次のように取り扱うことができるようになります。
q[r] = c


equeen.c
#include <stdio.h>
#include 
#define N 8

void printBoard(void);
int check(void);
void setQueen(void);

int q[N];	//q[列]=行
/*
 * 盤面
 */
void printBoard(void)
{
	int i,j;

	for(i=0; i<N; i++) printf("%2d",i);
	printf("\n");
	printf("-+----------------\n");
	for(i=0; i<N; i++){		//行
		printf("%d|",i);
		for(j=0; j<N; j++){	//列
			q[j]==i ? printf(" Q") : printf(" .");
		}
		printf("\n");
	}
	printf("\n");
}

/*
 * 縦横斜めチェック
 */
int check(void)
{
	int i,j;

	for(i=1; i<N; i++){
		for(j=0; j<i; j++){
			  //同一行 もしくは 斜線上
			if(q[i]==q[j] || abs(q[i]-q[j])==abs(j-i))
				return 0;
		}
	}
	return 1;
}
/*
 * クイーンの配置
 * @row 配置される列
 */
void setQueen(void)
{
	int i,j,k,l,m,n,o,p;
	for(i=0; i<N; i++){	//行の移動
		q[0] = i;	//0列目への配置
		for(j=0; j<N; j++){
			q[1] = j;	//1列目への配置
			for(k=0; k<N; k++){
				q[2] = k;
				for(l=0; l<N; l++){
					q[3] = l;
					for(m=0; m<N; m++){
						q[4] = m;
						for(n=0; n<N; n++){
							q[5] = n;
							for(o=0; o<N; o++){
								q[6] = o;
								for(p=0; p<N; p++){
									q[7] = p;
									if(check()) printBoard();
								}
							}
						}
					}
				}
			}
		}
	}
}

int main(void)
{
	setQueen();

	return 0;
}
 同一斜線上に2つのクィーンが配置された場合にはクィーン間の行間数と列間数が等しくなるという関係を利用して、並びのチェックを行っている。

再帰による8クィーン問題の解決

 先のプログラムでは、各列へのクィーンのすべての配置パターンを生成するためにforによる多重ループを利用しました。多重ループは再帰によって書き換えが可能です。
演習1
 次のプログラムは、先の8クィーンの配置パータンを求めるプログラムを再帰呼び出しを使って書き換えたものです。再帰関数setQueen()の空欄を埋めて完成させなさい。また、Nクィーンに対応できるように変更されたい。
equeen_rcv.c
#include <stdio.h>
#include <stdlib.h>
#define N 8

void printBoard(void);
int check(void);
void setQueen(int col);

int q[N];	//q[列]=行

/*
 * 盤面
 */
void printBoard(void)
{
	中略
}

/*
 * 縦横斜めチェック
 */
int check(void)
{
	中略
}
/*
 * クイーンの配置
 * @row 配置される列
 */
void setQueen(int col)
{
	int i;

	if([[空欄ア]]){	//配置終了
		if([[空欄イ]])	printBoard();
	}
	else {
		for(i=0; i<N; i++){	//行
			q[col] = i;	//配置
			setQueen([[空欄エ]]);	//次の列
		}
	}
}

int main(void)
{
	setQueen(0);

	return 0;
}	

演習2  配置パターンの数を表示するようプログラムを追加修正しなさい。
完成例
equeen_rcv.c
#include  <stdio.h>
#include <stdlib.h>
#define N 8 

void printBoard(void);
int check(void);
void setQueen(int col);

int q[N];	//q[列]=行
int count;	//パターン数

/*
 * 盤面
 */
void printBoard(void)
{
	int i,j;

	printf("No.%2d\n",count);
	printf("  ");
	for(i=0; i<N; i++) printf("%2d",i);
	printf("\n");
	printf("-+----------------\n");
	for(i=0; i<N; i++){		//行
		printf("%d|",i);
		for(j=0; j<N; j++){	//列
			q[j]==i ? printf(" Q") : printf(" .");
		}
		printf("\n");
	}
	printf("\n");
}

/*
 * 縦横斜めチェック
 */
int check(void)
{
	int i,j;

	for(i=0; i<N-1; i++){
		for(j=i+1; j<N; j++){
			if(q[i]==q[j] || abs(q[i]-q[j])==abs(j-i))
				return 0;
		}
	}
	return 1;
}
/*
 * クイーンの配置
 * @col 配置される列
 */
void setQueen(int col)
{
	int i;

	if(col==N){	//配置終了
		if(check()==1){
			printBoard();
			count++;
		}
	}
	else {
		for(i=0; i<N; i++){	//行の移動
			q[col] = i;	//配置
			setQueen(col+1);	//次の列
		}
	}	
}

int main(void)
{
	setQueen(0);

	return 0;
}


バックトラック法

 先のプログラムにおいて、Nの値を8ではなく9もしくは10に変更して実行してみてください。8の時には気にならなかった処理速度のことが気になるはずです。下図に示すように、出発点から枝分かれしながら配置パターンを作り出し、N個のクィーンの配置が完了した時点でそれが解なのかどうかをチェックする方法には多くの無駄があります。実際には枝分かれした時点でこれ以上進んでも配置できないことが明らかになる場合もあるからです。この無駄を減らすために、先に進まず後戻りして別のパターンに進むことで解を見つける方法をバックトラック法と言います。
backtrack
 バックトラック法では、配置を完了する前に縦横斜めの配置が可能か判断する必要があります。
equeen_rcv.c
#include <stdio.h>
#include <stdlib.h>
#define N 8

void printBoard(void);
int check(void);
void setQueen(int row);

int q[N];	//q[列数]=行数
int flag1[N],flag2[2*N],flag3[2*N];	//配置済み,縦横、斜め右上、斜め左上
int count;	//パターン数

/*
 * Nクィーン問題(バックトラック)
 */
int main(void)
{
	setQueen(0);

	return 0;
}

/*
 * 配置フラグ
 * @row:行, col:列 state 0:clear, 1:block
 */
void setFlag(int row, int col, int state)
{
	flag1[row]=state;	//縦横
	flag2[col+row]=state;	//斜め右上
	flag3[[[空欄ア]]]=state;	//斜め左上
}

/*
 * クイーンの配置
 * @col 配置される列
 */
void setQueen(int col)
{
	int i;

	if(col==N){	//配置終了
		printBoard();
		count++;
	}
	else{
		for(i=0; i<N; i++){	//行の移動
			if(!flag1[i] && !flag2[col+i] && !flag3[col-i+N-1]){	//配置済み?
				q[col] = i;
				setFlag(i,col,1);	//配置済み
				setQueen([[空欄イ]]);	//次の列
				[[空欄ウ]];	//バックトラック:配置済み解除
			}
		}
	}	
}

/*
 * 盤面表示
 */
void printBoard(void)
{
	int i,j;

	printf("No.%2d\n",count);
	printf("  ");
	for(i=0; i<N; i++) printf("%2d",i);
	printf("\n");
	printf("-+----------------\n");
	for(i=0; i<N; i++){		//行
		printf("%d|",i);
		for(j=0; j<N; j++){	//列
			q[j]==i ? printf(" Q") : printf(" .");
		}
		printf("\n");
	}
	printf("\n");
}
[解答]ア:col-row+N-1、イ:col+1、ウ:setFlag(i,col,0)

配置の可否のチェック
 一つの列には一つのクィーンだけを配置するものとしているので列方向のチェックはしません。行方向のチェックについては、同じ行番号であるかをチェックするだけです。では斜め方向の判定はどうしたらよいでしょう。
 下図に示すように配置されたクィーンの右斜め上の位置は、列番号をi、行番号をjとするとi + jの値はどこでも一定となります。一方、左斜め上の位置ではi - jの値がどこでも一定となります。ただし、i<jの場合にはマイナスになるため、そのままでは配列の添え字には利用できません。そこで、実際にはマス目数を加えたi-j+Nの値が一定であるものとして利用します。 equeen_j

expand_lessBack to TOP

演習問題

  1. 以下の足し算はAlphameticと呼ばれるパズルです。各アルファベットには0から9のいずれかの数字が隠れています。同じ文字には同じ数字が、先頭の文字は0ではありません。各文字に当てはまる数字を再帰呼び出しを使って求めるプログラムを作成しなさい。
  2.   SEND
    + MORE
    --------
     MONEY
    

  3. 次の等式を成立させるために、各空欄に当てはまる数字を再帰呼び出しを使って求めるプログラムを作成しなさい。各空欄には1から9のいずれかの数字が当てはまり、いずれの数字も1度しか利用することはできません。(ヒント:等式の成立をチェックする際、分数を含む式のままでは誤差を含むため正しい検査ができません。) $$ \frac{\Box}{\Box\Box} + \frac{\Box}{\Box\Box} + \frac{\Box}{\Box\Box} = 1 $$
  4. 3x3のマス目に1から9の数字を1つずつ配置して、縦・横・斜めのいずれの数字の合計も同じになるように配置するプログラムを作成しなさい。ただし、再帰呼び出しを使って実現すること。

  5. 1000円札を10円玉、50円玉、100円玉、500円玉硬貨で両替する場合のすべてのパターンを表示するプログラムを再帰呼び出しを使って作成しなさい。ただし、利用できる硬貨の枚数は最大15枚までとし、利用されない硬貨があっても良いものとします。

演習問題解答

OPEN ANSWER
  1. 回答例 ex5-1.c
  2. code/5/alphametic.c
  3. 回答例 ex5-2.c
  4. code/5/warmeat.c
  5. 回答例 ex5-3.c
  6. code/5/magicsquare.c
  7. 回答例 ex5-5.c
  8. code/5/change.c

expand_lessBack to TOP

確認テスト

OPEN ANSWER
1.
ア. printf("%d ", n);  イ. down(n-1);
ア. up(n+1)  イ. printf("%d ", n);

2.
(1)
if(n==1)
	return 1;
else
	return n+(n-1);
							

(2)
if(n==m)
	return n;
else
	return m + (m+1,n);
							


3.
ア.  イ.  ウ.  エ.  オ.  カ.  
(2) 2010012

本日の提出課題

次の各プログラムを作成しなさい。ただし、いずれも再帰関数を定義して利用すること。
  • 課題1
  • 整数aとbを入力して、aからbまでの和Sを求めるプログラム。ただし、整数mとn($n \geq m$)を引数、mからnまでの和を戻り値とする再帰関数sumofを定義して利用すること。
    --- stdin ---
    -3 0
    
    --- stdout ---
    -6
    		

  • 課題2
  • n個の整数があり、この合計を求めるプログラムを作成しなさい。ただし、配列aと、配列aのr番目とl番目までの要素の合計を分割統治法によって求める再帰関数sumof(int a[], int r, int l)を定義して利用すること。n個の整数は予め配列として用意されているものとしてよい。ここでは、a[] = {55, 2, 18, 41, 7, 29, 11,32, 46, 11, 5}を対象とした結果を表示すること。

  • 課題3
  • 正の整数nを入力して、その素因数を表示するプログラム。ただし、正の整数nと素因数の候補となる整数aを引数として、その素因数を表示する再帰関数prime_factorを定義して利用すること。
    --- stdin ---
    90
    
    --- stdout ---
    2,3,3,5
    		

  • 課題4(発展課題)
  • 2つの正の整数mとnを入力して、ユークリッドの互除法を使ってその最大公約数を求めるプログラム。ただし、正の整数mとn($n \geq m$)を引数として、その最大値を戻り値とする再帰関数gcdを定義して利用すること。
    (ヒント)
    ユークリッドの互除法
    (実行例)
    --- stdin ---
    3355,2379
    
    --- stdout ---
    61
    		

  • 課題5(発展課題)
  • 整数xを入力して、n個の整数の中に同じ値があるかを調べるプログラムを作成しなさい。ただし、昇順に並べられた配列aと探索したい整数x、配列aの探索範囲の左端と右端の位置lとrを引数として、見つかった配列の要素の位置を戻り値とする再帰関数search(int a[], int x, int r, int l)を定義して利用すること。見つからなかった場合は−1を戻り値とします。n個の整数は予め配列として用意されているものとしてよい。ここでは、a[] = {3,5,8,11,15,18,22,27,32,38,45,53,57,58,67,71}を対象とした結果を表示すること。

    [ヒント] あらかじめ、昇順に並べられている配列の中央の要素が探しているか値かを繰り返し調べていく処理を再帰呼び出しで実現します。もし、中央の要素の値が探している値よりも大きければ次の探索範囲は左半分になりますし、そうでなければ探索範囲は右半分になります。探索範囲がこれ以上分割できない状態になっても見つからなければ−1を返します。



  • [提出方法]
    1. ideone.comを利用して作成した課題プログラムのURLをCoursePowerへ提出のこと。
    2. 提出するideone.comの課題プログラムには、実行結果が表示されていること。
    3. プログラムの正当性についてどのようにして検証したか簡単に記すこと。
    4. 詳細はスライド「ideoneの使い方」参照のこと。

  • [評価について]
  • プログラムの内容の評価とは別に以下の要件を満たすことを評価の前提とする。
    1. C言語で記述されていること。
    2. ソースコードファイルのコンパイル結果に、ワーニングやエラーが出力されていないこと。
    3. 所定の実行結果が得られるていること。
    4. ソースコードは、インデントや適当な改行が施された見やすい状態であること。

  • [提出期限]
  • 2023年 7月17日(月)までとする。ただし、以降の提出も受け付ける。