令和5年度の科目B、その1問目です。プログラムは18行あって for が入れ子になっていますが、決め手は3つです ── どちらの for が「調べる対象」を回しているか、どちらが「割ってみる相手」を回しているか、そして条件式をよく読むこと。この2つの for の役目さえ決まれば、解答群に頼らずに空欄 a・b を自分で書き下せます(合っているかを照らすのは、いちばん最後です)。
出題
まず原文のまま読んでみてください。記号の読み方は次の節にまとめてありますので、読めなくてもここでは問題ありません。
出典:令和5年度 基本情報技術者試験 科目B 公開問題 問1
次のプログラム中の a と b に入れる正しい答えの組合せを,解答群の中から選べ。ここで,配列の要素番号は1 から始まる。
関数findPrimeNumbers は,引数で与えられた整数以下の,全ての素数だけを格納した配列を返す関数である。ここで,引数に与える整数は2 以上である。
〔プログラム〕
○整数型の配列: findPrimeNumbers(整数型: maxNum)
整数型の配列: pnList ← {} // 要素数0の配列
整数型: i, j
論理型: divideFlag
for (i を 2 から a まで 1 ずつ増やす)
divideFlag ← true
/* iの正の平方根の整数部分が2未満のときは,繰返し処理を実行しない */
for (j を 2 から iの正の平方根の整数部分 まで 1 ずつ増やす) // α
if ( b )
divideFlag ← false
αの行から始まる繰返し処理を終了する
endif
endfor
if (divideFlag が true と等しい)
pnListの末尾 に iの値 を追加する
endif
endfor
return pnList
解答群
ア a = maxNum / b = i ÷ j の余り が 0 と等しい
イ a = maxNum / b = i ÷ j の商 が 1 と等しくない
ウ a = maxNum + 1 / b = i ÷ j の余り が 0 と等しい
エ a = maxNum + 1 / b = i ÷ j の商 が 1 と等しくない
※ 解答群の原本は a・b を2つの列に分けた表です。当サイトでは1行にまとめて引用しました(字句は変えていません)。
答えだけ先に見る
正解は アです。ほかの3つが駄目な理由も、数を入れて確かめます。
この問題に出てくる記号
読めない記号があったときだけ開いてください。この表にあるだけ読めれば足ります。細かいところは使う場面で足していきます。
擬似言語の記号の読み方をひらく
| 書き方 | 読み方 |
|---|---|
○整数型の配列: findPrimeNumbers(…) |
ここから関数が1つ始まるという印。○ のうしろが返すものの型(ここでは整数の配列)、括弧の中が受け取るものです |
整数型の配列: pnList ← {} |
pnList という配列を1つ用意し、{}(要素が0個の配列)を入れる。← は右のものを左へ入れるという印です |
論理型: divideFlag |
divideFlag という箱を1つ用意する。論理型 なので true(真)か false(偽)しか入りません |
// 注釈/* 注釈 */ |
どちらも人間へのメモで、実行されません |
for (i を 2 から ◯ まで 1 ずつ増やす)endfor |
i を 2、3、4 … と増やしながら、あいだの行をくり返す。◯ のときも実行され、◯ を超えたところで終わります。始まりの数がすでに ◯ を超えていたら、1 回も実行されません(2 から 1 まで なら 0 回) |
if (…)endif |
かっこの中が成り立つときだけ、あいだの行を実行する(成り立つことを真、成り立たないことを偽といいます) |
αの行から始まる繰返し処理を終了する |
// α と書かれた for を途中でやめて抜ける。次に実行されるのは、その endfor の下の行です |
pnListの末尾 に iの値 を追加する |
配列 pnList のいちばんうしろに、i の値を1つ足す。配列は1つ長くなります |
iの正の平方根の整数部分 |
i の平方根(2 乗すると i になる数)の、小数を捨てた数。i が 9 なら平方根はちょうど 3、i が 10 なら 3.16… なので、どちらも3 です |
i ÷ j の余りi ÷ j の商 |
i を j で割ったときの余りと商。7 ÷ 2 なら商が 3、余りが 1 です |
記号でつまずいたら、下の教科書から、先にそこだけ読んでください(G6 のような番号は、当サイトの擬似言語の教科書の第何回かを表します)。
素数かどうかは、割り算の余りだけで決まる
素数とは、2 以上の整数で、1 と自分自身のほかには割り切れる数を持たないものです。2、3、5、7、11 … と続きます。
調べ方は割り算だけです。ある数を 2、3、4 … と順に割ってみて、1 回でも割り切れたら素数ではない。1 回も割り切れなければ素数。「割り切れた」は余りが 0 ということです(10 ÷ 2 は余り 0、10 ÷ 3 は余り 1)。擬似言語では、この余りを i ÷ j の余り と書きます。
知っておくのは、これで全部です。あとはプログラムを読みます。
2つの for は、それぞれ何を回しているか
この問題のポイントは、ここです。for が2つ、入れ子になっています。どちらが「素数かどうかを調べる対象」を回していて、どちらが「割ってみる相手」を回しているのか ── これが決まれば、あとは条件式を読むだけになります。
決め方は、その for の中で、回している変数がどう使われているかを見ることです。
2つの for は、別のものを回している
①は、あいだの行を …… で省いています(当サイト)。それ以外は〔プログラム〕のままです。
① 外側の for が回しているのは i
for (i を 2 から a まで 1 ずつ増やす)
divideFlag ← true
……
if (divideFlag が true と等しい)
pnListの末尾 に iの値 を追加する
endif
endfor
return pnList
pnList は、この関数が返す配列です。そこに足しているのは i の値 ── つまり i は「素数かどうかを調べる対象」。外側の for は、その対象を 2 から順に送り出しています。
② 内側の for が回しているのは j
for (j を 2 から iの正の平方根の整数部分 まで 1 ずつ増やす) // α
if ( b )
divideFlag ← false
αの行から始まる繰返し処理を終了する
endif
endfor
内側の for の中にあるのは、空欄 b の if ひとつだけです。そして divideFlag を false に倒すのは、プログラム全体でここ 1 か所 ── 倒れた i は、①の pnList に入りません。(出題の全文を見ても、divideFlag ← false はこの行だけです)。つまり空欄 b は「その i は素数ではない」と決める条件で、その判定を j を 1 つずつ変えながらくり返しています。素数は「1 回でも割り切れたら素数ではない」で決まるのでしたから、j は「i を割ってみる相手」です。
③ すると、j の上限の役目も決まる
for (j を 2 から iの正の平方根の整数部分 まで 1 ずつ増やす) // α
j が「割ってみる相手」だと決まったので、その上限に書いてある iの正の平方根の整数部分 の役目も決まります ── 割ってみる相手を、どこまで試すかです。この値がいくつになるかを知らなくても、置かれている場所から役目は読めます。
i = 調べる対象 / j = 割ってみる相手。
役目が決まったので、空欄の2つも、それぞれ何を書く場所なのかが決まりました。a は調べる対象をどこまで送るか、b は割り切れたかどうかを見る式です。
空欄に入るものを決める
a に入るものを決める
〔プログラム〕空欄 a のある行
for (i を 2 から a まで 1 ずつ増やす)
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
この for が送り出した i が、そのまま候補になります(2 から始まっているのは、素数が 2 以上だからです)。逆に言えば、ここで送らなかった数は、素数でも答えに入りません。
関数が返すのは「引数で与えられた整数以下の,全ての素数」。引数は maxNum ですから、送ってよい上限も maxNum です。for は上限そのものも実行する書き方なので、maxNum と書けば maxNum まで調べられます。
a に入るもの
for (i を 2 から maxNum まで 1 ずつ増やす)
1 つ多く送ると、maxNum より大きい数まで候補になります。それが素数だったとき、「maxNum 以下」ではない数が答えに混ざります。
b に入るものを決める
この if が「i が j で割り切れたかどうか」を見る行だ、というところまでは決まりました。ここからは条件式そのものを読みます。
〔プログラム〕空欄 b のある行(内側の繰返しごと)
for (j を 2 から iの正の平方根の整数部分 まで 1 ずつ増やす) // α
if ( b )
divideFlag ← false
αの行から始まる繰返し処理を終了する
endif
endfor
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
b が満たすこと
真になったら その i は答えに入らない。素数なのに真になったら誤り
どの j でも偽なら その i は答えに入る。素数でないのに全部偽なら誤り
はじめに決めた手順は「1 回でも割り切れたら素数ではない」でした。b は j を 1 つ受け取るたびに評価される行なので、そこに書くのは「その j で割り切れたか」 ── つまり i ÷ j の余りが 0 です。
b に入るもの
if (i ÷ j の余り が 0 と等しい)
本番の手順 ── for が入れ子になっていたら
- それぞれの
forが何を回しているかを、先に決める。決め手は、回している変数がその中でどう使われているか。この問題なら「pnListに足しているのはi」「divideFlagを倒すのは、内側のforの中の 1 か所だけ」。 - 役目が決まると、上限や条件の役目も決まる。知らない書き方が上限に置かれていても、置かれている場所から役目は読めます。この問題の
iの正の平方根の整数部分がそれです。 - そこまで来てから、条件式を読む。「割り切れた」と言葉にできていれば、似た式が並んでいても照合するだけで済みます。先に選択肢を当てはめにいくと、4 通りを全部たどることになります。
ここで解答群と照らす
a は maxNum、b は i ÷ j の余り が 0 と等しい。この組合せは ア ひとつだけです。
答え合わせ ── 入れて動かすと {2, 3, 5, 7} が返る
空欄に ア を入れて、maxNum が 10 のときを最後まで通します。見るのは divideFlag と pnList です。
1行ずつ追う 正解の ア(a = maxNum / b = i ÷ j の余り が 0 と等しい)
出典:令和5年度 科目B 公開問題 問1(空欄に ア を入れ、maxNum に 10 を与えたもの)
ループ開始前
集まった素数
いま計算していること
変数の状態
return で返した配列
トレース表(進めると1行ずつ積み上がります)
正解は ア
a = maxNum、b = i ÷ j の余り が 0 と等しい。返ってきたのは {2, 3, 5, 7} ── 10 以下の素数がそろっています。
ほかの 3 つは、どこで外れるかがはっきりしています。
イ と エ(b = i ÷ j の商 が 1 と等しくない)は、i が 5 のところで外れます。5 のとき j は 2 だけで、5 ÷ 2 は商が 2、余りが 1 です。商の 2 は 1 と等しくないので、この条件は真 ── 5 は割り切れていないのに divideFlag が倒れ、答えから落ちます。しかもこれは 5 に限りません。内側の for が回るとき j は必ず 2 から始まり、4 以上の数を 2 で割れば商は必ず 2 以上です(2 が 2 つぶん以上入っているからです)。つまり j = 2 の 1 回目で、もう「1 と等しくない」が真になります。結果、maxNum が 3 以上なら、返るのは {2, 3} だけです(2 と 3 は内側が 1 回も回らないので残ります)。
ウ(a = maxNum + 1)は、割り切れるかの判定は正しいのに、調べる対象を 1 つ多く送ります。maxNum が 10 なら 11 まで調べ、11 は素数なので {2, 3, 5, 7, 11} が返ります。11 は「10 以下」ではありません。引数の最小値である maxNum = 2 でも、{2, 3} になって同じように外れます。
次に読む
この問題でどこに手間取ったかで、行き先が変わります。
for の役割を読み分けるところで止まった人へG8-2論理型 ── true と false の箱の使い方をもう一度確かめたい人へ一覧科目B 全44問の解説 ── 同じ形の問題を続けて解きたい人へこの記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。
シミュレータの動きと、記事の終わりに書いた誤答の返り値は、当サイトで〔プログラム〕を解答群4つとも実際に回して求めたものです。maxNum に 10 を選んだのも当サイトです(原文は maxNum の値を決めていません)。図の中のプログラムは〔プログラム〕からそのまま切り出したもので、①のあいだの行を「……」で省いた以外は変えていません。

