世界を動かす技術を、日本語で。

多くの難しいLeetCode問題は簡単な制約問題である

概要

この記事では、 制約ソルバー (MiniZincなど)を使うことで、 アルゴリズム面接問題 を簡単に解決できる方法を解説します。 従来の 手書きアルゴリズム と比べて、制約ソルバーは 新しい制約の追加 や複雑な条件設定が容易。 Leetcodeのような最適化・満足化問題も、 数行の制約記述 で解決可能。 ただし、 実行速度や計算量 には注意が必要。 制約ソルバーの 実用性と柔軟性 について、具体例とともに紹介。

適材適所のツール選択:制約ソルバーの活用

  • アルゴリズム面接で頻出する 最適化問題 (例:コイン問題、最大利益問題)は、 制約ソルバー で簡潔に記述可能。
  • 例:コイン問題
    • コインの額面が [10, 9, 1] の場合、貪欲法は最適解を出せない。
    • MiniZincでは、 合計金額各コインの枚数 を変数として定義し、 合計が一致 するよう制約を設定。
    • solve minimize sum(coins);最小枚数 を自動計算。
  • 制約ソルバー は、Z3やOR-Toolsなど他にも多数存在。

制約ソルバーによる他の典型問題の解法

  • 株価最大利益問題
    • 配列pricesから 1回の売買で最大利益 を求める問題。
    • 制約例:sell > buyprofit > 0solve maximize profit;
  • 三数和満足化問題
    • リストから 3つの数を選び和または差が0 になるか判定。
    • 変数choices{0, -1, 1} を割り当て、合計0かつ 選択数が3 となるよう制約。
  • ヒストグラム最大長方形問題
    • 各棒の高さ配列numbersから 最大面積の長方形 を探索。
    • 変数x, dx, y開始位置・幅・高さ を表現し、全区間が高さy以上となるよう制約。

制約ソルバーの利点と限界

  • 新しい制約の追加仕様変更 が容易。
    • 例:株の売買回数や同時保有数を増やす場合も、 変数と制約を少し追加 するだけで対応可能。
  • 手書きアルゴリズム では困難な複雑条件も、 数行の制約 で記述可能。
  • 実行時間や計算量 は予測しにくく、 最適化されたアルゴリズム より遅い場合も多い。
  • それでも、 下手な手書き実装より高品質な解 を短時間で得られる。

教訓と実務での活用

  • Leetcode問題 は本来「制約最適化」問題であり、 制約ソルバー が得意とする分野。
  • 面接官が「計算量は?」と聞く場合は注意が必要だが、 柔軟な仕様変更新たな条件追加 には圧倒的に強い。
  • パズルや面接問題 だけでなく、実務の最適化・スケジューリングにも応用可能。
  • まとめ: 「適材適所のツール選択」 が最も重要。

補足:Leetcodeとは

  • Leetcode は「アルゴリズム面接問題」を多数掲載するサイト。
  • 実務とは直接関係しないが、 面接対策 として広く利用されている。

Hackerたちの意見

面接でこれを聞かれたら、制約ソルバーを使うんじゃなくて、制約ソルバーそのものを書くことを求められてる気がする。特定の制約問題のためにね、もっと一般的な制約ソルバーじゃなくて。

うん、そうでもあり、そうでもないかな。こういう質問を面接でしたことがあるけど、候補者が制約ソルバーを使おうとしたらプラスだと思う。実際のソフトウェアエンジニアリングでは全然使われてないから、これを使うことで候補者が時間を無駄にせずに正しい答えを早く得られるってことが分かる。もし制約ソルバーで答えたら、実際にコーディングできるか確認するために、ホワイトボードでフォローアップの質問をするかもしれないけど、制約ソルバーを答えとして出すのは全然悪くないよ。

これだね。NPのすべての問題は制約問題として表現できる。ソルバーが正しい解決策かどうかはアプリケーションによって大きく変わるし、面接ではほぼ定義上正しい解決策ではない。単純な動的プログラムと比べると、制約ソルバーはひどく遅いこともあるし、たいていそうだよ。

