セグメント木の理屈を学ぶ(1) データ構造とモノイド構造の整合性 の続きです。
前回はセグメント木のデータ構造と操作を定義し、それが期待通りに動くことを証明しました。
今回は、前回「天下り的に与えた」と書いた定義が、実はなるべくしてなった定義であることを見ていくとともに、木としての幾何的な構造が効いてくる場所を解き明かしていきます。
前提
- 前回の記事の設定と記法をそのまま引き継ぎます。
- 圏、関手、自然変換を断りなく使いますが、必要になるのは定義のレベルまでです。
- 今回もモノイドは可換性を仮定しません。
不変条件とは何なのか?
前回の記事は以下のような構成をしていました。
- 木の各ノードに値 vτ を持たせ、不変条件 vτ=∏i∈seg(τ)ai を課す。
set と query の疑似コードを与える。
- それらが不変条件を保ち、正しい答えを返すことを証明する。
今回より詳しく調べるのは、なぜ set や query があのような疑似コードになるかです。
セグメント木の「効率的に演算ができる『配列っぽさ』」を明確に定式化していくことで理解を深めていきます。
前回、不変条件というものを定義しましたが、今回は次を示します。
不変条件とは、木の状態空間と配列の空間の間の同型のことである。
これを認めると、set と query は同型で移送するだけで一意に定まってしまい、疑似コードには「何を計算するか」という自由度が最初から存在しなかったことがわかります。そして同型であるということは、木が情報としては配列と区別できないということでもあります。
一方、この同型からはセグメント木の効率性は導出できません。ここが面白いところですね。
セグメント木の状態空間 Segd(M)
では初めていきます。まず、木の「あり得る状態」の集合を定義します。
深さ d の完全二分木のノード集合を Td と書きます。∣Td∣=2d+1−1 です。木の状態とは各ノードにモノイドの元を割り当てたもの、すなわち MTd の元ですが、前回の不変条件を満たすものだけがセグメント木として意味のある状態です。
そこで、モノイド M 上の深さ d のセグメント木の状態空間 Segd(M) を
Segd(M)={v∈MTd∣vτ=vlch(τ)⋅vrch(τ)(∀h(τ)>0)}と定義します。これは葉でないノードに対して、「親は子の積である」という局所的な条件だけを課した集合であることに注意してください。区間 seg(τ) も配列 a も、この定義には登場しません。
葉への制限写像
2d=n 個ある葉のうち、インデックス i に対応する葉を leaf(i) と書きます。
セグメント木の状態空間を葉に制限する
πd:Segd(M)v⟶Mn⟼(vleaf(0),…,vleaf(n−1))が、これから主役になる写像です。管理したい配列 a は、木の葉に置かれた値そのものですから、πd は「木の状態から、それが表している配列を読み出す写像」に相当します。
補題2 の再解釈: πd は全単射
前回、補題2で set 操作により不変条件が保存されることを確認しました。先ほど定義した πd が全単射であることを示し、不変条件を別の角度から捉えてみます。
命題7
πd は全単射である。さらにその逆写像は
(πd−1(a))τ=i∈seg(τ)∏aiで与えられる。
見ての通り、右辺は前回の不変条件そのものです。つまり前回の不変条件は「v が a に対応する唯一の状態、すなわち v=πd−1(a) であること」を書き下したものだった、ということになります。
証明は前回と同様に高さに関する帰納法でもできますが、次の分解を使うと d に関する帰納法で短く書けます。前回の補題2 とは別筋なので、こちらを紹介します。
補題8 (再帰的分解)
d≥0 について、根の子を根とする左右の部分木への制限は全単射
ρ:Segd+1(M) ∼ Segd(M)×Segd(M)を与える。
証明. 深さ d+1 の木は、根 τ0 と、その子 τ1,τ2 を根とする深さ d の部分木 2 つからなる。v∈MTd+1 に対し、内部ノードにおける条件を根とそれ以外に分けると、
- 根における条件: vτ0=vτ1⋅vτ2
- それ以外の内部ノードにおける条件: 左右の部分木への制限がそれぞれ Segd(M) に属すること
と書ける。したがって v∈Segd+1(M) を与えることは、
Segd(M) の元の組 (v∣τ1,v∣τ2) を与えることと同じである。
根の値 vτ0 は組から一意に決まり、逆に任意の組に対して根の値をそう定めれば条件を満たす。◼️
根自体は情報がなく、値が組から決まってしまうという点が要点です。
命題7 の証明
d に関する帰納法で示す。
d=0 のとき、木はノード 1 つ(根であり葉である)からなり、内部ノードが無いので条件は空、
Seg0(M)=MT0=M であって π0 は恒等写像である。
d で成立するとする。モノイドの列を前半と後半に分ける同型 M2d+1≅M2d×M2dのもとで、図式
Segd+1(M)πd+1↓⏐M2d+1ρ∼Segd(M) × Segd(M)↓⏐πd×πdM2d × M2dは可換である(深さ d+1 の木の葉の前半は左部分木の葉、後半は右部分木の葉に他ならないため)。補題8 より ρ は全単射、帰納法の仮定より πd×πd は全単射、下の横向きの写像は全単射なので、πd+1 も全単射である。
逆写像の表示は、(πd−1(a))τ:=∏i∈seg(τ)ai と定めたものが Segd(M) に属し(結合則: 親の区間は子の区間の順序を保った非交和なので、親の積は子の積の積に等しい)、葉で ai を与えることから従う。◼️
一点注意を述べておきます。n が 2 冪でない場合、単位元でパディングして Mn→M2d と埋め込んでいました。
この写像は準同型と可換する自然な写像ですが、単射なだけで同型ではありません。そのため、2 冪でない場合にはそのまま同じ議論はできません。
操作は一意に決まる
配列側で、私たちが本当に欲しかった操作は
i 番目を書き換える
σi:M×Mn(c,a)⟶Mn⟼(a0,…,ai−1,c,ai+1,…,an−1)と区間の積を取る
μ[l,r):Mna⟶M⟼i∈[l,r)∏ai2 つの操作でした。
さて、命題7 により πd は全単射でした。したがって木側の操作は、配列側の操作を πd で移送することで一意に定まります。
setiquery[l,r):=πd−1∘σi∘(idM×πd):=μ[l,r)∘πd「一意に」というのは、次の2つの図式を可換にする木側の写像は他にない、という意味です。
M × Segd(M)id × πd↓⏐∼M × MnsetiσiSegd(M)∼↓⏐πdMnSegd(M)πd↓⏐∼Mnquery[l,r)μ[l,r)MMそして、前回の補題2・補題3 が主張していたのは、まさに
疑似コードで書いた再帰手続きは、この一意に定まる写像と一致する
ということでした。前回「天下り的に与えた」と書いた set, query は配列側の操作から自動的に決まってくるようなものだったということですね。
厳密に言えば、疑似コードは MTd 全体の上で動く手続きではあるので、主張は「その手続きは部分集合 Segd(M) を保ち、そこへの制限が上の写像に一致する」となります。前回の補題2 が不変条件の保存を言っていたのは、この「Segd(M) を保つ」の部分に対応します。
こうして前回の補題の役割分担が見えます。
- 補題2・補題3 (正当性): 疑似コードが、一意に定まった写像と一致することの確認。
- 補題4・補題6 (計算量): それが O(logn) で計算できることの主張。
存在と一意性は πd が同型であることから自明でした。非自明なのは後者だけです。
実際、seti の定義をそのまま実装すれば、πd で配列を読み出し、書き換え、πd−1 で木を作り直すことになり、これは O(n) かかります。
疑似コードは、変化しない部分を再計算せずに工夫することで、同じ写像をより効率的に求めているわけですね。
準同型との整合性
もう一つ、この定式化から自然に出てくる性質を見ます。
Segd は関手である
モノイドの準同型 f:M→N に対し、成分ごとの適用 fTd:MTd→NTd を考えると、これは整合性条件を保ちます。実際 vτ=vτ1⋅vτ2 なら
f(vτ)=f(vτ1⋅vτ2)=f(vτ1)⋅f(vτ2)です。よって
Segd(f):Segd(M)→Segd(N)が定まり、
Segd(id)=id,Segd(g∘f)=Segd(g)∘Segd(f)も成分ごとに明らかなので、Segd は関手 Mon→set です。(−)n も同様に関手であり、πd は成分の取り出しなので
Segd(M)Segd(f)↓⏐Segd(N)πd∼πd∼Mn↓⏐fnNnは可換で、
π:Segd⟹(−)nは自然変換、しかも各成分が全単射なので自然同型です。
操作も準同型と可換する
Segd が関手であるだけだと、set とか query といったセグメント木上で行う操作と整合的になっているか不安になって確かめたくなりますよね。
配列側で σi と μ[l,r) が f と可換することは直接確かめられます。
M × Mnσi↓⏐Mnf×fnfnN × Nn↓⏐σiNnσi は成分の入れ替えだけなので上は可換ですし、f がモノイド準同型であることから
Mnμ[l,r)↓⏐MfnfNn↓⏐μ[l,r)Nも可換です。
これを π の自然性と合わせると、木側でも
M × Segd(M)seti↓⏐Segd(M)f×Segd(f)Segd(f)N × Segd(N)↓⏐setiSegd(N)Segd(M)query[l,r)↓⏐MSegd(f)fSegd(N)↓⏐query[l,r)Nが成り立ちます。
競技プログラミング的に言えば、これはセグメント木を使う時であっても「Z 上で計算してから最後に mod m を取っても、最初から Z/m 上で計算しても答えは同じ」という、普段何気なく使っている事実です。準同型 Z→Z/m が積と 1 を保つからそうなっている、というのがここでの説明になります。
今までセグメント木を使う時はこのあたりは深く考えてなかったです。
余談: Segd(M) 自身はモノイドか
MTd には成分ごとの積でモノイド構造が入ります。これは Segd(M) に制限できるでしょうか。v,w∈Segd(M) に対して条件を書くと
(vw)τ=vτwτ=vτ1vτ2wτ1wτ2,(vw)τ1(vw)τ2=vτ1wτ1vτ2wτ2となり、両者が一致するには vτ2wτ1=wτ1vτ2 が必要です。実際、M=⟨x,y⟩(2文字の自由モノイド)、d=1 として葉の値が (e,x) の状態と (y,e) の状態を取ると、成分ごとの積の根の値は xy ですが、葉の成分ごとの積 (y,x) から決まる根の値は yx であって、一致しません。
つまり M が可換なときに限り、πd は集合の同型ではなくモノイドの同型に持ち上がります。ここまで可換性は一度も要らなかったので、非可換性が本当に効く数少ない場所として面白いところです。
おわりに
- セグメント木は追加の構造を課さないモノイドに対してあたかも配列のように振る舞い、しかも無駄がないはずだ、という素朴な直感が定式化できた
- πd が同型だということは、集合の圏の中では木と配列が区別できない
- 不変条件、
set, query はこの同型を通してモノイドの操作から定まる自然なもの
- 計算量の議論だけ(前回の補題4 と補題6)は、モノイドの性質を一切使わず、木の幾何(各高さの境界ノードが高々 2 個であること)だけから出ていて、πd を通した同型からは出てこない