擬似言語の多重ループ(入れ子の繰返し)がわからない人へ|二重forの読み方を解説
for の中に for があると、もう何回まわるのか分からなくなる。
擬似言語の問題で、繰返しが二重になった瞬間に手が止まっていませんか?
一重の for なら落ち着いて追えるのに、内側にもう一つ入ると急に自信がなくなる。この感覚は、学習中の人にとってごく自然なものです。
私はエンジニアとして10年以上、Webシステムの開発や運用に携わってきました。二重ループの内側と外側を取り違えたせいで、一覧画面に同じ行が何度も並んでしまう不具合を直した経験もあります。
つまずくのは頭の良さの問題ではありません。数え方の順番を知らないだけです。
この記事では、多重ループの読み方を、回数の数え方とトレースの手順に分けて整理していきます。
多重ループとは、繰返しの中に繰返しがある形¶
はじめに、言葉の意味をはっきりさせておきましょう。繰返し処理の中に、さらに別の繰返し処理が入っている形を多重ループと呼びます。
二つ重なっていれば二重ループ、三つなら三重ループです。科目Bで実際に問われるのは、ほとんどが二重までだと考えて構いません。
イメージしやすいのは時計の針でしょうか。分針が一目盛り進むあいだに、秒針は文字盤を一周しています。
外側が一つ進むあいだに、内側は端から端まで走り切る。この関係が多重ループのすべてです。
内側と外側でまわる回数が変わる¶
ここが最初の山になります。外側と内側では、実行される回数がまったく違うからです。
短い例で確かめてみましょう。
○整数型: i, j
for (i を 1 から 3 まで 1 ずつ増やす)
for (j を 1 から 4 まで 1 ずつ増やす)
i と j を表示する
endfor
endfor
外側の for がまわるのは 3 回です。内側の for は、その 3 回それぞれで 4 回ずつまわります。
したがって表示される回数は 3 × 4 で 12 回になります。足し算ではなく掛け算になる、という点をまず押さえてください。
繰返しそのものの書き方に不安が残っている場合は、先にこちらで土台を固めておくと読みやすくなります。
【関連記事】擬似言語のwhileとforってどう違う?繰返し処理で迷子にならない方法を詳しく解説
変数を二つ使い分けるのが約束¶
多重ループでは、繰返しを数える変数を必ず別々に用意します。外側が i なら、内側は j というように分けます。
同じ変数を使い回してしまうと、内側の処理が外側の数え方を壊してしまいます。そのため試験のコードでも、この使い分けは必ず守られています。
名前は i、j、k の順に使われることが多いです。i が外側、j が内側という並びを見たら、それだけで構造が読み取れます。
問題を開いたら、宣言の行で変数がいくつあるかを先に確認してみてください。整数型の変数が二つ並んでいたら、二重ループを疑う合図になります。
二重ループの回数を数える手順¶
回数を正しく数えられるようになると、多重ループへの苦手意識はかなり薄れます。順番は決まっているので、その通りに数えるだけです。
先ほどのコードで、変数の値がどう動くかを表にしてみます。
| 外側 i | 内側 j の動き | この周での表示回数 |
|---|---|---|
| 1 | 1 → 2 → 3 → 4 | 4 回 |
| 2 | 1 → 2 → 3 → 4 | 4 回 |
| 3 | 1 → 2 → 3 → 4 | 4 回 |
| 合計 | ― | 12 回 |
注目したいのは、内側の j が毎回 1 に戻っている点です。外側が次に進むたびに、内側は最初からやり直します。
この巻き戻りに気づかないと、j が 1 から 12 まで増え続けるように読んでしまいます。実際には 1 から 4 を三度繰り返しているだけです。
内側の回数が外側の値によって変わる場合もあります。その場合は掛け算では求まらず、周ごとに足し合わせる必要があります。
○整数型: i, j, kaisu
kaisu ← 0
for (i を 1 から 4 まで 1 ずつ増やす)
for (j を 1 から i まで 1 ずつ増やす)
kaisu ← kaisu + 1
endfor
endfor
内側の終わりが i になっているところが肝心です。外側が 1 のときは 1 回、2 のときは 2 回と、内側の回数が増えていきます。
合計は 1 + 2 + 3 + 4 で 10 回です。三角形のように増えていく形は、整列の問題でよく顔を出します。
科目Bで出る二重ループの典型パターン¶
二重ループが登場する場面は、実はそれほど多くありません。パターンを知っておけば、コードを見た瞬間に見当がつくようになります。
代表的な三つを、見分け方と一緒にまとめます。
| パターン | 何をしている | 見分け方 |
|---|---|---|
| 二次元配列の走査 | 表のマスを全部見る | 配列の添字が二つ書かれている |
| 総当たりの比較 | 要素どうしを組み合わせる | 内側が i の次から始まる |
| 整列の入れ替え | 順番を並べ替える | 内側で交換の処理が起きる |
上の三つを頭に入れておくと、読む前に目的の見当がつきます。目的が分かっていれば、細部で迷っても方向を失いません。
二次元配列を全部見るパターン¶
もっとも素直なのがこの形です。行と列でできた表を、上から順に一マスずつ見ていきます。
外側が行、内側が列を担当します。この対応を決めてから読み始めると、途中で混乱しません。
○整数型の二次元配列: hyo ← {{1, 2, 3}, {4, 5, 6}}
○整数型: gyo, retsu, goukei
goukei ← 0
for (gyo を 1 から 2 まで 1 ずつ増やす)
for (retsu を 1 から 3 まで 1 ずつ増やす)
goukei ← goukei + hyo[gyo, retsu]
endfor
endfor
このコードは、六つの数をすべて足して 21 を求めています。合計を入れる変数を、二重ループの外で 0 にしている点にも注目してください。
もし goukei ← 0 が外側の for の中にあったら、行が変わるたびに合計が消えてしまいます。初期化をどこに置くかで結果が変わる、という感覚を持っておくと安全です。
二次元配列の添字の並びに不安がある場合は、こちらで確認しておきましょう。
【関連記事】擬似言語の配列と添字がわからない人へ|要素数・二次元配列の読み方を解説
総当たりで組み合わせを比べるパターン¶
もう一つよく出るのが、配列の要素同士を比べる形です。同じ値が二つ入っていないかを調べる処理などで使われます。
このとき内側の for は、1 からではなく i + 1 から始まります。同じ組み合わせを二度調べないための工夫です。
○整数型の配列: data ← {3, 7, 3, 9}
○整数型: i, j
○論理型: onaji
onaji ← false
for (i を 1 から dataの要素数 - 1 まで 1 ずつ増やす)
for (j を i + 1 から dataの要素数 まで 1 ずつ増やす)
if (data[i] が data[j] と等しい)
onaji ← true
endif
endfor
endfor
内側の始まりが i + 1 になっているので、比べる組み合わせは重複しません。data[1] と data[3] が同じ 3 なので、結果は true になります。
外側の終わりが要素数 - 1 で止まっているのも意味があります。最後の要素には、後ろに比べる相手がいないからです。
集計や判定の処理をひとまとめに整理しておきたい人は、こちらもあわせてどうぞ。
【関連記事】擬似言語の合計・最大値・件数の求め方|科目Bで頻出の集計パターンを解説
つまずきやすい場所を先に知っておく¶
仕組みが分かっても、本番で足をすくわれる箇所は決まっています。私が学習の相談を受けてきた範囲では、次の三つが目立ちました。
先に知っておけば避けられるものばかりです。表で確認しておきましょう。
| つまずく場所 | 何が起きるか | 対処 |
|---|---|---|
| 内側の変数が戻ることを忘れる | 回数を数え間違える | 外側が進むたび内側は 1 からと唱える |
| 初期化を内側に書いてしまう | 合計や件数が途中で消える | 変数を 0 にする行の位置を確認 |
| 添字の順番を入れ替える | 行と列が逆になる | 外側は行、内側は列と決めておく |
三つ目は特に見落としやすい落とし穴です。hyo[gyo, retsu] と hyo[retsu, gyo] は、まったく違うマスを指します。
選択肢の中に、添字の順番だけを入れ替えたものが混ぜてあることも珍しくありません。焦っているときほど、この違いは目に入らなくなります。
内側の変数を初期化する位置で答えが変わる¶
もう少し踏み込んで見ておきましょう。内側で使う変数を、外側の中で初期化する形も実際に出題されます。
行ごとの合計を求める処理が、その典型です。この場合は、行が変わるたびに合計を 0 に戻す必要があります。
○整数型の二次元配列: hyo ← {{1, 2, 3}, {4, 5, 6}}
○整数型: gyo, retsu, gyoukei
for (gyo を 1 から 2 まで 1 ずつ増やす)
gyoukei ← 0 /* 行ごとに戻す */
for (retsu を 1 から 3 まで 1 ずつ増やす)
gyoukei ← gyoukei + hyo[gyo, retsu]
endfor
gyoukei を表示する
endfor
先ほどの全体の合計とは、初期化の位置だけが違います。それだけで、答えは 21 という一つの数から、6 と 15 という二つの数に変わります。
つまり空欄補充では、この一行の位置がそのまま問われるわけです。何を求めたいのかを先に読み取れば、迷わず選べます。
空欄の判断基準を体系的に確認しておきたい人は、こちらが参考になります。
【関連記事】基本情報技術者試験の科目Bの空欄補充問題の解き方を解説|擬似言語で見る判断ポイント
途中で抜ける処理は内側だけを抜ける¶
探索の問題では、見つかった時点で繰返しを終わらせる書き方が出てきます。ここで注意したいのが、抜けるのは内側だけという点です。
内側の繰返しから抜けても、外側はそのまま次に進みます。二重ループの外まで一気に出るわけではありません。
そのため、外側も止めたいときは論理型の変数で印を立て、外側の条件でもその印を見る形が使われます。見た目は複雑ですが、やっていることは単純な伝言です。
処理を止める判断が絡む探索の読み方は、次の記事で詳しく扱っています。
【関連記事】擬似言語の線形探索と二分探索の違いとは?科目Bで差がつく探索の読み方
多重ループのトレースは内側をまとめて書く¶
二重ループを一行ずつ全部書き出すと、表が長くなりすぎて途中で力尽きます。私も紙で追うときは、内側をまとめて書く方法を使っています。
やり方はこうです。外側の一周を一行にして、その行の中に内側の変化を横に並べます。
| 外側 gyo | 内側の retsu と加算 | 行の終わりの gyoukei |
|---|---|---|
| 1 | 1 → 2 → 3(1, 2, 3 を加算) | 6 |
| 2 | 1 → 2 → 3(4, 5, 6 を加算) | 15 |
この形なら、外側が二周する処理でも二行で収まります。値の動きを見失わずに、全体の流れをつかめます。
内側の細かい動きを確かめたいのは、たいてい最初の一周だけです。一周目で仕組みが分かれば、残りは同じことの繰返しだと安心して飛ばせます。
トレース表の基本的な書き方をまだ固めていない人は、先にこちらを読んでおくと効きます。
【関連記事】擬似言語のトレース表の書き方|科目Bで変数の値を追う手順を解説
実際に動かして内側の巻き戻りを見る¶
多重ループは、読むだけだとどうしても実感が持てない分野です。内側の変数が 1 に戻る瞬間は、自分の目で見たときにいちばん納得できます。
まずは i を 1 から 3、j を 1 から 4 でまわす数行を動かして、値の並びを眺めてみてください。数分で終わりますし、一度見れば忘れません。
Giji Academy の擬似言語シミュレーターなら、変数の中身が変わっていく様子を追いながら処理を確かめられます。二重ループの内側が巻き戻る動きも、そのまま画面で見られます。
整列の処理は二重ループの代表例なので、動きを見たあとに読むと理解が進みます。
【関連記事】擬似言語の整列(ソート)がわからない人へ|選択ソートと交換処理の読み方を解説
まとめ¶
最後に、この記事で確認したことを振り返ります。
多重ループは、繰返しの中に繰返しが入っている形でした。外側が一つ進むあいだに、内側は端から端まで走り切ります。
回数は掛け算で求まります。内側の終わりが外側の変数になっている場合だけ、周ごとに足し合わせて数えてください。
典型パターンは、二次元配列の走査、総当たりの比較、整列の入れ替えの三つです。内側が i + 1 から始まっていたら、組み合わせを比べる処理だと見当がつきます。
つまずきの多くは、内側の変数が 1 に戻ることを忘れるか、初期化の位置を取り違えるかのどちらかです。合計を 0 にする行が内側か外側かで、答えは別物になります。
トレースするときは、外側の一周を一行にまとめると楽になります。細かく追うのは一周目だけで十分です。
なお、二重ループは書き写しの誤りが起きやすい場所でもあります。写し間違いを減らす確認の手順は、こちらにまとめてあります。
【関連記事】擬似言語でケアレスミスが多い人へ|よくある間違いと確認方法
for の中の for が読めるようになると、科目Bで手が止まる場面はぐっと減ります。今日の一問から、外側と内側に印を付けて読んでみてください。