
武蔵野個別指導塾の特徴
高校情報I 二重ループ—外側ごとの処理を表にして組と回数を確かめる|武蔵野個別指導塾

係の組を表に並べ、内側の回数の変化と重複を除く条件を考える鍵は二重ループです。
よくあるのは、内側の繰り返し回数が外側の値によって変わるのに、同じ回数だと思って掛け算する場面です。係を2人ずつ組にするなら、同じ2人の順番を入れ替えたものを別の組に数えるかも、先に決める必要があります。表で外側ごとの仕事を区切ると、回数と重複の両方を確かめられます。
組を数える前に決めること
二重ループは、ある繰り返しの中に別の繰り返しを置く方法です。問われ方には、出力される組を順に書く型、内側の処理の回数を数える型、同じ組を除くように範囲を直す型があります。いずれも、まず何を1回と数えるかを決めます。内側の本体に入った回数と、条件に合って組を出力した回数は、同じとは限りません。
例えば、受付係と記録係をそれぞれ別の名簿から選ぶなら、役割が違うので「受付がア、記録がイ」と「受付がイ、記録がア」は別の割当です。一方、同じ名簿から役割の区別のない2人を選ぶなら、この2つは同じ組です。さらに、1人を2回選んだ組を許すか、同じ名前が名簿に現れるかを指定しなければ、正しい答えは決まりません。
以下では、配列の位置を0から数えます。Pythonの range(a, b) は a から始まり b の直前までです。終点そのものは含みません。空の範囲なら内側の本体は0回で、外側の次の値へ進みます。例題の入力は自作した係の名簿で、実在する集計ではありません。
外側の1回に内側を一巡させる
外側の変数を固定してから、内側の変数が取り得る値を順にたどります。内側が終わって初めて外側が次の値に進み、内側はその外側の値に対する範囲の先頭から始まります。内側の開始位置を外側の変数で決める場合は、一巡する範囲も外側ごとに変わります。
図1のように、独立した範囲では外側がどの値でも内側は0、1、2の3回です。外側が0、1の2回なら、内側の本体は3回と3回で合計6回です。外側に依存して内側を外側の次から2までにすると、外側0では1、2の2回、外側1では2の1回、外側2では0回です。こちらは2、1、0を足して3回です。

掛け算が使えるのは、各外側の回で内側の本体が同じ回数だけ動く場合です。回数が変わるときは、行ごとの回数を合計します。出力の個数を求めるときは、内側の本体の中にさらに条件があるかにも注意します。条件を調べた回数と条件を通過した回数は別の列に記録できます。
表の行と列は、二次元配列の添字にも対応します。行の番号を外側、列の番号を内側で回せば各マスに順に届きます。ただし、組の列挙に二次元配列は必須ではありません。ここでは表を、実際に訪れる位置を見落とさないための道具として使います。
基本例題:役割の違う2つの名簿
受付係はア、イの2人、記録係はウ、エ、オの3人です。両方の名簿は重ならず、それぞれの順序を保って受付係を先、記録係を後にした組を出力してください。同じ人を2役にする問題ではありません。出力の形はPythonの組の一覧とし、何組あるかを整数で答えます。
手で受付係をアに固定すると、記録係はウ、エ、オの3通りです。イについても同じ3通りです。図2のように2行それぞれに3マスがあり、合計6組です。図の各マスの左側は受付係、右側は記録係を表します。

reception = ["ア", "イ"]
record = ["ウ", "エ", "オ"]
pairs = []
for first in reception:
for second in record:
pairs.append((first, second))
print(pairs)
print(len(pairs))
最初の2行は名簿を作り、3行目は結果を入れる空の一覧を用意します。外側の for は受付係を先頭から選び、内側の for はその人を固定したまま記録係を先頭から選びます。append は現在の2人を組にして末尾へ追加します。最後の2行は一覧とその長さを表示します。
外側がアのとき、一覧は順に (ア, ウ)、(ア, エ)、(ア, オ) を蓄えます。外側がイになると、内側は記録係のウから始まり、さらに3組を追加します。内側の変数を前回の末尾から続けるわけではありません。Python 3.14.8で上の入力を実行した結果は次のとおりで、手で作った6マスと一致します。
[('ア', 'ウ'), ('ア', 'エ'), ('ア', 'オ'), ('イ', 'ウ'), ('イ', 'エ'), ('イ', 'オ')]
6
どちらかの名簿が空なら、組は空の一覧で個数は0です。受付係が空なら外側の本体へ入らず、記録係が空なら外側に入っても追加が起きません。各名簿が1人なら組は1つです。この違いを処理表に書くと、0件の理由まで説明できます。
同じ名簿から順番のない組を作る
今度は同じ名簿のカ、キ、ク、ケから、異なる2人を役割の区別なしに選びます。位置は順に0、1、2、3です。名前は重複しないものとし、結果は位置の小さい人を先にした組の一覧で答えます。たとえばカとキは1組であり、キとカを再び追加しません。
位置 i を先に固定し、j を i + 1 から名簿の末尾まで進めます。これで i < j が成り立つため、同じ位置同士も逆順も選ばれません。手で作ると、カからはキ、ク、ケの3組、キからはク、ケの2組、クからはケの1組、ケからは0組です。合計は6組です。
names = ["カ", "キ", "ク", "ケ"]
pairs = []
for i in range(len(names)):
for j in range(i + 1, len(names)):
pairs.append((names[i], names[j]))
print(pairs)
print(len(pairs))
names に名簿を入れ、pairs を空にします。外側は有効な位置を先頭から最後まで選びます。内側の終点は len(names) なので、実際の最後の位置はその1つ前です。2人の名前を取り出して追加し、終了後に一覧と個数を出します。最後の外側の位置3では range(4, 4) が空になり、追加はありません。
[('カ', 'キ'), ('カ', 'ク'), ('カ', 'ケ'), ('キ', 'ク'), ('キ', 'ケ'), ('ク', 'ケ')]
6
この一覧を元の名簿に戻すと、各組は異なる2人で、前の人の位置が後の人の位置より小さくなっています。任意の異なる2位置には小さい方と大きい方があるので、小さい方が外側になった回にその組が現れます。逆順になる回は j が i より大きいという条件で除かれます。したがって、列挙の漏れと重複を防ぐ理由を説明できます。
終了も確認します。外側は有限の名簿の位置を1回ずつ進み、内側も決まった有限の範囲を1回ずつ進みます。空名簿と1人の名簿では組は0件です。実行例が合ったことと、任意の有効な名簿で漏れがない理由を示すことは別の確認です。
発展:入力の不備を見つけてから組を作る
名簿を別の場所から受け取る場合は、名前が重複していると、異なる位置でも同じ名前の組が現れます。ここでは名簿を文字列のリストに限定し、各名前は空でなく、互いに異なるものとします。条件を満たさない入力には ValueError を出し、途中まで作った組を返しません。空リストは有効で、結果は空リストです。空白だけの名前を禁止する場合は別途条件が必要ですが、ここでは空文字列だけを禁止します。
def make_pairs(names):
if not isinstance(names, list):
raise ValueError("list required")
if any(not isinstance(name, str) or name == "" for name in names):
raise ValueError("nonempty strings required")
if len(set(names)) != len(names):
raise ValueError("duplicate name")
result = []
for i in range(len(names)):
for j in range(i + 1, len(names)):
result.append((names[i], names[j]))
return result
print(make_pairs(["サ", "シ", "ス"]))
最初の判定はリスト以外を受け取ったときに止めます。次の判定は各要素が文字列で空でないかを調べます。その後、集合にしたときの個数と元の個数を比べ、名前の重複を検出します。要素の型を先に検査するので、集合に入れられないリストなどが混じっても、意図した ValueError で知らせられます。検査が通ってから空の結果を用意し、位置の大小を使って組を追加し、完成した結果を返します。
サ、シ、スを与えた期待値はサとシ、サとス、シとスの3組です。Python 3.14.8の出力は次のとおりで、元の3人の間の組に戻しても不足はありません。
[('サ', 'シ'), ('サ', 'ス'), ('シ', 'ス')]
正常と不正を同じ4件ずつ試します。各行の期待と実際は一致しています。「例外」は ValueError を指します。期待値は組の一覧であり、件数だけを比べたものではありません。
入力 期待値と実際
[] []
['ソ'] []
['タ', 'チ'] [('タ', 'チ')]
['ツ', 'テ', 'ト'] [('ツ', 'テ'), ('ツ', 'ト'), ('テ', 'ト')]
None 例外
['ナ', 'ナ'] 例外
['ニ', ''] 例外
['ヌ', ['ネ']] 例外
空や1人は壊れた入力ではなく、作れる組がない正常な場合です。不正な4件はそれぞれ、容器の型、名前の重複、空文字、要素の型を確かめています。検査前に組作りを始めると途中の結果を誤って利用するおそれがあるため、返す前に入力全体を確かめます。
この関数は通信や外部の道具を使わず、有限のリストを処理します。外部の道具の失敗や時間切れを受け取る機能は設けていません。それらを呼び出す仕組みを加えるときは、失敗を知らせる方法と途中結果の扱いを先に決め、その仕組みについて正常時と失敗時を同数試します。例外を捕まえて空リストに置き換えると、有効な空名簿と不正入力の区別が消える点にも注意が必要です。
誤答を表へ戻して直す
4人の名簿カ、キ、ク、ケに対し、内側を毎回先頭から回し、同じ位置だけを除くとどうなるでしょうか。例えば i と j をともに0から3まで進め、i != j のとき追加する処理です。これは有限回で終わりますが、求める役割の区別のない組とは違います。
図3のように、位置0と1を選ぶマスと位置1と0を選ぶマスの両方が残ります。同じ位置の対角線を除いても、逆順の重複は消えません。各行で3回ずつ追加され、合計12組となります。

原因は「別の人」という条件と「同じ2人を1回だけ」という条件を一緒にしたことです。修正では j の開始を i + 1 にします。これによって右上側のマスだけを訪れ、行ごとの回数は3、2、1、0になります。先ほどの標準例題を再実行した6組が、図3の残す側と一致します。
別の誤りとして range(i, len(names)) では自分自身との組が残ります。最後の値を含むつもりで終点を len(names) - 1 にすると、末尾の人を含む組が抜けます。どちらも処理表の対角線と最後の列を確認すれば発見できます。回数だけでなく組の中身も点検するのは、間違った組が偶然同じ件数になる可能性があるためです。
練習:まず5問を自分でたどる
- 飾りの色は赤、青、黄、形は丸、四角です。色を外側、形を内側にして全組を列挙します。順序を保った組の一覧と個数を答えてください。色と形は別の役割です。
- 0から4までの位置を外側にし、内側は外側の位置の次から4まで進めます。内側の本体の回数を外側ごとに並べ、合計を整数で答えてください。
- ナ、ニ、ヌ、ネ、ノの5人から役割の区別なく異なる2人を選びます。名簿順で前の人を先にした全組と個数を答えてください。
- 位置0から3までの4人について、外側と内側をそれぞれ0から3まで回し、
i < jのとき出力します。内側の本体に入る回数と、出力の回数をそれぞれ整数で答えてください。 - 発展の
make_pairsに['ハ', 'ハ']と['ヒ']をそれぞれ渡します。返る一覧または例外の種類を答え、2つを同じ扱いにできない理由を説明してください。
練習の答えと確かめ方
[(赤, 丸), (赤, 四角), (青, 丸), (青, 四角), (黄, 丸), (黄, 四角)]の6組です。各色に2形が対応し、各マスの色と形が問題の候補に戻ります。色の行を飛ばさず、各行の内側を丸から始めます。- 外側の位置0、1、2、3、4に対し、4、3、2、1、0回で合計10回です。例えば位置3の相手は4のみで、位置4には後ろの位置がありません。毎回4回と数えると、存在しない後ろの位置を含めてしまいます。
[(ナ, ニ), (ナ, ヌ), (ナ, ネ), (ナ, ノ), (ニ, ヌ), (ニ, ネ), (ニ, ノ), (ヌ, ネ), (ヌ, ノ), (ネ, ノ)]の10組です。外側ごとの件数は4、3、2、1、0です。どの組も名簿内の異なる2人で、各人より後ろの人との組を照合すると漏れはありません。- 内側の本体には4回ずつ4行で16回入り、出力は3、2、1、0を足した6回です。
i < jの判定自体は16回行われます。出力された6組は位置の小さい方が先で、同じ位置と逆順は含まれません。 - 前者は
ValueError、後者は[]です。前者は同名の重複により名簿の条件を満たさず、後者は有効な1人の名簿ですが相手がいません。どちらも組を受け取れない点は似ていますが、前者は名簿を直す必要があります。
練習の計算はPython 3.14.8でも照合しました。問1は各色に2形がある6組、問2と問3はそれぞれ10、問4は本体16回と出力6回、問5は例外と空一覧でした。手で並べた組を名簿と照合した結果と一致します。
覚えておきたいこと
外側の値を固定して、内側をその範囲の先頭から終点の直前まで進めます。
内側の回数が変わるときは、外側ごとの回数を足します。
順番のない2人の組は、位置の大小で同一人と逆順を除けます。
内側の本体への到達回数と条件を満たす出力回数を区別します。
空や不正な入力の扱いも、組を作る前に決めます。
どこで手が止まるかを見分ける
回数が合わないときは、まず外側を1つ固定し、内側の開始と終了を声に出して説明してみてください。各行の回数を書ければ、その後の足し算と、そもそもの範囲の読み違いを分けて見直せます。個別指導で確かめるなら、最後の外側の値で内側が0回になる理由を、処理表と range の終点の両方から説明できるかを見ます。
組の数が合っていても、自分自身や逆順が混ざる場合があります。2つの位置を表の行と列に置き、対角線とその両側に何があるかを指してから、必要な側をコードに対応させてください。位置の条件を自分の言葉で言えれば、回数の式も列挙の結果も同じ表から確かめられます。
読んで終わりにするか、ここから変えるか。
武蔵境駅徒歩30秒、講師1人に生徒1人の完全1対1。授業のあいだ、先生は隣を離れません。いまの詰まりを、無料相談でそのまま話してください。
まず15分だけ電話で話したい方は、無料電話相談(15分)から。塾からお電話を差し上げます。
本気になった日が、入塾の日。
お子様の教育、迷ったら「丸投げ」してください。必ず、期待以上の結果で返します。武蔵野で育む、武蔵境で学ぶ。
電話は年中無休。365日、24時間、お電話可能。