誰かが制約ソルバーを使ってLeetCodeの難しい問題を解いたのに、あなたがその人を雇わなかったら、マジでバカだよ。世の中で制約ソルバーが何か知ってる人がどれだけ少ないか、わかる?それを正しく問題に定義することができる人なんて、さらに少ないんだから。俺も大学の3年生の時に宿題で制約ソルバーを使ったことがあるけど、制約を書くのが本当に大変だった!

あなたの言う通りだけど、それがこの面接アプローチの根本的なバカバカしさを示してるよね。実際のエンジニアリングの状況では、これらの問題を100%解決できる。だって、コーヒーを飲んだり、論文を読んだり、教科書を見たり、緑の場所を散歩して考えたり、制約ソルバーみたいなツールを使ったりできるから。でも面接では、こういう問題を0%解決できる。俺の頭はそういう風には働かないから。少なくとも、そう思ってる。LeetCodeの面接を受けることは考えたこともないんだ。

これはいくつかの面接では当てはまるけど、全てではないよ。僕は一般的に面接でのleetcodeの使用には反対なんだけど、使われるところを見たときは、たいてい一つの理由だけなんだ。それは、機能不全な採用プロセスが知られているから。こういうプロセスでは、採用プロセスの参加者がその機能不全を認識しているけど、力がなかったり、もっと多くの場合は、適切にプロセスを改革するための組織が整っていないんだ。時には、半技術的なディレクターがHRを利用して、広範囲なチームで同じ質問をすることで面接技術を「標準化」しようとすることもある。他の時は、リソースが不足している小さなチームが急いでオンラインリソースから面接質問を集めていることもある。こういう場合、leetcode面接に賛成しない技術面接官とやり取りする可能性が高くて、標準化された面接スコアリングアプローチを「回避」して、革新的な候補者を見つけようとしているんだ。多くの場合、ソルバーに興味を示したり、少しでも知識があることが、君にとって大きなプラスになると思うよ。

いい視点だね。でも、残念ながら面接には当てはまらない。 >「これらの問題のポイントは、あなたの賢さを試すことだ。」 そうじゃなくて、12個くらいの特定のパターンを暗記するだけだよ。リスクが高すぎて、ほとんどの人は自分の問題解決能力を賭けることはない。LeetCodeは完全にゲーム化されてしまって、準備する意欲以外の差別化の役割を失ってしまった。

重要なのは、あなたが一般的なパターンを磨くためにどれだけ時間をかけたか、そしてコミュニケーション能力を試すことだよ。

「これらの問題のポイントは、あなたの賢さを試すことだ。」 いや、それは12個くらいの特定のパターンを暗記するだけだよ。リスクが高すぎて、ほとんどの人は自分の問題解決能力を賭けることはない。LeetCodeは完全にゲーム化されてしまって、準備する意欲以外の差別化の役割を失ってしまった。

問題解決の問題で面接をする時、候補者がどう考え、コミュニケーションし、問題を分解するかを理解するのが目的だよ。重要なのは、問題解決の質問には難易度や複雑さを段階的に上げたり下げたりする方法があって、全ての候補者が「勝ち」を得られるようにしないといけない。そうしないと、誰も「ダンクシュート」を決められない。面接官は、瞬時のひらめきからは何も学べないし、誰かが詰まってしまうことからもほとんど何も学べない。残念ながら、これがいいものを持てない理由なんだ。面接での問題解決の質問は非常に有用なツールになり得るけど、悲しいことに、あまりうまく使われていないんだよね。

これらの問題の目的は、あなたの賢さを試すことだよ。前回Metaで受けたときは、特定の問題セットを何度も繰り返し解かせて、考えずに再現できるかを試すためだった。面接官がいつも驚くのは、LeetCodeや彼らが勉強した面接ガイドのテキストブックアプローチ以外の答えを出すからだよ。賢さは、彼らが求めているもののリストにはあまり入ってないね。

