一覧へ

完全探索 — ブルートフォース

完全探索の原理と適用時期を理解し、順列/組み合わせを活用した実装パターンを学びます。

中級
|
10
|
検証済み (2026-07)
完全探索ブルートフォース順列組み合わせexhaustive search
進捗0/23 (0%)

完全探索 — Brute Force

このトピックを終えると

完全探索が適切な状況を判断できるようになり、繰り返し文、順列、組み合わせを活用して、すべてのケースを体系的に探索できるようになります。


最も確実な方法

パスワードが4桁の数字である場合、0000から9999まですべて試せば必ず見つかります。これが完全探索(Brute Force、Exhaustive Search)です。

python
# 4桁の数字パスワードを全件調査
for code in range(10000):
if check(code):
print(f"パスワード: {code:04d}")
break

10,000個。コンピューターにとっては一瞬です。

完全探索の核心: 「漏れがないので、必ず答えが得られる」。正確性が保証されることが最大の長所です。


いつ使うか

ケースの数が十分に少ないときに完全探索を使います。

text
1秒あたり約1億(10^8)回の演算が可能な場合:
  n ≤ 20     → 2^20 = 約100万 ✅
  n ≤ 10     → 10! = 約360万 ✅
  n ≤ 8      → 8! = 40,320 ✅
  n ≤ 25~30  → 2^30 = 約10億 ⚠️ タイムアウトの危険性

コーディングテストで入力のサイズが小さい場合(n ≤ 20程度)、まず完全探索を思いつくべきです。複雑なアルゴリズムを使う前に、「単純にすべて試せば良いのではないか」を最初に確認することです。


パターン1 — ネストされた繰り返し文

最も基本的な形です。

python
# 2つの数の合計がターゲットであるペアを見つける
def two_sum(nums, target):
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return None
print(two_sum([2, 7, 11, 15], 9)) # [0, 1]

すべてのペアを試します。O(n²)ですが、nが小さい場合は十分です。


パターン2 — 順列(Permutation)

順序が重要なすべてのケース:

python
from itertools import permutations
# [1, 2, 3]のすべての配列の順序
for p in permutations([1, 2, 3]):
print(p)
# (1, 2, 3)
# (1, 3, 2)
# (2, 1, 3)
# (2, 3, 1)
# (3, 1, 2)
# (3, 2, 1)

n個の順列はn!個です。3! = 6、5! = 120、10! = 3,628,800。

python
# 数字カードで作成できる最も大きい数
cards = [3, 1, 4]
max_num = 0
for p in permutations(cards):
num = int("".join(map(str, p)))
max_num = max(max_num, num)
print(max_num) # 431

パターン3 — 組み合わせ(Combination)

順序が関係ないすべてのケース:

python
from itertools import combinations
# 5人の中から3人を選ぶすべての方法
people = ["A", "B", "C", "D", "E"]
for team in combinations(people, 3):
print(team)
# ('A', 'B', 'C')
# ('A', 'B', 'D')
# ('A', 'B', 'E')
# ... 合計10個 (5C3 = 10)
python
# 与えられたメニューから2つを選んで、合計が最も安い組み合わせ
prices = {"ラーメン": 3500, "おにぎり": 2500, "トッポギ": 4000, "スンデ": 3000}
items = list(prices.items())
cheapest = float('inf')
best_combo = None
for combo in combinations(items, 2):
total = combo[0][1] + combo[1][1]
if total < cheapest:
cheapest = total
best_combo = (combo[0][0], combo[1][0])
print(f"{best_combo}: {cheapest}円") # ('おにぎり', 'スンデ'): 5500円

パターン4 — ビットマスク

各要素を「選択/非選択」に分ける部分集合の問題:

python
# n個の部分集合 = 2^n個
items = ["リンゴ", "バナナ", "チェリー"]
n = len(items)
for mask in range(1 << n): # 0 ~ 2^n - 1
subset = []
for i in range(n):
if mask & (1 << i):
subset.append(items[i])
print(subset)
# []
# ['リンゴ']
# ['バナナ']
# ['リンゴ', 'バナナ']
# ['チェリー']
# ['リンゴ', 'チェリー']
# ['バナナ', 'チェリー']
# ['リンゴ', 'バナナ', 'チェリー']

