令和6年度の科目B、その5問目です。買い物の注文データから、いっしょに買われやすい商品を見つけるプログラムが出てきます。空欄は3つありますが、決めることは2つしかありません ── そこに気づけるかどうかで、かかる時間がまるで変わります。この記事では空欄になっていない1行を手がかりに2つとも決めます(解答群はいくつの軸でできているかだけ先に見て、中身は最後に1度だけ見ます)。
出題
まず原文のまま読んでみてください。記号の読み方は次の節にまとめてありますので、読めなくてもここでは問題ありません。
出典:令和6年度 基本情報技術者試験 科目B 公開問題 問5
次のプログラム中の a ~ c に入れる正しい答えの組合せを,解答群の中から選べ。ここで,配列の要素番号は1 から始まる。
一度の注文で購入された商品のリストを,注文ごとに記録した注文データがある。表に,注文データの例を示す。
表 注文データの例
| 注文番号 | 購入された商品のリスト |
|---|---|
| 1 | A, B, D |
| 2 | A, D |
| 3 | A |
| 4 | A, B, E |
| 5 | B |
| 6 | C, E |
注文データから,商品x と商品y とが同一の注文で購入されやすい傾向を示す関連度Lxy を,次の式で計算する。
Lxy = (Mxy × 全注文数)/(Kx × Ky)
※ / はわり算です。原文は分数の形(上が Mxy × 全注文数、下が Kx × Ky)で組まれていて、当サイトで1行に書き直しました。
ここで,Mxy は商品x と商品y とが同一の注文で購入された注文数,Kx は商品x が購入された注文数,Ky は商品y が購入された注文数を表す。表の例では,MAB が2,全注文数が6,KA が4,KB が3 であるので,商品A と商品B の関連度LAB は,(2 × 6) / (4 × 3) = 1.0 である。
手続putRelatedItem は,大域変数orders に格納された注文データを基に,引数で与えられた商品との関連度が最も大きい商品のうちの一つと,その関連度を出力する。プログラムでは,商品は文字列で表し,注文は購入された商品の配列,注文データは注文の配列で表している。注文データには2 種類以上の商品が含まれるものとする。また,注文データにある商品以外の商品が,引数として与えられることはないものとする。
〔プログラム〕
// 注文データ(ここでは表の例を与えている)
大域: 文字列型配列の配列: orders ← {{"A", "B", "D"}, {"A", "D"}, {"A"},
{"A", "B", "E"}, {"B"}, {"C", "E"}}
○putRelatedItem(文字列型: item)
文字列型の配列: allItems ← ordersに含まれる文字列を
重複なく辞書順に格納した配列
// 表の例では {"A", "B", "C", "D", "E"}
文字列型の配列: otherItems ← allItemsの複製から値がitemである
要素を除いた配列
整数型: i, itemCount ← 0
整数型の配列: arrayK ← {otherItemsの要素数個の0}
整数型の配列: arrayM ← {otherItemsの要素数個の0}
実数型: valueL, maxL ← -∞
文字列型の配列: order
文字列型: relatedItem
for (orderにordersの要素を順に代入する)
if (orderのいずれかの要素の値がitemの値と等しい)
itemCountの値を1増やす
endif
for (iを1からotherItemsの要素数まで1ずつ増やす)
if (orderのいずれかの要素の値がotherItems[i]の値と等しい)
if (orderのいずれかの要素の値がitemの値と等しい)
a の値を1増やす
endif
b の値を1増やす
endif
endfor
endfor
for (iを1からotherItemsの要素数まで1ずつ増やす)
valueL ← (arrayM[i] × c) ÷ (itemCount × arrayK[i])
/* 実数として計算する */
if (valueLがmaxLより大きい)
maxL ← valueL
relatedItem ← otherItems[i]
endif
endfor
relatedItemの値とmaxLの値をこの順にコンマ区切りで出力する
解答群
ア a:arrayK[i] b:arrayM[i] c:allItemsの要素数
イ a:arrayK[i] b:arrayM[i] c:ordersの要素数
ウ a:arrayK[i] b:arrayM[i] c:otherItemsの要素数
エ a:arrayM[i] b:arrayK[i] c:allItemsの要素数
オ a:arrayM[i] b:arrayK[i] c:ordersの要素数
カ a:arrayM[i] b:arrayK[i] c:otherItemsの要素数
答えだけ先に見る
正解は オ です。なぜオになるのかは、この記事で順に決めていきます。
この問題に出てくる記号
読めない記号があったときだけ開いてください。先に全部読む必要はありません。
擬似言語の記号 16コ の読み方をひらく
| 書き方 | 読み方 |
|---|---|
○putRelatedItem(文字列型: item) |
○ はここからひとまとまりの処理が始まるという印。括弧の中が受け取るもので、文字列型: item =「item という名前で文字列を1つ受け取る」。これを引数といいます。○ のうしろに型が書かれていないので、この処理は値を返しません(返さないものを手続、返すものを関数と呼び分けます) |
大域: 文字列型配列の配列: orders |
大域=プログラム全体で共有する箱。手続の中で作った箱と違い、呼ばれる前からそこにあります。文字列型配列の配列 は「文字列の配列」を要素にした配列で、この問題では注文1件が配列1本、それを6本ならべたものです |
{"A", "B", "D"} |
{ } で配列の中身を書きます。文字の並びは二重引用符でくくって書き、"A" と "B" は別のもの、"A" と "A" は等しいです |
a ← b |
右の値を、左へ入れる。入れられた側(左)は前の中身が消えて上書きされます |
整数型: i, itemCount ← 0 |
箱をまとめて2つ宣言している行です。← 0 が itemCount だけに掛かるのか2つとも 0 になるのかは、〔擬似言語の記述形式〕に書かれていません。ただしこの問題では結果が変わりません ── 効くのは itemCount が 0 から数え始めることだけで、i はあとの for が 1 から順に入れ直すためです |
{otherItemsの要素数個の0} |
0 を、otherItems の枡の数だけならべた配列。たとえば otherItems が4個なら {0, 0, 0, 0} |
実数型-∞ |
実数型 は小数も入る箱(整数型 は整数だけ)。-∞ は負の無限大で、どんな数よりも小さいと決められた値です。最大を探すときの出発点に使うと、1回目の比較が必ず成り立ちます |
文字列型の配列: order |
値を入れずに宣言だけしている行。中身がまだ無い状態を未定義といいます |
for (orderにordersの要素を順に代入する)endfor |
配列の要素を1つずつ取り出すくり返し。orders の1本目を order に入れて中を実行し、次に2本目を入れて…と6回くり返します。くり返しの1回ぶんを、この記事では「1周」と呼びます |
for (iを1からotherItemsの要素数まで1ずつ増やす) |
回数を数えるほうのくり返し。i に 1, 2, 3, 4 と順に入り、両端を含みます(otherItems が4個なら 4周) |
if (…)endif |
かっこの中が成り立つときだけ endif までを実行する。if の中に if を書けるので、そのときは外側が成り立ったうえで内側も成り立ったときだけ、いちばん内側が実行されます |
orderのいずれかの要素の値がitemの値と等しい |
その配列のどこか1つでも等しければ成り立つ。order が {"A", "B", "D"} で item が "A" なら、1番目が等しいので成り立ちます |
ordersに含まれる文字列を重複なく辞書順に格納した配列allItemsの複製から値がitemである要素を除いた配列 |
辞書順=辞書の見出しの順(英字なら A・B・C…)。重複なくなので、同じ商品は1回しか入りません。複製=元の配列をそのままコピーしたもの ── コピーのほうから除くので、allItems は減りません。除いたあとも、残りの並びは辞書順のままです |
otherItems[i] |
配列 otherItems の i 番目の枡。角かっこの中を添字といい、i のような箱の名前も書けます(i が 2 なら otherItems[2])。この試験では 1 から数えます |
◯の値を1増やす |
◯ ← ◯ + 1 と同じこと。いまの中身に 1 を足して、同じ場所に入れ直します |
(a × b) ÷ (c × d)/* 実数として計算する */ // 注釈 |
× がかけ算、÷ がわり算。丸かっこの中が先に計算されます。注釈は人が読むための書き込みで、それ自体は何も実行しません |
記号でつまずいたら、下の教科書から、先にそこだけ読んでください(G9-2 のような番号は、当サイトの擬似言語の教科書の第何回かを表します)。
for の中に for があるとき、何回まわるかG5選択処理 if ─ if の中の if は「両方そろったとき」G10大域変数と局所変数 ─ 大域: と書かれた箱は何が違うのか式とプログラムを重ねて、席を読む
表の例で putRelatedItem("A") を呼びます ── A といっしょに買われやすい商品を探す(式の x が A、y が相手の商品)。関連度の式と、プログラムで関連度を計算している行を、上下に並べます。
問題文の式Lxy = (Mxy × 全注文数)/(Kx × Ky)
プログラムvalueL ← (arrayM[i] × c) ÷ (itemCount × arrayK[i])
÷ は左右を入れ替えられないので、左は分子・右は分母と決まっています(× の左右は入れ替えられるので、決め手になりません)。
名前が半分は教えてくれます ── arrayM は Mxy でしょう。でも K は式に2つある(Kx と Ky)ので、arrayK[i] がどちらなのかは名前では決まりません。
そこで、空欄になっていない行を使います。
〔プログラム〕itemCount を増やしている行
if (orderのいずれかの要素の値がitemの値と等しい)
itemCountの値を1増やす
endif
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
「この注文に item(A)が入っていたら1増やす」を6件ぜんぶくり返すので、itemCount は A が入っていた注文の件数=Kx(表の例では注文1・2・3・4 の 4件)。
分母の片方が決まったので、残った arrayK[i] が Ky。分子も同じで、arrayM[i] が Mxy なら、残る c の席が全注文数です。
決め① a と b を決める
〔プログラム〕空欄 a と b があるかたまり
for (iを1からotherItemsの要素数まで1ずつ増やす)
if (orderのいずれかの要素の値がotherItems[i]の値と等しい)
if (orderのいずれかの要素の値がitemの値と等しい)
a の値を1増やす
endif
b の値を1増やす
endif
endfor
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
if が二重になっています。外側は「相手の商品が入っているか」、内側は「そのうえで item(A)も入っているか」。
くぐった条件の数が、数える注文の範囲を決める
b の行 外側だけくぐれば実行される → 相手の商品が入っている注文の数 = Ky
a の行 内側までくぐらないと実行されない → 両方が入っている注文の数 = Mxy
内側にあるほど条件が多い=数える注文が少なくなります。
相手の商品を "B" に決めて、注文を6件ぜんぶ通します。
注文6件を、相手の商品 "B" で通す
| 注文 | 中身 | くぐった if |
B を含む | 両方を含む |
|---|---|---|---|---|
| 1 | {“A”,”B”,”D”} | 外側○ 内側○ | 1 | 1 |
| 2 | {“A”,”D”} | 外側× | 1 | 1 |
| 3 | {“A”} | 外側× | 1 | 1 |
| 4 | {“A”,”B”,”E”} | 外側○ 内側○ | 2 | 2 |
| 5 | {“B”} | 外側○ 内側× | 3 | 2 |
| 6 | {“C”,”E”} | 外側× | 3 | 2 |
緑=その注文で増えた数。外側×=B が入っていないので、外側の if で止まる。注文5 だけ、増えたのが片方だけです ── B は入っているので外側は通りますが、A が入っていないので内側を通りません。
B を含む注文は 3件、両方を含む注文は 2件。問題文の KB = 3、MAB = 2 と一致します。
決め① の答え
a = arrayM[i] 内側=両方が入っている注文
b = arrayK[i] 外側=相手の商品が入っている注文
決め② c を決める
〔プログラム〕空欄 c の行
valueL ← (arrayM[i] × c) ÷ (itemCount × arrayK[i])
※ 上の〔プログラム〕からの抜粋です(1字も変えていません)。
c に入るのは全注文数、つまり注文が何件あるかです。注文をならべた配列は、問題文が「注文データは注文の配列で表している」と言っているとおり orders ── 表の例では 6本です。
「◯の要素数」は、何を数えた数か
ordersの要素数 注文の数 → 6
allItemsの要素数 商品の数(A・B・C・D・E) → 5
otherItemsの要素数 商品の数(A を除いた4つ) → 4
まちがえやすいのは下の2つです。
決め② の答え
c = ordersの要素数。表の例では 6 で、問題文の「全注文数が6」と一致します。
本番の手順 ── 空欄の数ではなく、決めることの数を先に数える
- 解答群の軸を数える。選択肢が6つでも、たいてい「2通り × 3通り」のような組合せです。軸の数だけ決めれば終わりで、6通りを試す必要はありません。今回は「入れ替えの2通り」と「要素数の3通り」でした。
- 式と計算行を、名前で重ねる。空欄になっていない部分から、箱の役どころを埋めていきます。
itemCountのように増やす行が空欄でない箱は、読むだけで何を数えているか決まります。 - 数える行が、どの
ifの内側にあるかを見る。内側にあるほど条件が多い=数える注文が少なくなる。 - 問題文が示している具体値で検算する。問題文が例の答えを書いてくれていることがあります(今回は
LAB= 1.0)。導いた組合せを入れて、その値が出るかを見れば答え合わせになります。
ここで解答群の中身を見る
a = arrayM[i]、b = arrayK[i]、c = ordersの要素数。解答群でこの組合せは オ ひとつだけです。
答え合わせ ── 動かすと D,1.5 が出る
正解は オ です。
まず、装置を押す前に手で検算します。c に ordersの要素数 を入れて、i = 1(相手は B)のぶんを計算します。
valueL ← (arrayM[1] × ordersの要素数) ÷ (itemCount × arrayK[1])
= ( 2 × 6 ) ÷ ( 4 × 3 ) = 1.0
※ 擬似言語の文ではなく、計算です。上段は〔プログラム〕の行を当サイトで i = 1 に書き換えたもの、下段は値をあてはめたものです。
問題文の「LAB は 1.0 である」と一致しました。あとは、はじめから最後まで通します。前半(数える)はざっと、後半(計算する)はじっくり見てください。
1行ずつ追う putRelatedItem(“A”)
出典:令和6年度 科目B 公開問題 問5(空欄には オ を入れてあります)
ループ開始前
配列
いま計算していること
変数の状態
出力されるもの
トレース表(進めると1行ずつ積み上がります)
正解は オ
出力は D,1.5。A といちばん関連度が高いのは、いっしょに買われた回数が同じ B ではなく D でした。
ほかの5つは、どれも別の値になります。下の値は、当サイトで6通りとも実際に回して求めたものです。
| 選択肢 | 後半1周目に出る値 ( LAB のはず) |
何が起きているか |
|---|---|---|
ア・イ・ウ a と b が入れ替わっている3つ |
1.875 2.25 1.5 |
入れ替わると arrayK が「両方の注文数」になります。arrayK はわるほうにいるので、A といっしょに買われたことが一度も無い C(arrayK[2] が 0)で 0 でわることになり、そこから先が出せません。1周目の値までは出ますが、2周目で止まります |
エ arrayM[i]/arrayK[i]/allItemsの要素数 |
0.8333… | a・b は正しいのに、全注文数を allItems の 5(商品の数)にしています。最後まで動きますが、関連度が全部小さめになり、出力は D,1.25 です |
オ arrayM[i]/arrayK[i]/ordersの要素数 |
1.0 | 問題文の LAB = 1.0 と一致。これが正解です |
カ arrayM[i]/arrayK[i]/otherItemsの要素数 |
0.6667… | 同じく c だけ違い、otherItems の 4(A を除いた商品の数)にしています。出力は D,1.0 |
次に読む
この問題でどこに手間取ったかで、行き先が変わります。
for の中の for が何回まわるか、つかめなかった人へ問4令和6年度 科目B 問4 ── 同じ年度の1つ前の問題を解きたい人へ一覧科目B 全44問の解説 ── 続けて解きたい人へこの記事で引用した資料
いずれも独立行政法人情報処理推進機構(IPA)が公表したものです。IPAは公表済みの試験問題について、教育目的での使用に許諾および使用料を不要としていますが、著作権は放棄していません。本記事では問題文を改変せずに引用しています。
「解答群6つそれぞれで、1周目にどんな値が出るか」の表は、当サイトで〔プログラム〕を6通りとも実際に回して求めたものです。