これらの問題の目的は、あなたの賢さを試すことだよ。俺の経験では、面接官はLeetCodeの「トップインタビュー150」リストに行って、「配列」と「文字列」カテゴリーの問題を使うのが好きだね。俺が受けた仕事(主にバックエンドのPython)には、こういう問題はあまり好きじゃない。ほとんどが「この配列に対してO(n)のランタイム、O(1)のメモリアルゴリズムを教えて」っていう挑戦で、日常の仕事とは全然似てないから。Pythonではインプレースの配列アルゴリズムをあまりやらないんだ。そういう問題は、パフォーマンスが重要な他の言語(CやRustなど)でほとんど処理されるからね。PythonやJavaScriptなどの言語で面接をするなら、「ハッシュマップ」セクションに行ってほしいな。そっちの方が賢さよりも、適切なツールを使って問題を解決できるかどうかに重点が置かれてるから。難易度の調整にも問題があるし、問題169(過半数要素)がO(n)のランタイム、O(1)のメモリ解法で「簡単」とされてるのは、俺には笑える。1981年に最初に説明されたアルゴリズム(ボイヤー–ムーア過半数投票アルゴリズム)にはウィキペディアのページもあるし、実装や理解は難しくないけど、その正しさは考えないとわからない。そうなると、ウィキペディアに載るような「賢さ」を持ってるってことだよね。これが「簡単」な問題だなんて、ちょっと過剰だと思う。

ボトムアップの動的プログラミングアルゴリズムは、ちょっとした工夫が必要だね。リストにあるやつは全部、トップダウンの動的プログラミングアルゴリズムで解けるよ。つまり「再帰的な解法を書いて、キャッシュを使ってメモ化する」ってこと。中にはもっと賢い解法もあるけど、例えばコインチェンジ問題はA*探索で解く方がいいね。それでも、実際にこのアルゴリズムが必要になるプログラマーは少ないと思う。一番大事なのは、うっかり二次関数的なアルゴリズムを書いてしまったときに気づくことだよ。https://accidentallyquadratic.tumblr.com/ をざっと見た感じ、優秀な人たちでも有名なプロジェクトでそのミスを繰り返してるみたい。だから、テストでアルゴリズムを作れるからって、実際の現場でアルゴリズムのミスを見抜けるわけじゃないんだね。

それは賢さの測定じゃないよ。問題を分解して、一般的な方法を適用できるかどうかが大事なんだ。それが仕事の全てだよ。学べるスキルだし、個人的なバイアスから学ぶことを拒むのは、俺の中では赤信号だね。

ほとんどの面接は、糖尿病患者が自分の地下室でインスリンを合成できないなら、人生のゲームで何かを不正にやっているという前提に基づいている。妻の血糖値が高いときは、インスリンを打つ。制約問題を解決する必要があるなら、制約ソルバーを使えばいいじゃん。もしあなたの会社が制約解決ソフトを作って売ってないなら、なんでそのソフトが存在しないと仮定してゼロから作り出す必要があるの?

コーディングテストを擁護するなら、簡単な動的プログラミングの問題を解けない人は、実際にはかなりひどいプログラマーであることが多いよ。少なくとも、俺の経験ではそうだ。例外もいるとは思うけどね。

これは、危機的な状況でインスリンを合成できるかどうかをテストしてるわけじゃなくて、「来週までにこの教科書を詰め込んで、電話でその合成方法を説明できるか?」っていう一般的な適性テストなんだよね。

え?

ずーっと前、まだ若造だった頃、州立大学のコンピュータサイエンスプログラムで制約解決について教わらなかった時に、友達のアイデアを手伝おうとして問題に直面したことがある。彼はスポーツクラブのオーナーが選手を日ごとにスケジュールするアプリを作りたいと言ってたんだけど、簡単だと思って挑戦したら、全然うまくいかなかった。その時は、自分が何を知らないのかもわからなかったんだ。あの経験は、自分の傲慢さを思い知らされる教訓になってる。見積もりやタイムライン、期待について話すときにすごく役立ってるよ。

これ、ちょっとバカな質問かもしれないけど(制約ソルバーには詳しくないから)、線形最適化のアプローチの方が良いのかな?過去にスケジューリングに線形最適化を使ったことがあるけど、ルールの対立をうまく処理できるのがいいところだよね。ルールに重みを設定すれば、最適化ツールが「一番マシな」解決策を見つけてくれるから。

