Python | while文によるループ(繰り返し処理)

Python
スポンサーリンク

ここからは、各問題について「考え方→設計→コード→落とし穴→発展」の順で掘り下げます。動かすだけでなく、なぜその形がよいのかを理解すると応用が効きます。


平均値を求める(問題1)

考え方と設計

  • 目的: 0が来るまで数値を受け取り、入力された数の平均を出す。
  • データ設計: 合計と個数を別変数で管理し、終了時に合計/個数を計算。
  • 終了条件: ユーザーが0を入力したら入力終了。0自体は平均の対象外。

解答例(堅牢版)

total = 0
count = 0

while True:
    s = input("数値を入力(0で終了): ").strip()
    if not s or s == "-":  # 空入力などの簡易チェック
        print("数値を入力してください")
        continue
    try:
        n = int(s)
    except ValueError:
        print("整数で入力してください")
        continue

    if n == 0:
        break
    total += n
    count += 1

if count > 0:
    print("平均値:", total / count)
else:
    print("入力がありませんでした")
Python

深掘りポイント

  • ゼロ除算の回避: 入力ゼロ件のときは平均を計算せずメッセージを返す。
  • 入力検証: int()で落ちる例(空文字、記号、全角数字)をtry/exceptで吸収。
  • 浮動小数の扱い: Pythonの割り算は小数になる。整数が欲しい場合は丸め方を明示する(例: round(total / count, 2))。

よくある落とし穴

  • 0を平均に含めてしまう: 仕様上「終了合図」なので含めない。
  • 負数の扱い: 要件次第。平均に含めてよいかを決める(本問題では含めてOK)。
  • 空入力で例外: 入力検証がないとint("")でクラッシュ。

発展

  • 小数入力対応: float()に切り替え、出力を丸める。
  • 集計の関数化: 入力と計算を関数に分けるとテストしやすい。
  • 中央値・分散: リストに格納して統計値を追加計算。

数当てゲーム(問題2)

考え方と設計

  • 目的: 乱数の正解を当てるまでヒントを出す。
  • フィードバック: 大きすぎる/小さすぎるを即時返すとユーザーが絞り込める。
  • 制約: 入力は1〜100の整数、範囲外や不正入力はやり直し。

解答例(バリデーション+回数表示)

import random

secret = random.randint(1, 100)
tries = 0

while True:
    s = input("1〜100の数を当ててください: ").strip()
    try:
        guess = int(s)
    except ValueError:
        print("整数で入力してください")
        continue

    if not (1 <= guess <= 100):
        print("1〜100の範囲で入力してください")
        continue

    tries += 1

    if guess == secret:
        print(f"正解!試行回数: {tries}")
        break
    elif guess > secret:
        print("大きすぎます")
    else:
        print("小さすぎます")
Python

深掘りポイント

  • 状態管理: 試行回数triesは成功/失敗を含む全入力回数でカウント。
  • ユーザー体験: 範囲外はヒントにならないので即弾く。
  • 乱数の再現性: テスト時に固定値が必要ならrandom.seed(0)で再現可能。

よくある落とし穴

  • 文字列のまま比較: guessstrだと大小比較でエラー。
  • 範囲外を許容: 当てやすさが下がる。ルールを明示して守る。
  • 無限ループ時の脱出がない: このゲームは正解で必ず抜けるが、制限回数を設けるケースもあり。

発展

  • 二分探索ヒント: 「あと±10以内」など誤差ヒントで難易度調整。
  • 難易度設定: 範囲を選べるようにする(1〜50、1〜1000など)。
  • スコア保存: ベスト試行回数をファイル/JSONに蓄積。

素数判定(問題3)

考え方と設計

  • 定義: 素数は2以上で、1と自分以外で割り切れない整数。
  • 単純法: 2〜n-1で割り切れるかをチェック。
  • 最適化: 2と奇数だけ試す、上限は√nまでで十分。

解答例(効率版)

import math

s = input("数を入力してください: ").strip()
try:
    n = int(s)
except ValueError:
    print("整数で入力してください")
    raise SystemExit

def is_prime(n: int) -> bool:
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    limit = int(math.isqrt(n))  # √n の整数部分
    for i in range(3, limit + 1, 2):
        if n % i == 0:
            return False
    return True

print(f"{n}は素数です" if is_prime(n) else f"{n}は素数ではありません")
Python

