科目Bの2次元配列問題の解き方|行と列を表にして追う方法
2次元配列が出てくると、どっちが行でどっちが列なのか分からなくなる。
科目Bで m[i, j] のような書き方を見た瞬間、少し身構えてしまいませんか?
1次元の配列なら追えるのに、添字が2つになったとたんに頭の中が混ざってしまう。そう感じている人は多いのではないでしょうか。
安心してください。2次元配列の問題は、行と列を紙の上の表にしてしまえば、1次元の配列とほとんど同じ感覚で追えます。
私はエンジニアとして10年以上、Webシステムの開発や運用に携わってきました。実務でも、表形式のデータを扱う処理で行と列を取り違え、集計結果が縦と横で入れ替わってしまう不具合を何度も見てきました。
そうした不具合を防ぐいちばん確実な方法は、頭の中で考えずに、実際に表を描いてみることです。この記事では、その方法を科目Bの解き方に合わせて5つの段階で紹介します。
科目B全体の中で配列やアルゴリズムの問題がどんな位置にあるのかを先に確かめたい人は、親記事から読んでおくと流れがつかみやすくなります。
【関連記事】基本情報技術者試験の科目Bとは?擬似言語・アルゴリズム対策を初心者向けに解説
2次元配列の問題は何を聞いているのか¶
最初に、2次元配列の問題がどんな形で出てくるのかを押さえておきましょう。何を聞かれているのかが分かると、読む場所を絞り込めます。
2次元配列は、行と列を持つ表のようなデータです。擬似言語では、要素を指定するときに行番号と列番号の順にカンマで区切って書きます。
たとえば m[2, 3] は、2行目の3列目にある値を表します。この順番さえ覚えておけば、記号の読み方で迷うことはありません。
よく出る設問のパターン¶
2次元配列の問題で聞かれる内容は、大きく分けるといくつかの型に収まります。代表的なものを表にまとめました。
| 設問のパターン | 聞かれやすいこと | 注目する場所 |
|---|---|---|
| 行や列の合計を求める | 合計を入れる変数の値 | 合計の初期化と足す行 |
| 条件に合う要素を数える | 数えた結果の個数 | if の条件と数える行 |
| 表を回転・転置する | 入れ替えた後の配置 | 代入の左辺と右辺の添字 |
| 盤面やマス目を調べる | 隣のマスの値や判定結果 | 添字に 1 を足し引きする行 |
| 空欄に入る添字を選ぶ | [i, j] か [j, i] か | 外側と内側のループ変数 |
表のとおり、どのパターンでも最後に問われるのは、どの行のどの列を見ているか、という一点です。ここを外さなければ、2次元配列の問題は怖くありません。
問題文の説明を先に読む¶
2次元配列の問題では、コードの前に、配列が何を表しているかの説明が書かれていることがよくあります。行が生徒で列が科目、行が日付で列が商品、といった具合です。
この説明を読み飛ばすと、コードの添字が何を意味しているのか分からなくなります。コードに入る前に、行が何で列が何かを一言でメモしておきましょう。
まず目を付ける場所¶
問題文の意味がつかめたら、次はコードのどこから読むかを決めます。2次元配列の問題では、目を付ける場所がはっきりしています。
宣言で行数と列数を確かめる¶
最初に見るのは、配列の宣言と初期値です。どんな値が何行何列で入っているのかを確かめます。
初期値は、内側の波括弧ひとつが1行分を表します。{{3, 7, 2}, {5, 1, 8}} なら、1行目が 3, 7, 2、2行目が 5, 1, 8 の、2行3列の表です。
ここで、紙に小さな表を描いてしまいましょう。行番号と列番号も書き添えておくと、あとのトレースが格段に楽になります。
| 1列 | 2列 | 3列 | |
|---|---|---|---|
| 1行 | 3 | 7 | 2 |
| 2行 | 5 | 1 | 8 |
このように描いておけば、m[2, 3] がどこを指しているのかは一目で分かります。2行目の行を右へ3つ進んだ場所、つまり 8 です。
ループ変数がどちらを動かしているかを見る¶
次に見るのは、二重ループの外側と内側が、それぞれ行と列のどちらを動かしているかです。2次元配列の問題で迷う原因のほとんどは、ここにあります。
確かめ方は簡単です。配列の添字の1つ目に入っている変数が行を、2つ目に入っている変数が列を動かしています。
外側のループが行を動かしているなら、表を1行ずつ左から右へ読んでいく順番です。外側が列なら、1列ずつ上から下へ読んでいく順番になります。
二重ループそのものの動き方に不安がある場合は、文法を整理したこちらの記事で先に確認しておくと安心です。
【関連記事】擬似言語の多重ループ(入れ子の繰返し)がわからない人へ|二重forの読み方を解説
例題:基準以上の要素を行ごとに数える¶
ここからは、実際の例題を使って追い方を確かめます。次のコードは、2次元配列の各行について、基準の値以上の要素がいくつあるかを数え、その合計を返す関数です。
なお、この例題は記事のために作成したもので、IPA の公開問題そのものではありません。
○整数型: countAtLeast(整数型の二次元配列: m, 整数型: border)
整数型: count ← 0
整数型: i, j
for (i を 1 から m の行数 まで 1 ずつ増やす)
for (j を 1 から m の列数 まで 1 ずつ増やす)
if (m[i, j] ≧ border)
count ← count + 1
endif
endfor
endfor
return count
宣言を見ると、i が行、j が列を動かしていることが分かります。添字の1つ目が i、2つ目が j だからです。
外側のループが行なので、表を1行目の左端から右へ、それが終わったら2行目の左端から右へ、という順番で読んでいく処理です。
変数をトレース表で追う¶
さきほどの2行3列の表を m として、border に 5 を渡したときの動きを追ってみましょう。i と j の組み合わせを1行ずつ書いていくのがコツです。
トレース表は次のようになります。
| i | j | m[i, j] | m[i, j] ≧ 5 | count |
|---|---|---|---|---|
| 1 | 1 | 3 | 偽 | 0 |
| 1 | 2 | 7 | 真 | 1 |
| 1 | 3 | 2 | 偽 | 1 |
| 2 | 1 | 5 | 真 | 2 |
| 2 | 2 | 1 | 偽 | 2 |
| 2 | 3 | 8 | 真 | 3 |
最後に count の 3 が返ります。7、5、8 の3つが基準以上なので、表と見比べても合っています。
j が先に動くことを表で確かめる¶
この表で注目してほしいのは、i が 1 のまま j が 1、2、3 と動いていることです。内側のループが一周し終わってから、ようやく i が 2 に進みます。
頭の中だけで追うと、i と j を同時に増やしてしまう人が少なくありません。表に書けば、内側が先に回るという動きが目に見えるので、その間違いを防げます。
表の上に指でなぞる順番を書き込む¶
トレースに慣れないうちは、最初に描いた行列の表のマスに、読む順番の番号を書き込むのもおすすめです。1行目に 1、2、3、2行目に 4、5、6 と振っていきます。
番号を振ったマスを順に指でなぞりながら、条件に合うかどうかを確かめていけば、トレース表を作るのと同じ効果があります。本番の試験でも、メモ用紙に小さく描くだけで迷いがぐっと減ります。
1次元の配列で添字を表にして追う基本の方法は、こちらの記事で詳しく解説しています。2次元配列は、この方法を行と列の2方向に広げたものだと考えてください。
【関連記事】科目Bの配列問題の解き方|添字を表にして追う方法
間違いやすい選択肢の見分け方¶
2次元配列の問題では、正解のすぐ隣に、よく似た誤りの選択肢が並んでいることがよくあります。どんな誤りが用意されやすいのかを知っておくと、選択肢を見た瞬間に候補を絞れます。
よくある誤りを表にまとめました。
| 誤りの種類 | 選択肢の例 | 起きること |
|---|---|---|
| 行と列を逆にする | m[j, i] | 転置した表を見てしまう |
| ループの上限を取り違える | j を m の行数 まで | 列が足りない、またははみ出す |
| 初期化の場所を間違える | count ← 0 を外側のループの中に置く | 行ごとに値がリセットされる |
| 隣のマスの添字をずらし間違える | m[i + 1, j] と m[i, j + 1] | 下のマスと右のマスを取り違える |
この中でも、特によく狙われるのが最初の2つです。順番に見ていきましょう。
[i, j] と [j, i] の取り違え¶
行と列を逆にした選択肢は、2次元配列の問題でもっとも多い引っかけです。正方形の表では、どちらを選んでも同じ要素を一度ずつ見るので、合計を求める問題では答えが変わらないこともあります。
ところが、表の形が長方形のときや、回転や転置のように位置そのものが問われるときは、結果がまるで変わります。迷ったら、2行3列のような正方形でない小さな表で試してみてください。
行と列の数が違う表で試すと、m[j, i] は途中で存在しない位置を指してしまいます。そこで誤りだとすぐに気づけます。
ループの上限が行数か列数か¶
もうひとつ多いのが、ループの上限を取り違えた選択肢です。i を m の列数 まで、j を m の行数 まで、と書かれていると、一見それらしく見えてしまいます。
確かめ方は、さきほどと同じです。その変数が添字の1つ目に入っていれば上限は行数、2つ目に入っていれば上限は列数です。
ここでも、正方形でない表で考えるのが近道です。行数と列数が同じだと、上限を取り違えても動いてしまい、誤りに気づけません。
隣のマスを見る添字のずらし方¶
盤面やマス目の問題では、m[i + 1, j] や m[i, j - 1] のように、添字に 1 を足し引きして隣のマスを調べます。1つ目の添字をずらすと上下に、2つ目の添字をずらすと左右に動く、と覚えておきましょう。
ここでも、描いておいた表の上で指を動かしてみるのが確実です。1行目で上のマスを見ようとしたり、最終列で右のマスを見ようとしたりすると表の外に出てしまうので、そうした端の扱いを確かめる条件が近くに書かれていないかも合わせて見ておくと安心です。
初期化の位置で意味が変わる¶
count ← 0 を外側のループの中に置いた選択肢も、ときどき見かけます。この場合、行が変わるたびに数え直しになるので、最後に残るのは最終行の個数だけです。
ただし、これはいつも誤りとは限りません。問題が行ごとの個数を配列に入れる処理なら、外側のループの中で初期化するのが正解になります。
だからこそ、最初に設問が何を聞いているかを読むことが大切なのです。全体の合計なのか、行ごとの結果なのかで、正しい初期化の位置が決まります。
実務でも、集計の初期化をどこに置くかで結果がまったく変わる場面は珍しくありません。私も以前、月ごとの集計のつもりで書いた処理が、初期化の位置のせいで年間の累計を出していたことがあり、表で追い直してようやく原因に気づきました。
値がずれたときに、どこでずれたのかを探す手順は、こちらの記事にまとめています。
【関連記事】擬似言語の答えが合わないときの直し方|値がずれた場所の探し方
Giji Academy のシミュレーターで動かす¶
紙のトレースで動きをつかんだら、最後は実際に動かして確かめましょう。二重ループのように動きが多い処理ほど、動かして見る効果は大きくなります。
Giji Academy の擬似言語シミュレーターでは、擬似言語のコードを1行ずつ実行し、ループ変数や配列の値が変わる様子を目で確かめられます。i が固定されたまま j だけが進んでいく様子を一度見ておくと、二重ループの順番は二度と迷わなくなります。
手で作ったトレース表とシミュレーターの結果を見比べてみてください。一致すれば理解できている証拠で、ずれたときはそのずれた行が次に練習すべき場所です。
まずはGiji Academy の擬似言語シミュレーターで、配列や繰返しの講座を選び、二重ループで表をたどる処理を動かしてみてください。
まとめ¶
2次元配列の問題は、添字が2つあるだけで、追い方の基本は1次元の配列と変わりません。行と列を紙の表にしてしまえば、どこを見ているのかは一目で分かります。
この記事でお伝えした5つの段階を振り返っておきましょう。
| 段階 | やること |
|---|---|
| 1 設問を読む | 行と列が何を表すか、何を求めるかを確かめる |
| 2 目を付ける | 宣言から行数と列数を確かめ、表を描く |
| 3 トレースする | i と j の組み合わせを1行ずつ書いて追う |
| 4 選択肢を見分ける | 行と列の取り違え、上限、初期化の位置を確かめる |
| 5 動かす | シミュレーターで実際の動きと照らし合わせる |
最初は、2行3列くらいの小さな表で十分です。小さな表を何度か自分の手で追ううちに、大きな表が出ても落ち着いて読めるようになります。
行と列がこんがらがっても、表を描けば必ずほどけます。次に2次元配列の問題に出会ったら、まずはメモ用紙に小さな表を1つ描くところから始めてみてください。