• 検索結果がありません。

赤白黄緑黒学校塾公園電車バス名前忘れた場所

N/A
N/A
Protected

Academic year: 2021

シェア "赤白黄緑黒学校塾公園電車バス名前忘れた場所"

Copied!
34
0
0

読み込み中.... (全文を見る)

全文

(1)

プログラミング言語 I 第 12 回 パズル 1

埼玉大学工学部 電気電子システム工学科 伊藤 和人

(2)

問題

„

ある雨の日、こういち君たち

5

人は自分の 傘をどこかに置き忘れてしまいました。傘 の色と忘れた場所は全員違います。

5

人の 話からそれぞれの傘の色と置き忘れた場 所を推理してください。

„

傘の色

:

白、赤、黄、緑、黒

„

場所

:

学校、塾、公園、電車、バス

出典

:

朝日新聞

(3)

5 人の話

„

こういち

:

ボクは白い傘を忘れたんだけど、場所は 塾ではないよ。

„

あさこ

:

バスに傘を置き忘れちゃった。黒い傘は私 のじゃないよ。

„

ひろこ

:

私の傘は緑色なんだけど、電車には置き忘 れていないの。

„

しょうご

:

黒い傘を学校に置き忘れた人はいないよ。

„

しんぞう

:

黄色い傘は公園に置き忘れていたんだっ て。その傘はボクのではないよ。ボクは電車に乗っ ていないから、電車に置き忘れることもないよ。

出典

:

朝日新聞

(4)

解き方のヒント

„

表を使って解く

„

正しいところに○

„

ありえなければ×

こういちあさこ しょうごひろこ しんぞう学校 公園 電車バス

黄 緑 黒 学校 公園 電車 バス

名前忘れた場所

傘の色 忘れた場所

(5)

こういちあさこ しょうごひろこ しんぞう学校 公園 電車バス

学校 公園 電車 バス

○ ×

× ○

○ ×

× ×

×

名前忘れた場所

忘れた場所

黄 緑 黒

傘の色

与えられた条件を表に記入

„

条件に従い、

○または ×を記入

こういち

:

ボクは白い傘を 忘れたんだけど、

場所は塾ではないよ。

(6)

表の最終形

„

例えば名前と傘の色について、

1

1

に対 応するから、各行、各列に○は

1

つ、他は 全て×

„

名前と場所、場所と色についても同様

名前と傘の色の 対応の例

こういちあさこ しょうごひろこ しんぞう

○××××

×○×××

×××○×

××○××

××××○

名前 黄 緑 黒傘の色

各行、列について

○は

1

つだけ

(7)

プログラムで解くための考え方

„

答えの組み合わせは何通り

?

„

名前と置き忘れた場所の組み合わせ

⇒ 5

の順列

⇒ 5!=120

通り

„

名前と傘の色の組み合わせ

⇒ 5

の順列

⇒ 5!=120

通り

„

組み合わせは

120 × 120=14400

通りだけ

„

全ての組み合わせを列挙し、条件に矛盾 しないものを選べばよい

(8)

全体のアルゴリズム

名前と場所の組み合わせ

(120

通り

)

を順に 発生

名前と色の組み合わせ

(120

通り

)

を順 に発生

名前・場所・色の組み合わせが条 件を満たすか

Yes ⇒

終了

(9)

プログラム化 ( その 1)

„

名前、場所、色を番号

(

整数

)

で区別する

„

名前

0:

こういち、

1:

あさこ、

2:

ひろこ、

3:

しょうご、

4:

しんぞう

„

置き忘れた場所

0:

学校、

1:

塾、

2:

公園、

3:

電車、

4:

バス

„

傘の色

0:

白、

1:

赤、

2:

黄、

3:

緑、

4:

(10)

プログラム化 ( その 2)

„

名前と場所の対応

配列

B

を使用

名前

0:

こういち、

1:

あさこ、

2:

ひろこ、

3:

しょうご、

4:

しんぞう 置き忘れた場所

0:

学校、

1:

塾、

2:

公園、

3:

電車、

4:

バス

B[0]=0

