🎯 事前計算セット管理
# 各行・列・ボックスの使用済み数字 rows = [set() for _ in range(9)] cols =
[set() for _ in range(9)] boxes = [set() for _ in range(9)]
効果: 制約チェック時間をO(27) → O(1)に削減
📋 空セル事前収集
# 空セルを最初に全て収集 empty_cells = [] for i in range(9): for j in
range(9): if board[i][j] == '.': empty_cells.append((i, j))
効果: セル探索時間をO(81) → O(1)に削減
🔢 セット演算による高速計算
# 使用可能数字を一発計算 used = rows[row] | cols[col] | boxes[box_idx]
available = {'1','2','3','4','5','6','7','8','9'} - used
効果: 候補数字計算の大幅高速化
🔄 効率的バックトラッキング
# O(1)での追加・削除 rows[row].add(num) cols[col].add(num)
boxes[box_idx].add(num) # バックトラック時 rows[row].remove(num)
効果: 状態の高速更新と復元
ステップバイステップ解析
Step 1: 初期化フェーズ
# 時間: O(81) - 一度だけ実行 for i in range(9): for j in range(9): if
board[i][j] != '.': val = board[i][j] rows[i].add(val) # O(1) cols[j].add(val) #
O(1) boxes[get_box_index(i,j)].add(val) # O(1)
Step 2: メイン解法ループ
# 各空セルに対して for idx, (row, col) in enumerate(empty_cells): #
使用可能数字の計算: O(1) used = rows[row] | cols[col] | boxes[box_idx] available
= ALL_DIGITS - used # 各候補数字を試行 for num in available: # 最大9回 #
配置とバックトラック: O(1)
Step 3: バックトラッキング
# 高速な状態更新 # 配置時 rows[row].add(num) # O(1) cols[col].add(num) # O(1)
boxes[box_idx].add(num) # O(1) # 取り消し時 rows[row].remove(num) # O(1)
cols[col].remove(num) # O(1) boxes[box_idx].remove(num) # O(1)
従来手法 vs 最適化手法
従来手法の問題点:
• 毎回全ボード探索: O(81) × 空セル数
• 制約チェックで27回比較
• Time Limit Exceeded発生
最適化後の改善:
• 事前計算によるO(1)アクセス
• セット演算による高速制約チェック
• 大幅な実行時間短縮
具体的な改善数値:
- 制約チェック: 27倍高速化(O(27) → O(1))
- セル探索: 81倍高速化(O(81) → O(1))
- 総合実行時間: Time Limit Exceeded → 高速実行
- メモリ効率: 最小限の追加使用量