pythonで学ぶ情報(I)その2

ちょいちょい疲れちゃいましたね。お疲れ様です。お気づきになったかもしれませんが、三角ボタンを押したときに出た「コンソール」というのがコマンドプロンプトの代わりになるのでした。これで編集から実行までが1つのウインドウでできちゃいます。

2.1 さっそく解いてみる

お借りした模試からこんな問題を引っ張ってきました。問題の関係でプログラム問題じゃないところも説明しています。

~第3問~

次の文章を読み、後の問い(問1~3)に答えよ。(配点25)

Kさんが所属する工芸部では毎年、文化祭に向けた集中製作合宿を開催し,複数の工芸品を部員全員で分担して製作している。Kさんは今年、工芸品を製作する担当の割当て作業を行うことになった。

問1 次の文章を読み、空欄ア~オに当てはまる数字をマークせよ。

表1は今年製作する各工芸品(1から順に番号を振る。)の製作日数である。製作日数は部員によって変わることはなく、例えば工芸品1の製作日数はどの部員が製作しても4日である。なお、一つの工芸品の製作は一人の部員が担当し,完了するまでその部員は他の工芸品の製作には取り掛からない。

表1 各工芸品の製作日数

工芸品123456789
製作日数413134243

Kさんは図1の割図を作成し、今年の工芸部の部員3名について、工芸品の番号順に割てを決めていくことにした。

日付(日目)12345678910
部員111
部員224
部員33

図1 割当図(工芸品4まで)

図1では、最上段に日付を合宿初日から順に1日目、2日目、・・・と表して記載している。その下に各部員(1から順に番号を振る。)に割り当てた工芸品の番号を、その製作期間を表す矢印とともに記載している。例えば、工芸品4は部員(ア)が(イ)日目から1日間製作することが、図1から読み取れる。

図1では工芸品4までが割り当てられており、部員1が5日目で割当てがない。このことを、部員1は5日目で空きであるという。

Kさんは各工芸品の担当と期間を割り当てていく際、次の規則を用いた。

最も早く空きになる部員(複数いる場合はそのうち最小の番号の部員)が,空きになった日付から次の工芸品を担当する。

Kさんは、工芸品5以降についても上の規則を用いて割り当て、各工芸品の担当と期間を一覧にした図2のような文面のメールを部員全員に送信した。

工芸品1…部員1:1日目~4日目

工芸品2 …部員2:1日目~1日目

工芸品3…部員3:1日目~3日目

工芸品4…部員(ア):(イ)日目~(イ)日目

工芸品5…部員(ウ):(エ)日目~(オ)日目

工芸品9….部員1:7日目~9日目

図2 各工芸品の担当と期間を一覧にしたメールの文面

以上を手作業で作成するのが手間だと感じたKさんは、図2のような文面を自動的に表示するプログラムを作成しようと考えた。

ちょっと解説…一番早く空くのは部員2だから部員2が工芸品5を担当することになる。工芸品5の製作日数は「3」だから3日目から5日目までが埋まることになる。

問2 次の文章を読み、空欄カ、クに当てはまる数字をマークせよ。また空欄キに入れるのに最も適当なものを、後の解答群のうちから一つ選べ。

Kさんはまず、次の規則(再掲)に従い、いくつかの工芸品がすでに割り当てられた状況で、その次の工芸品の担当部員を表示するプログラムを作ることにした。

最も早く空きになる部員(複数いる場合はそのうち最小の番号の部員)が、空きになった日付から次の工芸品を担当する。

最も早く空きになる部員の番号を求めるために、各部員が空きになる日付を管理する配列 Akibiを用意する。この配列の添字(1から始まる。)は部員の番号であり、要素はその部員が空きになる日付である。

例えば、図1の状況では、配列Akibiは図3のようになる。図1で部員1は5日目に空きになるため、図3で要素Akibi[1]は5となる。同様に要素Akibi [3]はカとなる。

日付(日数)12345678910
部員11(1)
部員224
部員33

図1 割当図(工芸品4まで)(再掲)

添字123
Akibi53

図3 図1の状況に対応する配列 Akibi

図3において、要素Akibi[ウ]が配列 Akibiの最小の要素であることから、部員ウが最も早く空きになることがわかる。

この考え方に基づき、Kさんは配列Akibiの要素と、部員数が代入された変数buinsuを用いて、次に割り当てる工芸品の担当部員を表示するプログラムを作成した(図4)。ここでは例として、(01)行目で図3のように配列Akibiを設定している。

(01)Akibi = [5, 3,(カ)]
(02)buinsu = 3
(03)tantou = 1
(04)buinを2から buinsu まで1ずつ増やしながら繰り返す:
(05)|もし(キ)ならば、
(06)└└tantou=buin
(07)表示する(“次の工芸品の担当は部員”,tantou,”です。”)

図4 次に割り当てる工芸品の担当部員を表示するブログラム