B[1]=4 B[2]=1 B[3]=3 B[4]=2

こういち

:

学校 あさこ

:

バス ひろこ

:

しょうご

:

電車 しんぞう

:

公園

配列インデクス が名前、値が 場所に対応

(11)

プログラム化 ( その 3)

„

名前と色の対応

配列

C

を使用

名前

0:

こういち、

1:

あさこ、

2:

ひろこ、

3:

しょうご、

4:

しんぞう 傘の色

0:

白、

1:

赤、

2:

黄、

3:

緑、

4:

C[0]=0 C[1]=2 C[2]=3 C[3]=4 C[4]=1

こういち

:

あさこ

:

ひろこ

:

しょうご

:

しんぞう

:

配列インデクス が名前、値が 色に対応

(12)

int B[5], C[5];

int n, n0;

n = 0;

B[n] = -1;

while( 1 ){

B[n]++;

if( B[n] >= 5 ){

if( -- n < 0 ) break;

}else{ /* B[n]

が割り当て済みの値でないか

*/

for( n0 = 0 ; n0 < n ; n0++ ){

if( B[n] == B[n0] ) break;

} if( n0 < n ) continue; /* B[n]

は割り当てできない

*/

if( n == 4 ){ /* B

割り当て完了

*/

if( EnumerateC() ) break;

}else{

B[++n] = -1;

} } }

A-C

間組み合わせを列挙

A-B

間組み合わせ列挙

以下の

B[n]++

と組み合わせて

B[n]=0

から開始するようにする

(13)

A-B( 名前 - 場所 ) の組み合わせ 1

1 4 0

配列

B 0 1 2 3 4

ここ

(n=3)

に入れる値を決める

候補

B[3]=0

B[2]=0

で既に使用済み

⇒ n0=2 < n ⇒×

候補

B[3]=1

B[0]=1

で既に使用済み

⇒ n0=0 < n ⇒ ×

候補

B[3]=2

n0=3 >= n ⇒

未使用なので割り当て可

 ⇒

++n

を行い、次の

n=4

の割り当てへ進む

(14)

A-B( 名前 - 場所 ) の組み合わせ 2

1 4 0 2

配列

B 0 1 2 3 4

ここ

(n=4)

に入れる値を決める

割り当て

B[4]=3

1 4 0 2 3

配列

B A-C

組み合わせ列挙 へ進む

バックトラック

: -- n (n=3) 1 4 0 3

配列

B 0 1 2 3 4

候補

B[3]=3

他に可能な割り当てなし

(15)

A-B( 名前 - 場所 ) の組み合わせ 3

0 1 2 3 4

配列

B 0 1 2 3 4 0 1 2 4 3 0 1 3 2 4 0 1 3 4 2 0 1 4 2 3 0 1 4 3 2 0 2 1 3 4 0 2 1 4 3 0 2 3 1 4

組み合わせが 得られる順

(16)

int EnumerateC( void ){

int n, n0;

n = 0;

C[n] = -1;

while( 1 ){

C[n]++;

if( C[n] >= 5 ){

if( -- n < 0 ) break;

}else{ /* C[n]

が割り当て済みの値でないか

*/

for( n0 = 0 ; n0 < n ; n0++ ){

if( C[n] == C[n0] ) break;

} if( n0 < n ) continue; /* C[n]

は割り当てできない

*/

if( n == 4 ){ /* C

割り当て完了

*/

if( CheckAssignment() ) return 1;

}else{

C[++n] = -1;

} }

} return 0;

}

A-B-C

間組み合わせが

得られたので条件に矛盾 しないか検査する

A-C

間組み合わせ列挙

A-C

間組み合わせを全て 調べたら

0

を返す

(17)

解候補 ( 組み合わせ ) の検査 1

„ 3

つの割り当ての組み合わせ

„ 名前×場所、名前×色、場所×色

„ 3

つの

int

2

次元配列で条件を表す

int AB[5][5];

int AC[5][5];

int BC[5][5];

A:

名前、

B:

場所

A:

名前、

C:

B:

場所、

C:

要素の値

1:

確定している割り当て

-1:

ありえない割り当て

0:

どちらでもない

(18)

解候補 ( 組み合わせ ) の検査 2

„

条件違反の検出

int i, j, k;

for( i=0 ; i<5 ; i++ ){

for( j=0 ; j<5 ; j++ ){

if( AB[i][j] > 0 && B[i] != j ) return 0;

if( AC[i][j] > 0 && C[i] != j ) return 0;

if( AB[i][j] < 0 && B[i] == j ) return 0;

if( AC[i][j] < 0 && C[i] == j ) return 0;

} }

名前

i

は場所

j

矛盾

B[i] != j

名前

i

は色

j

でない 矛盾

C[i] == j

(19)

解候補 ( 組み合わせ ) の検査 3

„

複合条件違反の検出

for( i=0 ; i<5 ; i++ ){

for( k=0 ; k<5 ; k++ ){

if( B[k] == i ) break;

} for( j=0 ; j<5 ; j++ ){

if( BC[i][j] > 0 && C[k] != j ) return 0;

if( BC[i][j] < 0 && C[k] == j ) return 0;

} }

場所

i

は色

j

矛盾

C[k] != j (

名前

k

は色

j

でない

)

名前

k

は場所

i

場所

i

は色

j

でない 矛盾

C[k] == j (

名前

k

は色

j)

(20)

int CheckAssignment( void ) { int i, j, k;

for( i=0 ; i<5 ; i++ ){

for( j=0 ; j<5 ; j++ ){

if( AB[i][j] > 0 && B[i] != j ) return 0;

if( AC[i][j] > 0 && C[i] != j ) return 0;

if( AB[i][j] < 0 && B[i] == j ) return 0;

if( AC[i][j] < 0 && C[i] == j ) return 0;

} }

for( i=0 ; i<5 ; i++ ){

for( k=0 ; k<5 ; k++ ){

if( B[k] == i ) break;

} for( j=0 ; j<5 ; j++ ){

if( BC[i][j] > 0 && C[k] != j ) return 0;

if( BC[i][j] < 0 && C[k] == j ) return 0;

} }

return 1;

}

A-B-C

間組み合わせ検査

違反・矛盾のない組み合わせ

与えられた条件の いずれかに違反

矛盾

(21)

int EnumerateC( void ){

int n, n0;

n = 0;

C[n] = -1;

while( 1 ){

C[n]++;

if( C[n] >= 5 ){

if( -- n < 0 ) break;

}else{ /* C[n]

が割り当て済みの値でないか

*/

for( n0 = 0 ; n0 < n ; n0++ ){

if( C[n] == C[n0] ) break;

} if( n0 < n ) continue; /* C[n]

は割り当てできない

*/

if( n == 4 ){ /* C

割り当て完了

*/

if( CheckAssignment() ) return 1;

}else{

C[++n] = -1;

} }

} return 0;

}

A-B-C

間組み合わせが

得られたので条件に矛盾 しないか検査する

A-C

間組み合わせ列挙

A-C

間組み合わせを全て 調べたら

0

を返す

(22)

int B[5], C[5];

int n, n0;

n = 0;

B[n] = -1;

while( 1 ){

B[n]++;

if( B[n] >= 5 ){

if( -- n < 0 ) break;

}else{ /* B[n]

が割り当て済みの値でないか

*/

for( n0 = 0 ; n0 < n ; n0++ ){

if( B[n] == B[n0] ) break;

} if( n0 < n ) continue; /* B[n]

は割り当てできない

*/

if( n == 4 ){ /* B

割り当て完了

*/

if( EnumerateC() ) break;

}else{

B[++n] = -1;

} } }

A-C

間組み合わせを列挙

A-B

間組み合わせ列挙

以下の

B[n]++

と組み合わせて

B[n]=0

から開始するようにする

(23)

問題の入力

„

こういち

(0):

ボクは白い傘

(0)

を忘れたん だけど、場所は塾

(1)

ではないよ。

„

方法

1:

プログラムの先頭に書く

方法

2:

データファイルを読み込む

