ここからは、各問題について「考え方→設計→コード→落とし穴→発展」の順で掘り下げます。動かすだけでなく、なぜその形がよいのかを理解すると応用が効きます。
平均値を求める(問題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)で再現可能。
よくある落とし穴
- 文字列のまま比較:
guessがstrだと大小比較でエラー。 - 範囲外を許容: 当てやすさが下がる。ルールを明示して守る。
- 無限ループ時の脱出がない: このゲームは正解で必ず抜けるが、制限回数を設けるケースもあり。
発展
- 二分探索ヒント: 「あと±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や範囲チェックを挟むと安定する。 - 見た目も設計の一部: 出力整形はユーザー体験を大きく変える。
次に進むなら、これらを「関数化→テスト可能に→再利用」と段階的に育てると、プロっぽい書き方に近づきます。