制約プログラミング言語の話が出ると、ハーカン・ケレルストランドを忘れちゃいけない。彼は素晴らしい問題と例のコレクションをまとめていて、MiniZinc用の問題もたくさんあるよ。彼のサイトで見てみてね: https://www.hakank.org/minizinc/

LeetCodeタイプの質問で一番の問題は、明確化の質問ができないことだよ。俺の頭は大抵の人とは違う働き方をするし、LeetCodeはある程度、人がLeetCodeタイプの答えを暗記することに頼ってる気がする。いくつかの問題には、実際に問題を理解するための文脈が十分にあるけど、他の問題では「質問/課題を理解する」ための情報が足りないんだ。だから、LeetCodeやAIの面接ステップを拒否し始めた。宿題や画面共有、1対1の面接はやるけど、上記のはやらない。大体、半分くらいは失敗するんだ。LeetCodeタイプのサイトで勉強するのは全然構わないけど、質問の説明や解答がちゃんとしてれば、もっと楽しいと思う。これだと挑戦の側面が薄れるけど、学ぶのはそれなしだと10倍難しい。スキルの問題じゃなくて、特定のタイプの問題を理解する能力がうまく働かないだけなんだ。追加情報や質問のチャンスがないと、まさに失敗するためのセッティングになってる。編集: 主にAI/自動化されたLeetCodeタイプの質問を面接前のスクリーニングとして使うことについて言ってる。もしこんなのを見たことがないなら、ラッキーだね。俺はそれを見すぎた。実際の面接で、リアルな人と話して明確化の質問ができるなら、比較的難しい質問でも全然大丈夫だよ。

俺が受けたやつは、コーディングよりもパズル解決能力のテストみたいに感じたな。でも一番ひどかったのは、面接に参加したときに初めて言われた問題があって、それをライブで見てるって言われたやつ。顔をずっとストリーミングしろって言われたやつもあったし(そういうのが平気な人もいるかもしれないけど)。別のページにタブを切り替えたらマイナス評価になるやつもあった。ドキュメントもなしで、ただググってるだけだと思われてるから。まあ、今は自分が準備してこういうことを予想するのが大事だね。

個人的に、leetcodeにはいくつか問題があると思う。1. 誰かにテストを受けさせることができる - 驚きだね。2. 誰かが小説を書く能力を、一文を読んで判断するようなものだよ。

これは解法を暗記することじゃないんだよね。確かにそれでかなり進めるけど、フォローアップがあると人はつまずく。だけど、もし暗記しててフォローアップにも答えられるなら、Leetcodeスタイルの問題には問題ないと思う。問題解決はパターンマッチングだから、知ってるパターンが多ければ多いほど、問題を解く能力が上がる。これは学べるスキルだし、今から身につけるのがいいよ。個人的には、面接で見たことのないLeetcodeスタイルの問題を解いたこともあるし、その中には動的プログラミングの問題もあった。最近は、GPTが多くの問題を解けるし、解法の良い説明もしてくれるから、学ぶのが得策だと思う。

Leetcodeタイプの質問で一番の問題は、明確化の質問ができないことだね。え?もちろんできるよ。Leetcodeで練習してるなら、質問できるディスカッションスレッドが各問題にあるから、いくらでも聞けるよ。面接中なら、面接官に聞けばいい。会話のはずなんだから。 > もしLeetcodeタイプのサイトで勉強するのが嫌じゃなければ、ちゃんとした説明があればいいのに。もし各問題の数百の無料説明が足りないなら、Leetcode Proにお金を払って、すべてを説明するエディトリアル回答にアクセスすることもできるよ。あるいは、ChatGPTを無料で使う手もあるし。 > スキルの問題じゃなくて、特定のタイプの問題を受け入れる能力がうまくいかないだけなんだ。失礼なことを言うつもりはないけど、100%スキルの問題だよ。それはいいニュースだね!努力すれば、俺がそうしたように、他の何千人もの人たちがそうしたように、学んで成長できるってことだ。 > 追加情報や質問のチャンスがないなら、文字通り失敗するためのセッティングだよ。そんな態度じゃ、失敗が確定だね!努力して諦めずにやれば、成功するよ。

LCの面接は、実際の仕事が遅くて苦痛で終わりのないジョギングなのに、練習後に100mをどれだけ速く走れるかをテストしてる感じだよね。でも、今はSMEGMA企業のトップの給料を狙うなら、そういうゲームをしなきゃいけないんだ。例えば、僕は自分で2Dゲームエンジンをゼロから作ったけど(サードパーティのライブラリは除く)、LCタイプの面接では複数のLCハードソリューションといくつかのバックフリップが必要だから、通らないだろうね。でも、それはそれで受け入れたよ。

100%。最近の面接プロセスで、課題を完璧にこなして(彼らが見た中で最高のものだった)、複数のエンジニアからポジティブなフィードバックをもらって、CEOにもすごく好かれたのに、CTOが「雰囲気コーディングパラノイア」のせいでサプライズのライブテストを与えてきて、結局11週間のプロセスで役割を得られなかった。ほんとに馬鹿げてる。これがデモ/持ち帰り課題だよ:https://github.com/rublev/monumental

「LeetCodeタイプの質問で一番の問題は、確認の質問ができないことだよね。」何を確認するの?LeetCodeの質問は大体明確だし、実際のプロジェクトよりずっとわかりやすいよ。入力のフォーマット、出力、各値の範囲が正確にわかるし、質問に加えて例もよくあるからね。期待されることは明確で、与えられた例の入力に対して、与えられた例の出力を返すことだけど、問題文のすべてのケースをカバーするように一般化する必要があるんだ。ボイラープレートも大体用意されてるしね。LeetCodeスタイルの質問が現実的でない理由の一つだと言えるかもしれないけど、現実の問題はしばしば不完全だったり間違ってたりして、ギャップを埋める必要があるからね。それに、実際の現場では確認のために質問できるとは限らない。「これを実装して」って言われて、「でもこの部分はどうするの?」って聞いても、「わからないし、知ってる人は締切前には戻ってこないから、頑張って」みたいな感じだよね。「コイン」の例は単純化されてるけど、実際の問題文はもっと詳細があるはずだし、この記事の著者はその詳細は記事には関係ないと思ったのかもしれないけど、テストを受ける人には関係あることだよね。

この投稿は、leetcodeの問題が面接に良いツールかどうかに関係なく、面白いと思う。制約ソルバーが役立つ問題の種類を紹介してるんだね。非線形最小二乗ソルバーについても、同じような投稿ができそうだな。

そうだね、特にソルバーの使い方を学ぶにはいいよね! > 「ほとんどの制約解決の例は、数独や「SEND + MORE = MONEY」みたいなパズルだよね。LeetCodeの問題を解く方が、もっと面白いデモになると思う。」彼の言う通り、制約プログラミングに関するチュートリアルはあまりないよね(前にちょっと触ったことがあるけど、ほぼ数独だったし)。練習用の問題がたくさんあるのは素晴らしいことだよ。

  • 制約ソルバー?いいアイデアだね、一度聞いたことある。でも、面接の目的なら、Pythonのコードを書こうよ。君の考え方を見たいんだ…(制約ソルバーを面接官に納得させるのはほぼ不可能だと思うけど、概念自体は素晴らしいよね)

import z3

制約ソルバーを使ったカレンダーのスケジューリングアプリを作ってるんだ。スケジュールの制約(時間帯の好みや要件、繰り返しルール)に基づいてイベントを自動的にスケジュールして、目標の進捗を追跡するんだ(進捗が遅れてる?通知が来るよ)。それに、食事プランナーでもあって、数千の健康的なレシピから、賞味期限が近い食材を再利用する食事プランを作成したり、パントリーをモデル化したり、食材の価格を見積もったり、栄養目標を達成するようにしてる。制約ソルバーはまさに黒魔術だね。

ちょっと話がそれるけど、貪欲法や動的計画法についてあまり知らないんだ。興味が湧いてきた。今回の会話はすごく参考になったし、頭の中がすごくクリアになったよ。