AC[0][0] = 1;

AB[0][1] = -1;

こういち

(A:0)

、白

(C:0)

こういち

(A:0)

、塾

(B:1)

どのようにデータとしてプログラムに入力するか

新しい問題のたびに、

プログラムの再コンパイルが必要

(24)

データファイルの記述ルール

„

こういち

(A0):

ボクは白い傘

(C0)

を忘れた んだけど、場所は塾

(B1)

ではないよ。

„

あさこ

(A1):

バス

(B4)

に傘を置き忘れちゃっ た。黒い傘

(C4)

は私のじゃないよ。

„

しょうご

(A3):

黒い傘

(C4)

を学校

(B0)

に置 き忘れた人はいないよ。

A0 and C0 A0 not B1 A1 and B4 A1 not C4

C4 not B0

残りの条件も同様に記述

こういち

(A0)

は白い

(C0)

あさこ

(A1)

は黒い

(C4)

傘でない

ファイル

‘data.txt’

に保存

(25)

データファイルの読み出し

„ fopen

で開き、

fclose

で閉じる

char buf[256], *p;

FILE *fp;

fp = fopen( "data.txt", "r" );

while( 1 ){

fgets( buf, 256, fp );

if( feof(fp) ) break;

…/*

データを処理

*/

}

fclose( fp );

ファイルを読み出し

(read)

モードで開く

ファイルが終了

(End-Of-File)

したら ループを抜け出し ファイルが不要になったら閉じる

1

行読み出し

(26)

読み出したデータを処理 (1)

int A,B,C,nA,nB,nC,n,logic;

char c;

fgets( buf, 256, fp );

if( feof(fp) ) break;

A = B = C = 0;

p = strtok( buf, " " );

c = *p++;

n = atoi(p);

switch( c ){

case 'A': A = 1; nA = n; break;

case 'B': B = 1; nB = n; break;

default: C = 1; nC = n; break;

}

p

が指す先頭文字を変数

c

に代入 さらに、

p

1

増やす

buf: " A0 and C0 "

p → "A0"

p → "0"

p

が指す文字列を

10

進数に変換 最初の項目 を記録

(27)

読み出したデータを処理 (2)

/*

前ページから続く

*/

p = strtok( NULL, " " );

if( *p == 'n' ) logic = -1;

else logic = 1;

p = strtok( NULL, " " );

c = *p++;

n = atoi(p);

switch( c ){

case 'A': A = 1; nA = n; break;

case 'B': B = 1; nB = n; break;

default: C = 1; nC = n; break;

} if( A && B ) AB[nA][nB] = logic;

if( A && C ) AC[nA][nC] = logic;

if( B && C ) BC[nB][nC] = logic;

p → "C0"

2

番目の項目 を記録

p → "and"

‘and’

‘not’

を区別

buf: " A0 and C0 "

条件として

配列を初期化

(28)

文字列を扱う関数

„

トークン切り出し

p = strtok( buf, " " ) ;

1

番目の引数

(char

型配列

)

2

番目の引数の 文字で複数の部分文字列に切り分ける。

最初の部分文字列を指すポインタを返す。

p = strtok( NULL, " " ) ;

直前に

strtok

で切り分けた部分文字列のうち、

次の部分文字列を指すポインタを返す。

利用するには

#include <string.h>

が必要

p0 = strtok( "A0 and C2", " " ) ; p1 = strtok( NULL, " " ) ;

p2 = strtok( NULL, " " ) ;

p0 → "A0"

p1 → "and"

p2 → "C2"

(29)

文字列を扱う関数

„

ファイルから

1

行読み出し

„

文字列

整数変換

char buf[256];

fgets( buf, 256, fp );

ファイル

fp

から改行で区切られた

1

行を読み出し、

char

型配列に記録する

読み出し最大文字数を指定する

int n;

n = atoi(buf);

文字列が表す

10

進数を整数値に変換する 利用するには

#include <stdlib.h>

が必要

(30)

Copyright © 2008 Kazuhito Ito

問題 : 5 人の話

„

そうた

:

ボクはジャガーズに入っています。野 球のチームではありません。

„

こうじ

:

リーブズはバスケットボールのチーム ですが、ボクはそのチームにはいません。

„

まさし

:

私はラグビーをやっています。

„

あつし

:

プラネッツはサッカーのチームです。

でもボクはプラネッツでもブルーズでもないよ。

„

よしと

:

スパローズは野球のチームではない し、ボクも野球のチームには属していません。

他にスポーツの種類としてバレーボール

出典

:

朝日新聞

(31)

データファイルを作る

„

名前

(A)

0:

そうた、

1:

こうじ、

2:

まさし、

3:

あつし、

4:

よしと

„

チーム名

(B)

0:

ジャガーズ、

1:

リーブズ、

2:

プラネッツ、

3:

ブルーズ、

4:

スパローズ

„

スポーツの種類

(C)

0:

野球、

1:

バスケットボール、

2:

ラグビー、

3:

サッカー、

4:

バレーボール

A0 and B0

A0 not C0

B1 and C1

A1 not B1

A2 and C2

B2 and C3

A3 not B2

A3 not B3

B4 not C0

A4 not C0

(32)

実行結果

× × × × ○ |○ × × × × |

○ × × × × |× × × ○ × |

× × ○ × × |× × × × ○ |

× ○ × × × |× ○ × × × |

× × × ○ × |× × ○ × × | ---|---

× × × × ○ |

× ○ × × × |

× × × ○ × |

○ × × × × |

× × ○ × × | ---

チーム スポーツの種類

名前チーム そうた

=

ジャガーズ

=

バレー

B

こうじ

=

ブルーズ

=

野球

まさし

=

スパローズ

=

ラグビー あつし

=

リーブズ

=

バスケット

B

よしと

=

プラネッツ

=

サッカー

(33)

答え合わせ

„ そうた

:

ボクはジャガーズに入っています。

野球のチームではありません。

„ こうじ

:

リーブズはバスケットボールのチームですが、

ボクはそのチームにはいません。

„ まさし

:

私はラグビーをやっています。

„ あつし

:

プラネッツはサッカーのチームです。

でもボクはプラネッツでもブルーズでもないよ。

„ よしと

:

スパローズは野球のチームではないし、

ボクも野球のチームには属していません。

そうた

=

ジャガーズ

=

バレーボール こうじ

=

ブルーズ

=

野球

まさし

=

スパローズ

=

ラグビー

あつし

=

リーブズ

=

バスケットボール

よしと

=

プラネッツ

=

サッカー ・・・

OK

(34)

まとめ

„

パズルを解くプログラムを作成

„

発想、考え方を

C

言語でプログラム化する

„

問題によっては「総当り」もあり

(“

分枝限定法

など、効率よく総当りを行う ためのアイデアも存在

)

„

問題の内容・サイズに応じて適切なアルゴ リズムを用いることが重要

„

なるべくたくさんのアルゴリズムを知ってお くことが早く「いいプログラム」を作るコツ

参照

関連したドキュメント

姉妹園がバス運行しているが、普通乗用車(ワゴン車)で送迎している。人数も3名・ 4 名程度を運転

“〇~□までの数字を表示する”というプログラムを組み、micro:bit

神姫バス 車内サイネージ 神戸市バス車内 サイネージ.

小学校学習指導要領より 第4学年 B 生命・地球 (4)月と星

エネルギー  687   kcal    マグネシウム  124   mg    ビタミンB 2   0.55  mg たんぱく質  28.1  g    鉄  4.1   mg      ビタミンC   25.0  mg 脂質  22.6   g    

静岡大学 静岡キャンパス 静岡大学 浜松キャンパス 静岡県立大学 静岡県立大学短期大学部 東海大学 清水キャンパス

11  特定路外駐車場  駐車場法第 2 条第 2 号に規定する路外駐車場(道路法第 2 条第 2 項第 6 号に規 定する自動車駐車場、都市公園法(昭和 31 年法律第 79 号)第

2【 ME 】シート 記入日(       ) 名前 呼ばれたい ニックネーム. 学校(所属)