仮に部員数が変わったとしても、配列Akibiと変数buinsuを適切に設定すれば、このプログラムを用いることができる。部員が5名に増えた場合、(01)行目を例えばAkibi=[5,6,4,4,4]に、(02)行目をbuinsu=5 に変更して図4のプロクラムを実行すると、(06)行目の代入がク回行われ、「次の工芸品の担当は部員3です。」と表示される。

キの解答群

⓪ buin < tantou ① Akibi [buin] < Akibi [tantou]

② buin > tantou ③ Akibi [buin] › Akibi [tantou]

~いったん引用ここまで~

ようやくプログラムらしいものが出てきたので、ここでようやく解説に入ります。情報(I)で使われる言語は「仮想言語」といい、要するに世の中には実在しません。たぶんバージョン違いの食い違いを防ぐためだと思いますが、たいていの人はいきなりここでこけると思います。実習するにもとりあえず実在する言語に置き換えないといけません。この解説ではタイトルの通りpythonを用いますが、理由は「けっこう似てたから」です。

pythonでは先頭の行番号は使いません。あと「繰り返し」の構文が違います。逆に言えば簡単な要素だけ把握しておけばすぐにpythonに置き換え可能です。「とりあえず実行してみる」ことで感覚として学ぶことができます。

まず繰り返しは「for」文です。あとは「もし~ならば」は「if」文です。まずこの問題はこれらの要素だけで解けます。

あと1個だけ忘れてました。pythonに限らず多くの言語は配列を「0」から始めます。Akibi配列は3個ですが、これの指定はAkibi[0], Akibi[1], Akibi[2]の3つになります。Akibi[3]を指定してしまうと「そんな中身はありません」とエラーになります。仮想言語というよくわからないものを導入するのはこんな理由なのかもとちょっと思いました…。

「buinを2から buinsu まで1ずつ増やしながら繰り返す」

をpythonで書くと

for buin in range(1,buinsu):

となります。注意点が二つ。先ほどの通り0から始まるのでbuinも2と言われたら1個減らして1から指定します。あれ、じゃあbuinsuもbuinsu-1にしなきゃいけないんじゃないの、と思ったあなたはえらい!rangeというのはその名の通り変数buinが変化する範囲を指定できるのですが、「(2番目の引数ー1)までしか変化しない」という特徴があります。つまりに引数からrangeから全部「1個ずれてる」んですね。わかりにくい…。てなことでbuinsuは本来3まで変化するのですが、配列も1こずれるしrangeも1個前の「2」までしか変化しないので、こっちはこのままでいいです。

あとかっこの後に小さく”:”があります。これは必要です。

問題文のような”└”はいらないのですが、そのかわり「TABキー」で一段後ろにシフトしておくことが必要になります。よくあるような二段ループだと、2段目は2つシフトします。こんな感じです。

for a in range(0,2):
    for b in range(0,2):
        print(a,b)

ちなみにこれだけでも実行できます。結果がこちら。

0 0
0 1
1 0
1 1

2の1個前だから結局0~1で繰り返すんですね。

とりあえずさっくり置き換えてみましょう。

Akibi=[5,3,4]
buinsu =3
tantou =0
for buin in range(1,buinsu):
    if (buin < tantou):
        tantou = buin
print("次の工芸品の担当は部員",tantou,"です。")

for文、if文の後に:(コロンといいます。;がセミコロン)をつけること、rangeの指定がちょっと癖があることを除けば、ほぼそのまま行けると思います。ちなみに(キ)に該当する部分は何も考えずに選択肢⓪を入れています。まあまあ実行します。

次の工芸品の担当は部員 0 です。

これを考えます。空き日程は5,3,4だったはずですから、一番早いのは部員2→配列は1個ずれるので「1」になるはずですが、なっていません。総当たりをしてもいいのですが、ちょっと考えます。

tantou=0としておきながら、forループのbuinは1から始まっています。そしてtantou=buinのような処理はif文の中だけです。そうなると「まず担当tantouを部員1に設定して、部員2~3の予定を部員1と比較する。部員2~3の方が早くできそうならtantouを部員2か3に置き換えて、ダメなら部員1のまま結果を表示する。」みたいな流れが想像できます。

だとすると、⓪や②のように部員の番号を比較してもしょうがないことに気が付きます。大事なのは空き日程、なので①か③に絞られます。あとは大きいか小さいか。

そして「早くできそうなら」を考えます。現在の担当者より早ければいいので、①の”Akibi [buin] < Akibi [tantou]”が適切な気がします。改めて以下のプログラムを実行します。

Akibi=[5,3,4]
buinsu =3
tantou =0
for buin in range(1,buinsu):
    if (Akibi[buin] < Akibi[tantou]):
        tantou = buin
print("次の工芸品の担当は部員",tantou+1,"です。")

最後の結果表示が配列のせいで1個ずれるのが分かりづらいと思ったので、結果表示だけ元に戻して(+1)みました。すると、

次の工芸品の担当は部員 2 です。

おおお、ようやくまともな結果が出た。

とりあえずお疲れさまでした。(つづくよ)