令和6年度の科目B、その4問目です。小さい順に並んだ配列を2本受け取って、1本の小さい順の配列にまとめるプログラムが出てきます。ただし聞かれているのは、できあがる配列ではありません。ある1行が、何回実行されるかです。この記事では、3つの番号が動くようすを自分の手で追って、解答群を1度も見ないまま回数を言えるところまで進みます。
出題
まず原文のまま読んでみてください。記号の読み方は次の節にまとめてありますので、読めなくてもここでは問題ありません。
出典:令和6年度 基本情報技術者試験 科目B 公開問題 問4
次の記述中の に入れる正しい答えを,解答群の中から選べ。ここで,配列の要素番号は1 から始まる。
関数merge は,昇順に整列された整数型の配列data1 及びdata2 を受け取り,これらを併合してできる昇順に整列された整数型の配列を返す。
関数 merge を merge({2, 3}, {1, 4}) として呼び出すと,/*** α ***/ の行は 。
〔プログラム〕
○整数型の配列: merge(整数型の配列: data1, 整数型の配列: data2)
整数型: n1 ← data1の要素数
整数型: n2 ← data2の要素数
整数型の配列: work ← {(n1 + n2)個の 未定義の値}
整数型: i ← 1
整数型: j ← 1
整数型: k ← 1
while ((i ≦ n1) and (j ≦ n2))
if (data1[i] ≦ data2[j])
work[k] ← data1[i]
i ← i + 1
else
work[k] ← data2[j]
j ← j + 1
endif
k ← k + 1
endwhile
while (i ≦ n1)
work[k] ← data1[i]
i ← i + 1
k ← k + 1
endwhile
while (j ≦ n2)
work[k] ← data2[j] /*** α ***/
j ← j + 1
k ← k + 1
endwhile
return work
解答群
ア 実行されない
イ 1 回実行される
ウ 2 回実行される
エ 3 回実行される
答えだけ先に見る
正解は イ(1 回実行される)です。なぜ1回なのかは、この記事で最後まで自分の手でたどります。
この問題に出てくる記号
読めない記号があったときだけ開いてください。いま読めればいいのは12個だけで、細かいところは使う場面で足していきます。
擬似言語の記号 12コ の読み方をひらく
| 書き方 | 読み方 |
|---|---|
○整数型の配列: merge(…) |
ここから関数が1つ始まるという印。○ のうしろの 整数型の配列 は返すものの型、括弧の中が受け取るものです。ここでは配列を2本受け取って、配列を1本返します |
整数型: i ← 1 |
i という箱を1つ用意して、1 を入れる。整数型 なので整数だけが入ります |
a ← b |
右の値を、左へ入れる。入れられた側(左)は前の中身が消えて上書きされます。読まれた側(右)はそのままです |
data1[i] |
配列 data1 の i 番目の枡。角かっこの中を添字といい(出題文の要素番号と同じものです)、箱の名前(変数)も書けます(i が 2 なら data1[2])。この試験では 1 から数えます |
data1の要素数 |
その配列に入っている枡の個数。{2, 3} なら 2 です |
{(n1 + n2)個の 未定義の値} |
枡だけ用意して、中身は空という配列。n1 と n2 がどちらも 2 なら、4枡ぶん用意されます。この「中身が空」の状態を未定義といいます |
while (…)endwhile |
かっこの中が成り立つあいだ、endwhile までをくり返す。くり返しに入る前に、毎回かならず条件を見ます ── だからはじめから成り立たなければ、中は1回も実行されません。くり返しの1回ぶんを、この記事では「1周」と呼びます |
if (…)elseendif |
かっこの中が成り立つときだけ else までの行を、成り立たなければ else から endif までの行を実行する |
a ≦ b |
左が右以下のとき成り立つ。等しいときも成り立ちます |
and |
左右がどちらも成り立つときだけ成り立つ。片方でも成り立たなければ、全体が成り立ちません |
return ◯ |
◯ を返して、その関数はそこで終わり。呼んだ側にその値が渡ります |
| /*** α ***/ | 注釈。人が読むための書き込みで、それ自体は何も実行しません。この問題では行に名札を貼るために使われていて、聞かれているのはその名札が貼られた行のほうです |
記号でつまずいたら、下の教科書から、先にそこだけ読んでください(G7 のような番号は、当サイトの擬似言語の教科書の第何回かを表します)。
3本の配列と、3つの番号
併合とは、小さい順に並んだ列を2本受け取って、小さい順に並んだ1本の列にまとめることです(出題文の「昇順に整列された」が、この「小さい順に並んだ」にあたります)。この問題では data1 と data2 を受け取り、work という3本目の配列に答えを書いていきます。
プログラムはまず、data1 の枡の数を n1、data2 の枡の数を n2 に入れます ── どちらもここでは 2 です。そして、いま見ている場所をおぼえておく箱を3つ用意します。
3つの番号が、それぞれ別の配列を見ている
i data1 の次に見る枡の番号
j data2 の次に見る枡の番号
k work の次に書く枡の番号
3つとも整数を入れる箱で、中身が枡の番号です。この記事ではまとめて「番号」と呼びます。
出発の形 ── 3本の配列と、3つの番号がどこを指しているか
▲=その番号がいま指している枡 / 網掛け=未定義(まだ何も入っていない)
merge({2, 3}, {1, 4}) が呼ばれた直後
n12
n22
i1
j1
k1
data1
12▲
23
work
1未定義▲
2未定義
3未定義
4未定義
data2
11▲
24
data1 も data2 も小さい順に並んでいます。work は n1 + n2 = 4 枡で、中身は4つとも未定義。3つの番号は、どれも 1 から始まります。
work を真ん中に置いてあります。上の data1 と下の data2 から、真ん中へ集めていきます。
1周ですることは2つだけです。① data1[i] と data2[j] をくらべて、小さいほうを work[k] へ写す(判定は data1[i] ≦ data2[j] なので、同じ値のときは data1 側が写ります)。② 写したほうの番号だけを1つ進める(k は毎周かならず進みます)。写さなかったほうの番号は、そのまま次の周にも残ります。
手で動かす ── 1つ目の while を最後まで
呼び出しは merge({2, 3}, {1, 4})。data1 が {2, 3}、data2 が {1, 4} です。動かすのは、この10行だけです。
〔プログラム〕1つ目の while(この節で動かすのはここだけ)
while ((i ≦ n1) and (j ≦ n2))
if (data1[i] ≦ data2[j])
work[k] ← data1[i]
i ← i + 1
else
work[k] ← data2[j]
j ← j + 1
endif
k ← k + 1
endwhile
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
止まるところまで、1周ずつ進めます。
1つ目の while ── くらべて、小さいほうを真ん中へ写す
▲= i・j・k がいま指している枡(data1 と data2 の ▲ が、その周にくらべる2枡) / 琥珀=その周に写した値(写しもと) / 緑=その周に書き込まれた枡 / 灰=もう写し終えた枡 / 網掛け=未定義。「次へ」で1周ずつ進みます。
① 1周目 ── data1[1] の 2 と data2[1] の 1 をくらべる
i1
j1
k1
data1
12▲
23
work
11▲
2未定義
3未定義
4未定義
小さいほうの 1 を写す
data2
11▲
24
条件は (1 ≦ 2) and (1 ≦ 2) で成り立ちます。2 ≦ 1 は成り立たないので else のほうへ進み、data2 側の 1 を写します。進むのは j だけで、i は 1 のままです。
② 2周目 ── data1[1] の 2 と data2[2] の 4 をくらべる
i1
j2
k2
data1
12▲
23
小さいほうの 2 を写す
work
11
22▲
3未定義
4未定義
data2
11
24▲
条件は (1 ≦ 2) and (2 ≦ 2) で成り立ちます。2 ≦ 4 が成り立つので data1 側の 2 を写します。今度は i だけが進みます。
③ 3周目 ── data1[2] の 3 と data2[2] の 4 をくらべる
i2
j2
k3
data1
12
23▲
小さいほうの 3 を写す
work
11
22
33▲
4未定義
data2
11
24▲
条件は (2 ≦ 2) and (2 ≦ 2) で成り立ちます。3 ≦ 4 が成り立つので data1 側の 3 を写し、i が 3 に。これで data1 は2枡とも写し終えました。
④ 4周目に入ろうとして、止まる
i3
j2
k4
data1
12
23
work
11
22
33
4未定義▲
data2
11
24▲
条件をもう一度見ます。i ≦ n1 は 3 ≦ 2 で成り立ちません。and は片方でも成り立たなければ全体が成り立たないので、ここで1つ目の while を抜けます。i は 3 ── data1 の枡の外を指しているので、▲ が付きません。そして data2 の 4 は、まだ写されていません。
3周で work は {1, 2, 3, 未定義}。抜けた時点で i = 3、j = 2。
4周目に入ろうとしたところで、i が data1 の枡の数を超えていたので止まりました。この while は、data1 と data2 のどちらか一方が尽きた瞬間に止まります。
コードの中で、3つの while は何をする行か
いま手で動かしたことを、プログラムの各行に当てはめます。while が3つ並んでいるのが、このプログラムの形です。
○整数型の配列: merge(整数型の配列: data1, 整数型の配列: data2)
受け取るのは配列2本、返すのも配列
整数型: n1 ← data1の要素数
data1 の枡の数。ここでは 2
整数型: n2 ← data2の要素数
data2 の枡の数。ここでは 2
整数型の配列: work ← {(n1 + n2)個の 未定義の値}
答えを書く先。2 + 2 = 4 枡を、すべて未定義で用意する
整数型: i ← 1
data1 の次に見る枡の番号
整数型: j ← 1
data2 の次に見る枡の番号
整数型: k ← 1
work の次に書く枡の番号
while ((i ≦ n1) and (j ≦ n2))
1つ目。両方に残りがあるあいだ。and なので片方が尽きたら止まる
if (data1[i] ≦ data2[j])
先頭どうしをくらべる
work[k] ← data1[i]
data1 側を写す
i ← i + 1
写した側の番号だけ進む
else
work[k] ← data2[j]
data2 側を写す
j ← j + 1
写した側の番号だけ進む
endif
k ← k + 1
書く先は毎周かならず1つ進む
endwhile
while (i ≦ n1)
2つ目。data1 に残っていた分を、そのまま後ろへ写す
work[k] ← data1[i]
i ← i + 1
k ← k + 1
endwhile
while (j ≦ n2)
3つ目。data2 に残っていた分を、そのまま後ろへ写す
work[k] ← data2[j] /*** α ***/
この行が問われている行。うしろの注釈は名札で、実行されない
j ← j + 1
k ← k + 1
endwhile
return work
並べ終わった配列を返す
※ 右側の注釈は当サイトで書き加えたものです。プログラム自体は原文のまま引用しています。
3つの while の役割
1つ目 両方に残りがあるあいだ、小さいほうを写す
2つ目 data1 に残っていた分を写す
3つ目 data2 に残っていた分を写す ── 問われている行はここ
問われている行が何回実行されるかを決める
決めるのは、後ろの2つの while です。
〔プログラム〕2つ目の while(data1 の残りを写す)
while (i ≦ n1)
work[k] ← data1[i]
i ← i + 1
k ← k + 1
endwhile
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
〔プログラム〕3つ目の while(問われている行を含む)
while (j ≦ n2)
work[k] ← data2[j] /*** α ***/
j ← j + 1
k ← k + 1
endwhile
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
どちらも、条件は1つ目の while を抜けた時点の i と j で決まります。そこがどうなっていたかを、もう一度見ます。
1つ目の while を抜けたところ ── 残っているのはどちらの配列か
灰=もう写し終えた枡 / 琥珀の破線=まだ写していない、残っている枡 / ▲= j・k がいま指している枡 / 網掛け=未定義
n12
n22
i3
j2
k4
data1
12
23
work
11
22
33
4未定義▲
data2
11
24▲
data1 は 2 も 3 も写し終えて、i は 3 ── 枡の外です。写していないのは data2 の 4 だけ。
data1 の残り 0個 / data2 の残り 1個。
2つ目の while の条件は i ≦ n1。i は 3、n1 は 2 なので 3 ≦ 2 は成り立ちません。while はくり返しに入る前に条件を見るので、この while の中は 1回も実行されません。
3つ目の while の条件は j ≦ n2。j は 2、n2 は 2 なので 2 ≦ 2 は成り立ちます。中に入り、問われている行が実行されて、残っていた 4 が work[4] へ入ります。そのあと j が 3 になり、もう一度条件を見ると 3 ≦ 2 は成り立たないので、そこで終わりです。
この while は、j を1つずつ進めながら n2 を超えるまで回ります。つまり回る回数は、入ってきた時点で data2 に残っていた個数そのものです。ここでは残りが 1個だったので、1回。
問われている行が実行される回数
1 回。3つ目の while が回る回数は、1つ目の while が data2 に残した個数と同じです。
ここで初めて解答群を見る
1回。解答群でこの回数は イ ひとつだけです。
答え合わせ ── 入れて動かすと、問われている行を1回だけ通る
はじめから最後まで、1行ずつ通します。見るのは i・j・k の3つの番号と、work です。
1行ずつ追う merge({2, 3}, {1, 4})
出典:令和6年度 科目B 公開問題 問4
ループ開始前
3本の配列
いま計算していること
変数の状態
return で返す配列
トレース表(進めると1行ずつ積み上がります)
正解は イ
問われている行を通るのは 1回だけ。返る配列は {1, 2, 3, 4} です。
ほかの3つは、data2 の残りが違う呼び出しでの回数です。同じプログラムでも、渡す配列が変われば回数はこう変わります。
ア(0回) data2 の残りが 0個。先に尽きるのが data2 のほうだった場合です。merge({1, 4}, {2, 3}) なら、j が 3 になって1つ目の while が止まり、残った 4 は2つ目の while が写します。
イ(1回) data2 の残りが 1個。本問の merge({2, 3}, {1, 4}) がこれです。
ウ(2回) data2 の残りが 2個。merge({1}, {2, 3}) なら、1つ目の while は1周で data1 を使い切り、data2 の 2 と 3 が丸ごと残ります。
エ(3回) data2 の残りが 3個。同じように、data1 が先に尽きて data2 が3つ余った場合です。
次に読む
この問題でどこに手間取ったかで、行き先が変わります。
while ── 「入る前に条件を見る」がつかめなかった人へ問3令和6年度 科目B 問3 ── 同じ年度の1つ前の問題を解きたい人へ一覧科目B 全44問の解説 ── 続けて解きたい人へこの記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。
「別の呼び出しで確かめる例(merge({1, 4}, {2, 3}))」は当サイトのオリジナルです。