1 << nは2^nです。ビット1つが「この要素を含めるかどうか」を決定します。


完全探索から最適化へ

完全探索でまず正解を得た後、パフォーマンスが不足している場合に最適化します。

完全探索最適化手法
すべてのペア (O(n²))ハッシュマップ (O(n))
すべての部分和2ポインター、スライディングウィンドウ
すべてのパスDP(メモ化)
全件調査枝刈り(pruning)

コーディングテストの解法手順:

  1. 完全探索で正確な解法をまず作成
  2. タイムアウトになったら規則性を見つけて最適化
  3. 最適化された解法が完全探索と同じ答えを出すか検証

核心のまとめ

方法ケースの数使用ツール
ネストされた繰り返し文O(n^k)for文
順列n!itertools.permutations
組み合わせnCritertools.combinations
部分集合2^nビットマスク

バックトラッキング — 枝刈りが付いた完全探索

完全探索で「この方向は答えになり得ない」と判断した場合、さらに進まずに引き返す手法です。

python
# N-Queen問題:N×Nのチェス盤にN個のクイーンを互いに攻撃できないように配置
def solve_nqueens(n):
solutions = []
def backtrack(queens, row):
if row == n:
solutions.append(queens[:])
return
for col in range(n):
if is_safe(queens, row, col):
queens.append(col)
backtrack(queens, row + 1)
queens.pop() # 巻き戻し(backtrack)
def is_safe(queens, row, col):
for r, c in enumerate(queens):
if c == col or abs(r - row) == abs(c - col):
return False
return True
backtrack([], 0)
return solutions
print(len(solve_nqueens(8))) # 92個の解

8×8のチェス盤で可能なすべての配置は約43億個ですが、枝刈りによって実際に探索するケースの数は数千個レベルに減ります。


再帰 vs 繰り返し文の選択

python
# 再帰 — ツリー構造の探索に自然
def find_all_paths(graph, start, end, path=[]):
path = path + [start]
if start == end:
return [path]
paths = []
for node in graph[start]:
if node not in path:
paths.extend(find_all_paths(graph, node, end, path))
return paths
# 繰り返し文 — 単純な列挙に適している
for i in range(n):
for j in range(i + 1, n):
check(arr[i], arr[j])

ネストされた繰り返し文は深さが決まっている場合、再帰は深さが可変の場合に使用します。Pythonの再帰の深さの制限(デフォルト1000)に注意してください。



時間制限の感覚

コーディングテストで時間制限(通常1〜2秒)に合わせるには、演算回数を推測する必要があります。

text
Python基準(おおよその感覚):
  10^6 演算 → ~0.1秒
  10^7 演算 → ~1秒
  10^8 演算 → ~10秒(タイムアウト)
python
# n=10 → 2^10 = 1,024 → 完全探索可能
# n=20 → 2^20 = 1,048,576 → 可能だがやや厳しい
# n=30 → 2^30 = 1,073,741,824 → タイムアウト
# n! → n=10なら3,628,800、n=12なら479,001,600
nの範囲可能な複雑度手法
≤ 10O(n!)順列完全探索
≤ 20O(2^n)部分集合/ビットマスク
≤ 1,000O(n²)二重for文
≤ 100,000O(n log n)ソート + 二分探索
≤ 10,000,000O(n)線形探索、ハッシュ

入力のサイズを見て、どのような複雑度が通過するかをまず判断することで、完全探索を試すか、すぐに最適化するかを決定できます。


完全探索は「無茶な方法」ではありません。正確な答えを保証する基準線であり、より良いアルゴリズムの出発点です。

💬 質問・コメント

0件のコメント

ログインせずに投稿できます。ゲスト投稿は投稿者自身で編集・削除できません。

0/2000

読み込み中...