深掘りポイント

  • √nまでの理由: もしnが合成数なら、最小の約数aに対応するbがあり、a ≤ √n。√nを超える最小約数は存在しない。
  • 偶数スキップ: 2以外の偶数は素数ではないため、ループを奇数に限定。
  • 大規模入力: この方法で数百万程度まで実用。より大きい範囲はエラトステネスの篩が有効。

よくある落とし穴

  • 1や負数の扱い: 素数ではない。先頭で弾く。
  • 0判定: 0は素数ではない。n % i == 0は真だが定義外。
  • 浮動小数入力: 整数に制限するか、丸めずに拒否する。

発展

  • 範囲内の素数列挙: エラトステネスの篩で高速化。
  • 素因数分解: 素数チェックの応用で最小約数から分解。
  • 確率的判定: 巨大数向けにMiller–Rabin(高度だが知っておくと役立つ)。

フィボナッチ数列(問題4)

考え方と設計

  • 定義: F1=1, F2=1, Fn = F(n-1) + F(n-2)。
  • 更新法: 直前2項を持ち、次項へ同時代入で進む。
  • 出力個数: 20項など固定回数ならfor rangeが簡単。

解答例(同時代入・整形あり)

a, b = 1, 1
result = []
for _ in range(20):
    result.append(a)
    a, b = b, a + b

print(", ".join(map(str, result)))
Python

深掘りポイント

  • 同時代入の安全性: a, b = b, a + bは右辺が先に評価され、左辺へ一斉に代入されるので、aの古い値が必要でも破壊されない。
  • 値の成長: 指数関数的に増える。大きい項数では表示桁が膨大になる。
  • 初期条件の違い: F0=0から始める流儀もある。要件に合わせて初期値を決める。

よくある落とし穴

  • 代入順序の破壊: a = b; b = a + bだとaの更新で情報が消える。必ず同時代入を。
  • ループ回数のズレ: 20項なのに21回出力するなどのオフバイワンに注意。
  • リスト未使用の出力: 逐次printはOKだが、後で整形したいなら一旦リストへ。

発展

  • メモ化/再帰: 再帰は教育的だがPythonではスタック制限に注意。メモ化で高速化。
  • 行列冪: 高度だが対数時間で計算可能。
  • 黄金比近似: Fn ≈ φ^n / √5。理論の橋渡しに。

九九表(問題5)

考え方と設計

  • 目的: 1〜9の掛け算表を行列で表示。
  • 手段: 二重ループで行(段)と列(掛ける数)を回す。
  • 整形: タブや固定幅で揃えると見やすい。

解答例(整形とヘッダ付き)

# ヘッダ行
print("   ", end="")
for j in range(1, 10):
    print(f"{j:>3}", end="")
print("\n" + "-" * 31)

# 本体
for i in range(1, 10):
    print(f"{i:>2}|", end="")     # 行ラベル
    for j in range(1, 10):
        print(f"{i * j:>3}", end="")
    print()
Python

深掘りポイント

  • 整形指定子>3: 3桁右寄せで列がそろう。10以上の表でも見やすくなる。
  • 役割分担: 外側ループが「行」、内側が「列」。視覚化すると理解が早い。
  • 性能: 9×9程度は問題にならない。大きくしてもO(n^2)で直感的。

よくある落とし穴

  • 改行忘れ: 行末のprint()を忘れると一行に詰まる。
  • スペース/タブの混在: タブ"\t"は環境で幅が変わるので桁がずれる。整形指定子の方が安定。
  • 範囲のオフバイワン: range(1, 10)で1〜9。range(10)だと0〜9になる。

発展

  • 可変サイズ表: N×Nを引数で生成する関数化。
  • 行列の見せ方: CSV出力、HTMLテーブルに整形。
  • 応用: 九九の練習プリントをランダムに生成。

総括と次の一歩

  • ループと条件分岐の設計が鍵: どこで終わるか、何を数えるか、どの入力を許すかを先に決めると迷わない。
  • 入力検証を癖にする: try/exceptや範囲チェックを挟むと安定する。
  • 見た目も設計の一部: 出力整形はユーザー体験を大きく変える。

次に進むなら、これらを「関数化→テスト可能に→再利用」と段階的に育てると、プロっぽい書き方に近づきます。

Python
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました