
2026/09/28 5:58
CP-SAT でコルンのパズルを解く
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
ルール
- 元の意味を正確に保持する(追加、省略なし)。
- ドキュメントの構造(見出し、箇条書きなど)を保持する。
- 技術用語は正確に保つ(API、LLM、zero-trust は自然な日本語の対義語が存在しない限りそのままにする)。
- トーンと確実性のレベルを維持する。
- まとめ、説明、改変は行わず、翻訳のみを行う。
出力形式
日本語翻訳:
(ここに日本語翻訳を記述)
翻訳対象となるテキスト:
(必要に応じて貼り付けてください;そうでなければ元のテキストを繰り返します):オリジナルサマリー
本文
3D プリンターパズルの自動解法:蛮力法から CP-SAT へ
🤖 AI に任せた結果、工业レベルの手法が適用された話
退屈していた金曜日の午後、誰かが 3D プリンターで作ってくれたパズルをいじっていました。
それは**溝が刻まれた白い「コブ(cobs)」**と、**3〜10 個の粒が集まった多数の「コーンピース」**で構成される物理的なパズルです。
- 目的: コーンピースを溝にスライドさせ、重ね合わさらずに隙間なくぴったり収めること。
- 状況: ソフトウェアエンジニアとして 2026 年まで生きながらえる私の短命な熱中ぶりを考えると、すぐに手動での解法が退屈しました。「これらを私がやる必要はない」と判断し、Claude に解決を委ねることにしました。
少しの手間をかけて形状説明を行いましたが、コンピュータビジョン技術の進歩もあって、Claude は Python スクリプトを作成して解法を提示してくれました。
そのコードは私が独自に書くよりも遥かに洗練されており、私自身が多くのことを学ぶことができました。
🔍 私が期待していたアプローチ:再帰的バックトラック
もし私がこの問題を独自で解こうとした場合、真っ先に思い浮かぶのは**再帰的バックトラック(recursive backtracking)**です。
プログラミング入門コースでは必ず触れるべき手法であり、本質的には以下のような蛮力法(brute force)の一種です。
- 試行錯誤: ランダムな手を試し、合法であれば次の手を試す。
- バックトラック: 行き詰まって合法な移動がなくなったら、直前の手を戻して再度挑戦する。これを繰り返す。
独自の実装でできる最適化
単に選択肢を網羅的に試すだけでなく、問題構造に基づく以下の工夫が可能です。
- 対称性の利用: コブ全体を回転させたとしても解が変わらない場合を考慮し、計算量を削減する。
- 早期失敗判定: 「孤立した 1 つの粒しかない空間」ができたり、「どのピースも覆えない状態」になったりしたら、即座に失敗と判断して探索を終了させる。
十分に吟味された再帰的バックトラックなら、この問題に対処できるでしょう。
⚙️ クラスメソッドの真実:車輪はすでに完成していた
Claude が提示したスクリプトの最初の行を見て、直ちに自分が愚かだったことに気づきました。
from ortools.sat.python import cp_model
バックトラックという単純な試行錯誤ではなく、工業用レベルのライブラリをそのまま利用していました。
Google が提供する OR-Tools CP-SAT は、制約付き最適化問題や充足可能性(satisfiability)問題に特化して設計されています。
本質:変数と制約によるモデル化
このアプローチの本質は、以下のステップです。
- モデル化: 問題を「変数の集合」と「変数が同時に満たすべき制約の集合」として表現する。
- 変数の定義: 伝統的にバイナリ(真偽)で表現され、最適化問題では目的関数も設定される。
- 解探索: 数十年にわたる研究知見を活用し、すべての制約を満たす効率的な変数の設定を見つける。
コーンパズルにおける「正確な被覆問題」
コーンパズルは**「正確な被覆問題(Exact Cover Problem)」**の一種です。
各ピースと配置をバイナリ変数 $V_{i,n}$ で定義します。
- $V_{i,n} = 1$: ピース $i$ が配置方法 $n$ で使用されている。
- $V_{i,n} = 0$: ピース $i$ が配置方法 $n$ で使用されていない。
🛑 制約の定義
制約クラス 1:各ピースは恰好いい回数だけ使われる
各ピース $i$ に対して、以下の式で記述します。
$$ \sum_{n=1}^{N_i} V_{i,n} = 1 $$
- 意味: ピース $V_{i,n}$ のうち正確に一つだけが 1 で、それ以外は 0 になることを保証します。
制約クラス 2:コブ上の各場所は恰好いい回数だけ覆われる
こちらは複雑に見えますが、単に面倒な作業です。
各ピースの配置について、そのピースが覆うコブ上のすべての空間を精査し、「その空間を覆うピースの集合 $C_s$」を作成します。
$$ \sum_{V_{i,n} \in C_s} V_{i,n} = 1 $$
- 意味: 「各ピースは一回だけ使用され、各場所も一回だけ覆われる」という条件が満たされます。
- 前提: パズルが適切に設計されている(ピースの総粒数とコブ上の空間数が等しい)ことを仮定します。これだけで必要な制約を網羅できます!
💻 クリーンなソルバー呼び出し
Claude のスクリプト自体は、すべてのピースの可能な位置を列挙する部分で複雑に見えますが、ソルバーを呼び出す部分は非常に簡潔です。
for plist in by_piece: m.Add(sum(v for v, _ in plist) == 1) for clist in by_cell: m.Add(sum(clist) == 1) # ... (モデル化の完了) ... s.solve(m)
まさに手作業で丁寧に仕込んだ CP-SAT の呼び出しです。
これにより、問題の探索空間を効率的に削減し、最善の解を短時間で発見することが可能になります。
📚 結論と教訓
今後、CP-SAT を必要とするような問題が発生したら、再び車輪を自作する代わりに以下を実践すべきです。
- 既存のソルバーを活用する: OR-Tools などの強力なライブラリを利用する。
- ドキュメントを参照する: 必要な追加機能を仕様書で確認し、正しく実装する。
🔗 リンク情報
- Claude との対話ログ: コーンパズルの解法について行った会話(Claude が書いた Python スクリプトを含む)
- 作成した Sudoku ソルバー: 私たちのチームが以前作成したスルードーソルバー
- 短講資料: 私は RC でこの話題についての短講を行いました;関連するスライド資料