科目Bの出現回数を数える問題の解き方|カウント用の配列を追う
count[data[i]] って、配列の中に配列が入っていて、何を数えているのか分からない。
科目Bの問題で、角かっこが二重になった行を見た瞬間に手が止まったことはありませんか?
1つずつの配列なら読めるのに、添字の中にまた配列が入ると、急に頭の中がこんがらがる。そう感じている人は少なくないのではないでしょうか。
大丈夫です。この形は、値そのものを箱の番号として使って数えているだけです。仕組みが分かれば、むしろ読みやすい部類の問題になります。
私はエンジニアとして10年以上、Webシステムの開発や運用に携わってきました。アクセスログから時間帯ごとの件数を集計したり、問い合わせを種類ごとに数えたりする処理は、実務でも毎週のように書いています。
そうした集計のずれを調べるとき、私は数えるための箱を紙に並べ、1件ずつ数字を足していく方法で確かめています。この記事で紹介する追い方は、まさにその方法をそのまま試験向けにしたものです。
ここでは、出現回数を数える問題を、科目Bの解き方に合わせて5つの段階で紹介します。科目B全体の中でこうした問題がどんな位置にあるのかを先に知りたい人は、親記事から読むと流れがつかみやすくなります。
【関連記事】基本情報技術者試験の科目Bとは?擬似言語・アルゴリズム対策を初心者向けに解説
出現回数を数える問題は何を聞いているのか¶
最初に、問題文が何を求めているのかを整理しておきましょう。出現回数を数える処理が中心になる問題は、聞かれ方によっていくつかの型に分かれます。
よく見かける型を表にまとめました。
| 問われ方 | 具体的な聞かれ方 | 主に見る場所 |
|---|---|---|
| 結果を答える | 処理が終わったとき、カウント用の配列の中身はどうなるか | ループの中の足し算の行 |
| 空欄を埋める | 正しく数えられるように、空欄に入る添字や式はどれか | 角かっこの中の式 |
| 最も多い値を答える | いちばん多く出現した値はどれか | 数え終わった後の最大値探し |
| 並べ替えに使う | 数えた結果から整列された配列を作るとき、出力はどうなるか | 2つ目のループの範囲 |
どの型でも、根っこにある問いは同じです。この値は、カウント用の配列の何番目の箱に足されるのか。
問題文を読んだら、まずどの型なのかを決めましょう。型が決まれば、コードのどの行をていねいに読めばよいかも自然に決まります。
普通の件数カウントとの違い¶
条件に合うものを数えるだけなら、変数を1つ用意して1を足していけば済みます。合計・最大値・件数のような集計の基本形は、こちらの記事で整理しています。
【関連記事】擬似言語の合計・最大値・件数の求め方|科目Bで頻出の集計パターンを解説
出現回数の問題が違うのは、数える対象が1種類ではなく、値ごとに別々に数える点です。1が何回、2が何回、3が何回と、値の種類だけ数える箱が要ります。
変数を値の種類ぶん用意するのは大変なので、配列をまとめて箱として使います。そして、値そのものを箱の番号、つまり添字として使うのがこの問題の正体です。
まず目を付ける場所¶
問われ方が分かったら、次はコードのどこから読むかを決めます。出現回数の問題では、目を付ける場所が3つあります。
ひとつ目はカウント用の配列の宣言と要素数、ふたつ目は角かっこの中の式、みっつ目は数え終わった後の処理です。この順番で見ていくと、何を数えているのかがはっきりします。
カウント用の配列の要素数を確かめる¶
最初に見るのは、カウント用の配列がいくつの箱を持っているかです。数える値が1から6までなら、箱は6個必要になります。
科目Bの擬似言語では、配列の要素番号は1から始まるのが基本です。値の範囲と箱の番号の範囲がそろっているかどうかを、宣言の時点で確かめておきましょう。
ここで、値が0から始まる場合は注意が必要です。0番目の箱は無いので、値に1を足してから添字に使うといった工夫がコードに入ります。
1から始まる要素番号の数え方に不安がある場合は、先にこちらの記事で確かめておくと安心です。
【関連記事】科目Bの配列問題の解き方|添字を表にして追う方法
角かっこの中を内側から読む¶
次に見るのは、count[data[i]] のような二重の角かっこです。こうした式は、必ず内側から読みます。
まず i の値を確かめ、次に data[i] の値を取り出します。その値が、外側の count の何番目の箱を指すのかを最後に決める、という順番です。
たとえば i が 3 で、data[3] が 5 なら、count[data[3]] は count[5] のことになります。1段ずつ置き換えるだけなので、慣れてしまえば迷いません。
例題:サイコロの目を数える¶
ここからは、実際の例題で追い方を確かめます。この記事の例題は説明のために作成したもので、IPA の公開問題そのものではありません。
次のプログラムは、サイコロを振った結果が入った配列 data から、1から6までの目がそれぞれ何回出たかを数えます。数え終わったら、最も多く出た目を変数 mode に入れます。
整数型の配列: data ← {3, 1, 4, 1, 5, 6, 5, 3, 5}
整数型の配列: count ← {0, 0, 0, 0, 0, 0}
整数型: i, mode
for (i を 1 から data の要素数 まで 1 ずつ増やす)
count[data[i]] ← count[data[i]] + 1
endfor
mode ← 1
for (i を 2 から 6 まで 1 ずつ増やす)
if (count[i] > count[mode])
mode ← i
endif
endfor
最初のループで数え、2つ目のループで最も多い目を探す、という2段構えになっています。前半が出現回数の数え方、後半が最大値探しの形です。
count の中身が最初はすべて 0 になっている点も見逃さないでください。数える箱は、空っぽの状態から始めるのが決まった形です。
2つ目のループは箱の番号で回す¶
2つ目のループの i は、data の何番目かではなく、count の何番目の箱か、つまりサイコロの目そのものを表しています。同じ i という名前でも、ループによって意味が変わる点に注意しましょう。
mode に入るのも、回数ではなく目の値です。いちばん多い回数を答えるのか、いちばん多く出た値を答えるのかは、選択肢で必ず狙われる分かれ道になります。
トレース表でカウント用の配列を追う¶
頭の中だけで数えようとすると、どの箱にいくつ入っているかをすぐに見失います。そこで、ループ1回につき1行を使うトレース表を書きます。
ポイントは、data[i] の値を書く列を用意し、その右にカウント用の配列の6つの箱を並べることです。その回で増えた箱だけを書き換えると、値が箱に振り分けられる様子が目で見えるようになります。
最初のループを、1回ずつ表にしました。
| i | data[i] | count[1] | count[2] | count[3] | count[4] | count[5] | count[6] |
|---|---|---|---|---|---|---|---|
| 開始前 | ― | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 3 | 0 | 0 | 1 | 0 | 0 | 0 |
| 2 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
| 3 | 4 | 1 | 0 | 1 | 1 | 0 | 0 |
| 4 | 1 | 2 | 0 | 1 | 1 | 0 | 0 |
| 5 | 5 | 2 | 0 | 1 | 1 | 1 | 0 |
| 6 | 6 | 2 | 0 | 1 | 1 | 1 | 1 |
| 7 | 5 | 2 | 0 | 1 | 1 | 2 | 1 |
| 8 | 3 | 2 | 0 | 2 | 1 | 2 | 1 |
| 9 | 5 | 2 | 0 | 2 | 1 | 3 | 1 |
最後の行を見ると、1が2回、2が0回、3が2回、4が1回、5が3回、6が1回です。すべての箱を足すと9になり、data の要素数と一致します。
この足し算の確認は、トレースのミスを見つけるいちばん手軽な方法です。合計が要素数と合わなければ、どこかで箱を取り違えています。
表の書き方そのものに慣れていない場合は、こちらの記事で基本の形を確かめてから例題に戻ってみてください。
【関連記事】擬似言語のトレース表の書き方|科目Bで変数の値を追う手順を解説
最も多い値を探す後半も表にする¶
続いて、2つ目のループで mode がどう変わるかを追います。比べる相手は、count[i] と、その時点で最も多い箱である count[mode] です。
| i | count[i] | count[mode] | 比較の結果 | mode |
|---|---|---|---|---|
| 開始前 | ― | ― | ― | 1 |
| 2 | 0 | 2 | 偽 | 1 |
| 3 | 2 | 2 | 偽 | 1 |
| 4 | 1 | 2 | 偽 | 1 |
| 5 | 3 | 2 | 真 | 5 |
| 6 | 1 | 3 | 偽 | 5 |
i が 3 のとき、count[3] と count[1] はどちらも 2 です。比較が > なので同じ回数では入れ替わらず、mode は 1 のまま残ります。
最終的に mode は 5 になり、最も多く出た目は5だと分かります。回数で答えるなら3、目で答えるなら5です。
間違いやすい選択肢の見分け方¶
出現回数の問題では、読み方の小さな取り違えを狙った選択肢が並びます。どんな誤りが用意されやすいのかを知っておけば、選択肢を見た瞬間に候補を絞れます。
よくある誤りを表にまとめました。
| 誤りの種類 | 起きること | 見分け方 |
|---|---|---|
| count[i] と count[data[i]] を取り違える | 何番目かを数えてしまい、全部の箱が1になる | 角かっこの中が値か位置かを確かめる |
| 値の範囲と箱の範囲がずれる | 0の値で存在しない箱を指す、最大の値が数えられない | 最小値と最大値で添字を計算してみる |
| 回数と値を取り違える | 最も多い目を聞かれているのに回数を答える | 最後に代入されるのが i か count[i] かを見る |
| > と ≥ を取り違える | 同じ回数のとき、小さい値と大きい値のどちらが残るかが逆になる | 回数が並ぶ箱どうしで比較を試す |
| 初期化を忘れる | 前の結果が残ったまま数え始める | ループの前に箱を0にしているか見る |
この中でも、特によく狙われるのが添字の取り違えと範囲のずれです。順番に見ていきましょう。
count[i] になっている選択肢¶
空欄補充では、count[data[i]] の部分を空欄にし、count[i] を選択肢に紛れ込ませる形がよく見られます。count[i] にすると、値ではなく位置で数えることになります。
その場合、1番目の要素も2番目の要素も、1回ずつ別の箱に足されるだけです。すべての箱が1になり、何も数えていない結果になります。
迷ったときは、data の中に同じ値が2回ある場面を1つ試してください。同じ箱に2回足されるのが、正しい出現回数の動きです。
値が0から始まるときの添字¶
数える値が 0 から 9 までの数字だったらどうなるでしょうか? 要素番号が1から始まる配列では、0番目の箱がありません。
そのため、count[data[i] + 1] のように、値に1を足して箱の番号に変える式が入ります。値の0は1番目の箱へ、値の9は10番目の箱へ入る、という対応です。
点数を10点刻みで数えるような問題では、score ÷ 10 + 1 のように割り算と組み合わさることもあります。どの値がどの箱に入るのかを、最小値と最大値の2つで試しておけば、範囲のずれはすぐに見抜けます。
数の割り算や余りの扱いで迷う場合は、整数どうしの割り算の考え方を確かめておくと、箱の番号の計算が楽になります。
【関連記事】擬似言語の余り(剰余)と割り算がわからない人へ|偶数判定と桁の取り出しを解説
数えた結果から並べ替える問題¶
数え終わった箱を番号の小さい順に見て、回数の分だけ値を書き出すと、整列された配列ができます。この方法は計数ソートとも呼ばれます。
例題の count を使うと、1を2回、3を2回、4を1回、5を3回、6を1回と書き出し、1, 1, 3, 3, 4, 5, 5, 5, 6 が得られます。比べて入れ替える整列とは、考え方がまったく違う点が面白いところです。
この形が出たら、外側のループが箱の番号、内側のループが回数を表しているかを確かめてください。どちらのループが何を回しているかが分かれば、出力はそのまま書けます。
Giji Academy のシミュレーターで動かす¶
紙のトレース表で箱の動きをつかんだら、最後は実際に動かして確かめましょう。出現回数の処理は、1件ごとに箱の数字が1つずつ増えていくので、動かして見る効果がとても大きい分野です。
Giji Academy の擬似言語シミュレーターでは、擬似言語のコードを1行ずつ実行し、変数や配列の中身が変わる様子を目で確かめられます。自分で書いたトレース表と見比べながら進めると、どの回で箱を取り違えたのかがすぐに分かります。
試すときは、この記事の例題を少しずつ変えてみるのがおすすめです。data の値を入れ替えたり、> を ≥ に書き換えたり、count[data[i]] を count[i] にしたりして、結果がどう変わるかを予想してから動かしてみてください。
予想と結果が一致すれば、その読み方は身についています。ずれた場合は、どの回でどの箱に足したのかを読み違えたのかを探すことが、いちばんの練習になります。
まずはGiji Academy の擬似言語シミュレーターで配列の講座を選び、箱の数字が1件ずつ増えていく様子を確かめてみてください。
まとめ¶
科目Bの出現回数を数える問題は、見た目ほど難しいものではありません。値そのものを箱の番号として使い、内側の角かっこから順に置き換えて読めば必ず解けます。
この記事でお伝えした5つの段階を振り返っておきましょう。
| 段階 | やること |
|---|---|
| 1 設問を読む | 中身・空欄・最も多い値・並べ替えのどれが問われているかを決める |
| 2 目を付ける | カウント用の配列の要素数と、角かっこの中の式を内側から読む |
| 3 トレースする | 1回1行で、増えた箱だけを書き換え、最後に合計を要素数と比べる |
| 4 選択肢を見分ける | 値と位置、回数と値、> と ≥、範囲のずれを確かめる |
| 5 動かす | 値や添字を変えたコードをシミュレーターで動かす |
最初は、この記事のサイコロの例のように、箱が6つほどの短いコードで十分です。箱を横に並べて1件ずつ足す練習を続けるうちに、二重の角かっこを見ても落ち着いて読めるようになります。
値ごとに数えて傾向をつかむ考え方は、科目Bだけでなく、実務でデータを集計するときにもそのまま役立つ力です。次に count[data[i]] のような行に出会ったら、まずは箱を横に並べるところから始めてみてください。