<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Math on Saan</title>
        <link>https://markovian.net/categories/math/</link>
        <description>Recent content in Math on Saan</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Wed, 15 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://markovian.net/categories/math/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Graph Coloring via Rings IRL (II)</title>
        <link>https://markovian.net/post/graph_col_irl/</link>
        <pubDate>Wed, 15 Jul 2026 00:00:00 +0000</pubDate>
        
        <guid>https://markovian.net/post/graph_col_irl/</guid>
        <description>&lt;h3 id=&#34;introduction&#34;&gt;Introduction
&lt;/h3&gt;&lt;p&gt;A bit ago, I made a post about a result characterizing $k$-colorability of a graph using ring theory of ideals:
&lt;div class=&#34;callout callout-theorem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Theorem: &lt;em&gt;Theorem 1&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    For a graph $G=(V,E)$ with $|V|=n$, define its adjacency polynomial $f_{G}(x_{1},...,x_{n})=\prod_{ij\in E}(x_{i}-x_{j})$, and define the ideal $I_{k}=\langle x_{1}^{k}-1,...,x_{n}^{k}-1\rangle$ for some integer $k$ in the ring $\mathbb{C}[x_{1},...,x_{n}]$. Then $G$ is $k$-colorable if and only if $f_{G}\in I_{k}$.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Thus graph coloring problem is reduced to an ideal membership problem. I think this result is cool for its own sake, but the next natural question is &amp;ldquo;does this make this problem any easier to solve?&amp;rdquo; I spent quite a bit of time seeing if I could make it work as a computational task, and this post is a sort of journal covering the hoops I had to jump through to try and make it feasible.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;1-smooth-sailing&#34;&gt;1. Smooth sailing
&lt;/h3&gt;&lt;p&gt;&lt;em&gt;Disclaimer: this section covers mathematical background that I hope is accessible, but it is not strictly necessary to understand the implementation. At the end of the section, I outline the actual thing that needs to be computed, you may skip to it directly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the nice thing. In general, to solve an ideal membership problem, we have to compute a &lt;em&gt;Gröbner basis&lt;/em&gt; of said ideal. An ideal can be represented with many bases (generating sets), but some of them are much more amenable to doing any sort of computation, and Gröbner bases are the best of them. The downside is that computing such a basis is really hard by itself (at least as hard as graph coloring, for certain). The upside is that we don&amp;rsquo;t need to, and I will give a brief overview to explain this.&lt;/p&gt;
&lt;p&gt;We begin with polynomial division. In a univariate polynomial ring, such as $\mathbb{R}[x]$, we have what is called the &amp;ldquo;division algorithm&amp;rdquo;: for any polynomials $f$ and $g$, we can find polynomials $q,r$ such that $f=qg+r$, where $\deg(r)&lt;\deg(g)$, that is, we divide $f$ by $g$ with remainder. This works always because a univariate ring is a lovely and handsome Principal Ideal Domain, which means that any ideal $I$ can be generated by only one element, like $I=\langle x\rangle$; this is like how the set of even numbers in $\mathbb{Z}$ can be represented by $\langle 2\rangle$, i.e. all multiples of 2. But a multivariate ring, like $\mathbb{R}[x,y]$, does not have this property. So how do we divide polynomials in 2 variables?&lt;/p&gt;
&lt;p&gt;When we divide $f$ by $g$, what we actually do is divide by the ideal $\langle g\rangle$. If you&amp;rsquo;ve taken any sort of abstract algebra, you may be familiar with this action as the canonical homomorphism $R\rightarrow R/\langle g\rangle$ sending each element of the ring to the coset of the ideal to which it belongs (the remainder is the coset representative). But in $\mathbb{R}[x,y]$, some (most) ideals are not principal; we have to write them as $I=\langle g_{1},g_{2}\rangle$. If you attempt to run the standard division algorithm to divide by $g_{1}$ and $g_{2}$ trying to find the quotients and the remainder in $f=q_{1}g_{1}+q_{2}g_{2}+r$, you may find that the order of $g_{1}$ and $g_{2}$ affects not only the quotients, but also the value of the remainder itself, so the &amp;ldquo;canonical homomorphism&amp;rdquo; is actually not a function at all.&lt;/p&gt;
&lt;p&gt;A Gröbner basis is what lets us fix this, since their essential property is that they always produce the same minimal remainder $r$ when used as a divisor, regardless of the permutation of elements within. Thus we can actually compute $(f\mod I)$ when $I$ is expressed as a Gröbner basis $\langle g_{1},...,g_{n}\rangle$.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t really defined what constitutes a Gröbner basis, and actually I&amp;rsquo;m not gonna do that here at all, because the main takeaway that matters is that $I_{k}=\langle x_{i}^{k}-1\mid i\in[n]\rangle$ is already Gröbner. In particular, reducing a polynomial $f\mod I_{k}$ is simply reducing its every exponent mod $k$.
Thus our problem is:
&lt;div class=&#34;callout callout-problem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;▲&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Problem: &lt;em&gt;The Big Thing&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    For a given graph $G=(V,E)$ and integer $k$, compute the adjacency polynomial $f_{G}=\prod_{ij\in E}(x_{i}-x_{j})$ and reduce every exponent mod $k$. $G$ is $k$-colorable if and only if the result is $0$.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;examples&#34;&gt;Examples
&lt;/h4&gt;&lt;p&gt;Before we begin, here are some examples of graphs and their corresponding polynomials, just to get a feel for it:
&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol1.png&#34;
	width=&#34;1189&#34;
	height=&#34;790&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;150&#34;
		data-flex-basis=&#34;361px&#34;
	
&gt;&lt;/p&gt;
$$\begin{align}
&amp;P_{2}\rightarrow \quad x_{1}-x_{2}.\\
&amp;P_{3}\rightarrow \quad x_{1}x_{2}-x_{1}x_{3}-x_{2}^{2}+x_{2}x_{3}.\\
&amp;C_{3}\rightarrow \quad -x_1^2 x_2 + x_1 x_2^2 + x_1^2 x_3 - x_2^2 x_3 - x_1 x_3^2 + x_2 x_3^2.\\
&amp;S_{4}\rightarrow \quad x_1^4 - x_1^3 x_2 - x_1^3 x_3 + x_1^2 x_2 x_3 - x_1^3 x_4 + x_1^2 x_2 x_4 + x_1^2 x_3 x_4 - x_1 x_2 x_3 x_4 \\&amp;- x_1^3 x_5 + x_1^2 x_2 x_5 + x_1^2 x_3 x_5 - x_1 x_2 x_3 x_5 + x_1^2 x_4 x_5 - x_1 x_2 x_4 x_5 \\&amp;- x_1 x_3 x_4 x_5 + x_2 x_3 x_4 x_5.\\
&amp;C_{4}\rightarrow \quad x_1^2 x_2 x_3 - x_1 x_2^2 x_3 - x_1^2 x_3^2 + x_1 x_2 x_3^2 - x_1^2 x_2 x_4 + x_1 x_2^2 x_4 + x_1^2 x_3 x_4 \\&amp;-2 x_1 x_2 x_3 x_4 + x_2^2 x_3 x_4 + x_1 x_3^2 x_4 - x_2 x_3^2 x_4 + x_1 x_2 x_4^2 - x_2^2 x_4^2 \\&amp;- x_1 x_3 x_4^2 + x_2 x_3 x_4^2.\\
&amp;K_{4}\rightarrow \quad x_1^3 x_2^2 x_3 - x_1^2 x_2^3 x_3 - x_1^3 x_2 x_3^2 + x_1 x_2^3 x_3^2 + x_1^2 x_2 x_3^3 - x_1 x_2^2 x_3^3 \\&amp;- x_1^3 x_2^2 x_4 + x_1^2 x_2^3 x_4 + x_1^3 x_3^2 x_4 - x_2^3 x_3^2 x_4 - x_1^2 x_3^3 x_4 + x_2^2 x_3^3 x_4 + x_1^3 x_2 x_4^2\\&amp; - x_1 x_2^3 x_4^2 - x_1^3 x_3 x_4^2 + x_2^3 x_3 x_4^2 + x_1 x_3^3 x_4^2 - x_2 x_3^3 x_4^2 - x_1^2 x_2 x_4^3 \\&amp;+ x_1 x_2^2 x_4^3 + x_1^2 x_3 x_4^3 - x_2^2 x_3 x_4^3 - x_1 x_3^2 x_4^3 + x_2 x_3^2 x_4^3.
\end{align}$$&lt;p&gt;As you can see, these get large rather quickly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;2-the-first-look&#34;&gt;2. The First Look
&lt;/h3&gt;&lt;p&gt;Since have a (theoretical, at this point) algorithm for checking whether $G$ is $k$-colorable, to find the minimum such $k$, or the chromatic number $\chi(G)$, we will simply run binary search; this works because &lt;code&gt;is_colorable(G,k)&lt;/code&gt; is a monotonic function with respect to $k$.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chromatic_number&lt;/span&gt;(G, k):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	low, high &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, maxdegree(G)&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# assume |E| &amp;gt; 0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	med &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (low&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;high)&lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; low &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; high:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		bool k_valid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; is_colorable(G, med)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; k_valid:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			low, high &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; low, med
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			low, high &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; med&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, high
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; med
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This solves for $\chi(G)$ using $O(\log_{2}(\max\deg(G)-1))$ queries. We use $\max\deg(G)+1$ as that is the upper bound on $\chi(G)$ by Brooks&amp;rsquo; theorem (and unless $G$ is $K_{n}$ or $C_{n}$ with $n$ odd, you can drop the +1). So going forward, all focus is on optimizing the query &lt;code&gt;is_colorable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The task therefore is repeated multiplication of $n$-variate polynomials. The main bottleneck here is storing and updating terms. My first choice here is processing the edges one by one and reducing exponents mod $k$ at each step, since that caps the number of possible terms to $k^{n}$ (an astronomical number, but at least it&amp;rsquo;s not unbounded). At each step we multiply the previous iteration of the polynomial $f_{t}$ by some $(x_{i}-x_{j})$:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;f_0 = 1
for (i, j) in E:
	f_{t+1} = (f_t * x_i) - (f_t * x_j)
	f_{t+1} %= I_k
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The second line in the loop reduces each exponent in each term mod $k$.
A polynomial has the following expanded form:
&lt;/p&gt;
$$f_{t}=(c_{1}\cdot x_{1}^{e_{1}^{(1)}}...x_{n}^{e_{n}^{(1)}})\;+ \;(c_{2}\cdot x_{1}^{e_{1}^{(2)}}...x_{n}^{e_{n}^{(2)}})\;+\;...$$&lt;p&gt;
We can represent it as a list of terms, each term consisting of a coefficient and a monomial, and each monomial is a list $[e_{1}^{(i)},...,e_{n}^{(i)}]$ of exponents for each $x_{j}$.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;f_t = [
	(c1, [e11, e12, ..., e1n]),
	(c2, [e21, e22, ..., e2n]),
	...,
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We multiply $f_{t}\cdot x_{j} \mod I_{k}$ by incrementing $j$th exponent in each term of $f_{t}$ (modulo $k$).&lt;/p&gt;
&lt;p&gt;This is&amp;hellip; less than ideal. First, while the updates themselves are fast, the memory footprint is atrocious, since we must allocate a vector for each term. Second, to simplify the polynomial (when we do the subtraction at each step), we want to combine like terms. To do this, we must iterate by pairs of terms in $f_{t}\cdot x_{i}$ and $f_{t}\cdot x_{j}$, which is $O(n^{2})$. Vector comparison is another $O(n)$ factor.&lt;/p&gt;
&lt;p&gt;The natural two improvements are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hash the monomial exponent vectors into single integers for $O(1)$ comparison&lt;/li&gt;
&lt;li&gt;Use a hashmap $\text{monomial exponent vector hash }\mapsto \text{ coefficient}$&lt;/li&gt;
&lt;/ol&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;exp_list = [
	(hash1, [e11, e12, ..., e1n]),
	(hash2, [e21, e22, ..., e2n]),
	...,
]

coeffs_map = {
	hash1 -&amp;gt; c1,
	hash2 -&amp;gt; c2,
	...
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now the algorithm can do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# input: edge ij, previous-step polynomial f_t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_exps &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;new_coeffs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; exp_hash, exp_vec &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; exp_list:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	exp_vec_i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; copy(exp_vec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	exp_vec_i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (exp_vec_i[i] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; k
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_hash_i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hash(exp_vec_i) &lt;span style=&#34;color:#75715e&#34;&gt;# O(?)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	exp_vec_j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; copy(exp_vec)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	exp_vec_j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (exp_vec_j[j] &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; k
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_hash_j &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; hash(exp_vec_j)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_coeffs[new_hash_i] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; coeffs[exp_hash]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_coeffs[new_hash_j] &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; coeffs[exp_hash]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_exps&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;insert( (new_hash_i, exp_vec_i) )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	new_exps&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;insert( (new_hash_j, exp_vec_j) )
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;exp_list &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; new_exps
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;coeffs_map &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; new_coeffs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay, now assume &lt;code&gt;hash()&lt;/code&gt; is constant time. This works nicely because combining like terms occurs naturally as we simply update coefficient values indexed by hashes of the exponent vectors. This doesn&amp;rsquo;t solve the storage problem, but the loop is certainly faster.&lt;/p&gt;
&lt;p&gt;Okay, now un-assume &lt;code&gt;hash()&lt;/code&gt; is constant time, because you cannot, in fact, process a length-$n$ vector in $O(1)$. You can do something else, though: make the hash incremental, i.e., homomorphic with respect to coordinate-wise incrementation, so we can compute &lt;code&gt;new_hash_i&lt;/code&gt; from &lt;code&gt;exp_hash&lt;/code&gt; and &lt;code&gt;i&lt;/code&gt; in constant time. The rest of this section is dedicated to this problem.&lt;/p&gt;
&lt;p&gt;There are two kinds of modifiable hashes that I considered for this: polynomial rolling hash and Zobrist hash. The former hashes a vector $A=[a_{1},...,a_{n}]$ as
&lt;/p&gt;
$$h(A)=\sum_{i=0}^{n-1}a_{i}\cdot B^{i} \mod P,$$&lt;p&gt;
where $B$ and $P$ are pre-determined (prime) constants. This hash is widely used in competitive programming for processing strings, since it has nice properties for when you want to compare prefixes and suffixes. For our purposes, defining $A&#39;$ to be the vector identical to $A$ except in the $j$th coordinate, for which the value is $b\neq a_{j}$, we write:
&lt;/p&gt;
$$h(A&#39;)\equiv h(A)-a_{j}B^{j}+bB^{j} \pmod P.$$&lt;p&gt;
So we undo the $j$th coordinate in the original hash and replace it with the desired value $b$, which in our case would be $a_{j}+1 \mod k$.&lt;/p&gt;
&lt;p&gt;The Zobrist hash is quite different; it is incremental by design and was invented to store games like chess - when a move is made, the state of the board changes only slightly, and you&amp;rsquo;d not want to recompute the hash from scratch when only two squares change.&lt;/p&gt;
&lt;p&gt;Generate a $n\times k$ table of random values, call it $Z$. Define &lt;/p&gt;
$$h(A)=\bigoplus_{i=1}^{n}Z[i, a_{i}],$$&lt;p&gt;where $\oplus$ is bitwise XOR. Now to modify the $j$th coordinate, we again undo the current value and replace it with another:&lt;/p&gt;
$$h(A&#39;)=h(A)\oplus Z[j,a_{j}]\oplus Z[j,b].$$&lt;p&gt;
Both of these hashes work, but for this task I went with Zobrist, since it doesn&amp;rsquo;t require finding suitable constants, XOR is faster than mod-multiplication, and overall it&amp;rsquo;s just cleaner.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;an-aside-about-benchmarking&#34;&gt;An aside about benchmarking
&lt;/h4&gt;&lt;p&gt;When I began this project, I wanted to put together some graphs for testing accuracy and speed. Initially I drew a few up by hand, and tested my results against the symbolic computation done by SageMath. For a graph on more than $10$ vertices, SageMath took too long, though, and I didn&amp;rsquo;t want to generate random graphs and run actually practical coloring algorithms on them to figure out the true $\chi(G)$. For this reason I turned to two known graph families: the Mycielski and the Turán graphs. The point is that both of them are easily constructible and have a regular structure that makes the chromatic number a fundamental property, without needing any computation.&lt;/p&gt;
&lt;p&gt;The Mycielski graph $M_{k}$ is specifically designed to have chromatic number $k$, with each successive $M_{k}$ being constructed iteratively from $M_{k-1}$. The special property is that it has no triangle subgraphs, making it quite sparse. The Turán graph $T_{n,r}$ is the opposite - it is the $r$-partite complete graph, having chromatic number $r$ and as many edges as possible.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;3-eliminating-exponent-vector-storage&#34;&gt;3. Eliminating Exponent Vector Storage
&lt;/h3&gt;&lt;p&gt;The Zobrist hash trick allows us to compare monomials&amp;rsquo; exponent vectors via their hashes, as well as update these hashes incrementally. However, the memory footprint remains: to update a hash by incrementing $a_{j}$, we actually have to know the current value, because the value we set is $a_{j}+1$ if $a_{j} &lt; k$ and $0$ otherwise; there is a branch involved. We cannot perform a &amp;ldquo;blind&amp;rdquo; incrementation mod $k$ on the hash, so we must store the entire vector.&lt;/p&gt;
&lt;p&gt;This was a glaring inefficiency that didn&amp;rsquo;t let me sleep at night for some time.
&lt;div class=&#34;callout callout-problem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;▲&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Problem: &lt;em&gt;Cyclic Incremental Hash&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Let $A=[a_{1},...,a_{n}]$ be an $n$-vector with $a_{i}\in\set{0,...,k-1}$, and let $A&#39;$ be the same vector in all coordinates except $j$, where it is $a&#39;_{j}=a_{j}+1 \mod k$. Find a hash $h:\mathbb{Z}_{k}^{n}\rightarrow \set{0,1}^{128}$ such that $h(A&#39;)=f(h(A),j)$ for some computable function $f$, that is, $h(A&#39;)$ can be computed directly knowing only $h(A)$ and $j$.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Okay, so the &amp;ldquo;blind&amp;rdquo; wrap-around mod $k$ should point to a cyclic representation of some kind.
Suppose $G$ is a group of prime order $p$, so every non-identity element is a generator. If we pick $n$ such elements $g_{1},...,g_{n}$ and define $h(A)=\prod_{i\in n}g_{i}^{a_{i}}$, then we will see that we can change the $j$th element to some arbitrary element $b$ with:
&lt;/p&gt;
$$h(A&#39;)=h(A)\cdot g_{j}^{-a_{i}}\cdot g_{j}^{b}.$$&lt;p&gt;
This idea I found in Bellare et al. (2021)&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;. The group here would be a multiplicative group mod $p$, where $p$ is be chosen to satisfy $p=2q+1$ for another prime $q$; this ensures $\mathbb{Z}_{p}^{\times}$ has prime order. However, this does not yet give us the cyclic effect mod $k$. For that, we can use a trick: choose $p$ to instead satisfy $p\equiv 1\pmod k$. Since it no longer has prime order, not every element is a generator, but nevertheless there are $\varphi(p-1)$ of them&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;. We need $n$ elements $g_{1},...,g_{n}$ with order $k$, for which we can take a generator $g$ and raise it to power $(p-1)/k$. Once again let $h(A)=\prod_{i\in[n]}g_{i}^{a_{i}}$ but now, to increment the $j$th coordinate we simply multiply $h(A&#39;)=h(A)\cdot g_{j}$. This works because we&amp;rsquo;ve made it so $g_{j}^{k}=e$, which gives us the desired modulo effect.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s a significant problem with this approach (see if you can find it). Because we take a product over elements with $g_{i}^{k}=e$, the set of outputs is exactly the elements of the group which satisfy this. So what is the size of our hash&amp;rsquo;s image?&lt;/p&gt;
&lt;div class=&#34;callout callout-lemma&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Lemma: &lt;em&gt;Hash Output Space&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Let $G(k)=\set{g\in\mathbb{Z}_{p}^{\times}\mid g^{k}=e}$. Then $|G(k)|=k$.
&lt;em&gt;Proof:&lt;/em&gt; $\mathbb{Z}_{p}^{\times}$ is a cyclic group, so $\exists x\text{ s.t. }\langle x\rangle=\mathbb{Z}_{p}^{\times}$. Then $x^{(p-1)/k}$ has order $k$, and subsequently $y_{t}:=x^{t(p-1)/k}$ satisfies $y_{t}^{k}=e$ for $t\in 1,...,k$. This means $|G(k)|\geq k$.
On the other hand, we can note that $\mathbb{Z}_{p}$ is a field and the elements of $G(k)$ are $k$-th roots of unity in it, in other words, the roots of $x^{k}-1$. A degree $k$ polynomial has at most $k$ roots. Having bounded above and below, we get $|G(k)|=k$.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;So while the hash function thus constructed satisfies our original requirements, it can only ever output $k$ distinct values&amp;hellip; This is far too few to ever be useful. My solution here was to extend this idea - rather than have only one $k$-cyclic subgroup which hard-caps our outputs to a small number, we embed multiple.&lt;/p&gt;
&lt;p&gt;Let $p$ and $q$ be prime, and $N:=pq$. Recall that $\mathbb{Z}^{\times}_{N}\cong \mathbb{Z}^{\times}_{p}\times \mathbb{Z}^{\times}_{q}$ and that each of the groups on the right side are cyclic. Choosing again $p$ such that $|\mathbb{Z}^{\times}_{p} |=p-1$ is divisible by $k$, we guarantee the group contains a cyclic subgroup of order $k$ (isomorphic to the cyclic group $C_{k}$). We choose $q$ likewise, with the only added constraint of $p\neq q$. Applying the isomorphism above, we&amp;rsquo;ve ensured that $C_{k}\times C_{k}$ is isomorphic to a subgroup of $\mathbb{Z}_{N}^{\times}$. If $\langle g\rangle=C_{k}$, then $C_{k}^{2}=C_{k} \times C_{k}=\langle (g,e), (e,g)\rangle$. In other words, if we can find the elements corresponding to $(g,e)$ and $(e,g)$ in $\mathbb{Z}_{N}^{\times}$, we can add an extra dimension to our hash image!&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do this concretely. The Chinese Remainder theorem gives us $\mathbb{Z}^{\times}_{p}\times \mathbb{Z}^{\times}_{q}\cong \mathbb{Z}^{\times}_{N}$, let $\phi$ be this isomorphism, and suppose we&amp;rsquo;ve found the elements $h_{p}\in\mathbb{Z}^{\times}_{q}$ and $h_{q}\in\mathbb{Z}^{\times}_{q}$ as above, so the $\text{ord}(h)=k$. The basis $\set{ (h_{p},e),(e,h_{q})}$ generates $C_{k}^{2}$ and we can lift it to $\mathbb{Z}^{\times}_{N}$: define $g_{1}:=\phi(h_{p},e)$ and $g_{2}:=\phi(e,h_{q})$. Restating above, we get:
&lt;/p&gt;
$$C_{k}^{2}\cong \langle g_{1},g_{2}\rangle\leq \mathbb{Z}^{\times}_{N}.$$&lt;p&gt;
The point is that the size of $\text{span}\set{g_{1},g_{2}}$ in $\mathbb{Z}^{\times}_{N}$ is $k^{2}$ and we can find these elements very easily using a CRT solver (essentially the implementation of $\phi$) for the systems
&lt;/p&gt;
$$\begin{cases} g_{1}\equiv h_{p}\pmod p \\
g_{1}\equiv 1 \pmod q\end{cases}
 \qquad\begin{cases} g_{2}\equiv 1 \pmod p \\
g_{2}\equiv h_{q}\pmod q.
\end{cases}$$&lt;p&gt;
Lastly, we choose $s_{1},s_{2}$ from $0,...,k-1$ randomly, and define $w=g_{1}^{s_{1}}g_{2}^{s_{2}}$. Since $g_{i}^{k}=e$, we know also that $w^{k}=e$. Now rather than just one, generate $n$ pairs $(s_{1}^{(i)},s_{2}^{(i)})$, and for each of them let $w_{i}$ be defined likewise. What we have done at this point is construct $w_{1},...,w_{n}\in \mathbb{Z}^{\times}_{N}$ which span the product cyclic subgroup and satisfy $w^{k}=e$, which is the exact condition we wanted. We formalize the hash function for $A=[a_{1},...,a_{n}]$ as
&lt;/p&gt;
$$h(A)=\prod_{i\in[n]}w_{i}^{a_{i}}$$&lt;p&gt;
and verify that for $A&#39;$ defined as $A$ but with $j$th coordinate incremented mod $k$, it satisfies
&lt;/p&gt;
$$h(A&#39;)=h(A)\cdot w_{j}.$$&lt;p&gt;
The order of each $w$ is $k$ (or, in rare cases, a divisor of $k$ if $gcd(k,s_{1},s_{2})&gt;1$), so $w^{k}$ cycles back to $e$, and $\text{span}\set{w_{1},...,w_{n}}=\text{span}\set{g_{1},g_{2}}$ with size $k^{2}$. Thus we have our magic hash!&lt;/p&gt;
&lt;h4 id=&#34;asterisks&#34;&gt;Asterisks
&lt;/h4&gt;&lt;p&gt;This yields a hash with image size $k^{2}$ but in practice this is still far too small. However, this method is entirely extendable: rather than use two primes, we use $N:=p_{1}p_{2}...p_{d}$, leverage $\mathbb{Z}_{p_{1}}^{\times}\times ... \times \mathbb{Z}_{p_{d}}^{\times}\cong \mathbb{Z}_{N}^{\times}$ and the generalization
&lt;/p&gt;
$$C_{k}^{d}=\langle h_{1}\rangle\times ...\times \langle h_{d}\rangle \cong \langle \phi(h_{1},e,e,...,e), \phi(e,h_{2},e,...,e),...\rangle\leq \mathbb{Z}_{N}^{\times}$$&lt;p&gt;
to make $w_{1},...,w_{n}$ span $d$ dimensions, resulting in the image size $k^{d}$. Since the operations in $\mathbb{Z}_{N}^{\times}$ happen modulo $N$, this is the bottleneck - the product of our $d$ distinct special primes must fit in an (128-bit) integer data type. To maximize $d$, we choose the primes to be as small as possible, but on average, we can get $\approx 18$. Still, this gives us a very sizable output space.&lt;/p&gt;
&lt;p&gt;A key step I&amp;rsquo;ve skipped is producing a generator $g$ of $\mathbb{Z}_{p}^{\times}$. There&amp;rsquo;s not a known closed form solution for this problem, but (for any $p$) on average about a third of the elements of $\mathbb{Z}_{p}^{\times}$ are generators, so a guess-and-check algorithm is actually quite efficient for this. We know by Lagrange&amp;rsquo;s theorem that if an element $g$ has order $m &lt; n:=p-1=|\mathbb{Z}_{p}^{\times}|$, then $m$ is a divisor of $n$. The contrapositive tells us that if for some $m\mid n,\; g^{m}=e$, then $g$ is not a generator; thus, we will pick a random element and go through all maximal divisors of $n$ to see if our candidate evaluates to $e$ with that exponent; if it doesn&amp;rsquo;t for any of them, we&amp;rsquo;ll have found our generator.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; randint(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;,p&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	failed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; q &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; prime_factors(n):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; pow(x, n&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;q, p) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			failed &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; failed:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; x
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For prime factorization I will precompute the SPF array (&lt;code&gt;spf[n] = smallest prime factor of n&lt;/code&gt;) up to a constant maximum; this needs only to be done once and allows $O(\log x)$ factorization. I use this method very often in competitive programming.&lt;/p&gt;
&lt;p&gt;Now having found the generator of each $\mathbb{Z}_{p_{i}}^{\times}$, we let $h_{i}$ be it&amp;rsquo;s $((p_{i}-1)/k)^{\text{th}}$ power; observe that each $h_{i}$ has order $k$ and thus $C_{k}\cong \langle h_{i}\rangle \leq \mathbb{Z}_{p_{i}}^{\times}$.&lt;/p&gt;
&lt;h4 id=&#34;summary&#34;&gt;Summary
&lt;/h4&gt;&lt;p&gt;To recap, what we&amp;rsquo;ve done so far is construct a group $\mathbb{Z}_{N}^{\times}$ that contains as many embedded copies of the cyclic group $C_{k}$ in it as we could fit. We know that their product $C_{k}\times ... \times C_{k}\cong \langle h_{i}\rangle\times...\times\langle h_{d}\rangle$ is a group to which there exists an isomorphic subgroup in $\mathbb{Z}_{N}^{\times}$ by the Chinese remainder theorem. We find the vectors spanning $C_{k}^{d}$ and lift them to $\mathbb{Z}_{N}^{\times}$, and we generate weights which give us the cyclic incrementation property.&lt;/p&gt;
&lt;p&gt;As a final note, solving a CRT modular system of $d$ equations in the standard way requires $O(d)$ time; as there are $d$ such systems, we&amp;rsquo;re looking at $O(d^{2})$. However, the systems are all vectors of the &amp;ldquo;standard basis&amp;rdquo;: in the $i$th vector, all elements but the $i$th are identity. We can use this to speed up the computation to $O(d)$ in total by doing it in a batch with $O(1)$ per system/vector with $N$ known.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;4-reduction-via-edge-ordering&#34;&gt;4. Reduction via Edge Ordering
&lt;/h3&gt;&lt;p&gt;The algorithm can be viewed as a process that begins with an empty graph on $n$ vertices and adds an edge at each step. The revelation of each edge $(ij)$ corresponds to multiplying the previous step&amp;rsquo;s polynomial by $(x_{i}-x_{j})$. The number of terms in this polynomial grows very quickly (more or less exponentially, as you might imagine); the more terms we have at step $t$, the more we will have to process and insert at $t+1$. But not all edges give the same growth. Can we slow the term count growth by revealing edges in a clever order?&lt;/p&gt;
&lt;p&gt;The answer is yes, and here&amp;rsquo;s a simple example:
&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol2.png&#34;
	width=&#34;1990&#34;
	height=&#34;390&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;510&#34;
		data-flex-basis=&#34;1224px&#34;
	
&gt;&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol3.png&#34;
	width=&#34;1990&#34;
	height=&#34;390&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;510&#34;
		data-flex-basis=&#34;1224px&#34;
	
&gt;
$f_{t}$ is the polynomial of the partial graph with edges $1,...,t$ (with respect to a particular ordering), and $\text{T}(f_{t})$ is the number of terms in the polynomial. Notice that the second ordering produces fewer terms at steps 3 and 4. Of course, the final result will be the same in any ordering as the final graph is fixed, but smaller intermediate results means less work at each subsequent step.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a more noticeable difference:
&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol4.png&#34;
	width=&#34;3190&#34;
	height=&#34;390&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;817&#34;
		data-flex-basis=&#34;1963px&#34;
	
&gt;&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol5.png&#34;
	width=&#34;3190&#34;
	height=&#34;390&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;817&#34;
		data-flex-basis=&#34;1963px&#34;
	
&gt;
The second ordering processes, in total, 73 fewer terms than the first. Clearly, among all orderings of edges of the graph $G$, there must be an ordering which yields the fewest intermediate terms over the course of execution. So&amp;hellip; what is it?&lt;/p&gt;
&lt;div class=&#34;callout callout-problem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;▲&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Problem: &lt;em&gt;Minimum Cost Edge Ordering&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    For a given graph $G$, we as usual define the polynomial $f=\prod_{ij\in E}(x_{i}-x_{j})$ and let $\text{T}(f)$  denote the number of terms in the expanded &amp;amp; simplified form of the polynomial. If $e_{1}\prec e_{2}\prec ...\prec e_{m}$ is any ordering of edges of $G$, we call $f_{t}^{\prec}$ the partial polynomial of the first $t$ edges: $\prod_{ij\in E \;:\; (ij)\preceq e_{t}}(x_{i}-x_{j})$; it is defined w.r.t. the ordering. Finally, define the cost of the ordering $c(\prec):=\sum_{t\in[m]}\text{T}(f_{t}^{\prec})$.&lt;br&gt;
Find the optimal ordering $\prec^{\star}$ such that the cost $c(\prec^{\star})$ is minimum.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is a hard problem, I hate to say, and I don&amp;rsquo;t have a solution. But there are some heuristics which can give a &lt;em&gt;pretty good&lt;/em&gt; approximation. The main problem is the behavior of the function $\text{T}(\cdot)$ - I don&amp;rsquo;t have a simpler formula for the term count than its definition, even a recurrent one. However, observe that each time we introduce an edge containing an endpoint not already in the graph, the term count doubles. This should make sense: in the step $f_{t}\mapsto x_{i}f_{t}-x_{j}f_{t}$ the terms of our polynomial are duplicated, but a cancellation may occur when we combine like terms. However, if $x_{i}$ or $x_{j}$ is not in $f_{t}$ already, a term from $x_{i}f_{t}$ can never be equal to a term in $x_{j}f_{t}$. Thus, $\text{T}(f_{t+1})\leq2\text{T}(f_{t})$ with equality iff $x_{i}$ or $x_{j}$ are added for the first time. In a way, our task here is to maximize cancellation, which occurs when we link two vertices already connected; that is, close a cycle.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the key to why the second ordering in the both above examples wins: we close cycles greedily, aiming to close the shortest cycle available at each step, as short cycles provide the largest cancellation effect. When no cycles can be closed immediately, we attempt to predict which vertex reveals would produce potential for shortest cycles in the future. This method allows us to maximize cancellation at each step, and thus reduce the number of intermediate terms.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s denote $G_{t}$ as the partial graph with the first $t$ edges revealed. Which edge should we pick next to maximize the closure of shortest cycles? A working strategy is to compute the distances $d_{t}(u,v)$ for all edges $uv$ remaining not revealed at step $t$ and pick the one that minimizes this distance, thereby closing a $d_{t}(u,v)+1$-long cycle. Naively this would require recomputing the distances in $G_{t}$ between the endpoints of all potential edges at every step; with BFS it comes out to $O((m+n)m^{2})$ where $m$ is the number of edges in $G$. Here, there&amp;rsquo;s a clever trick we can do: observe that if we insert an edge $uv$, the distance between any two other vertices $x$ and $y$ changes in a very predictable way.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://markovian.net/post/graph_col_irl/graphcol6.png&#34;
	width=&#34;571&#34;
	height=&#34;432&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;132&#34;
		data-flex-basis=&#34;317px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Here we plan to reveal the edge $uv$ and the black solid line is the shortest path between $u$ and $v$ in $G_{t}$ (prior to the insertion). What is the shortest path between $x$ and $y$ in $G_{t+1}$? There are two options: it either does not include the edge $uv$ at all (1) or includes it once (2). In the first case, $d_{t+1}(x,y)=d_{t}(x,y)$, and in the second, the route first takes the segment $x-u$, then the newly added shortcut edge, then the segment $v-y$, so $d_{t+1}(x,y)=d_{t}(x,u)+1+d_{t}(v,y)$. We also get a candidate path by swapping $u$ and $v$ in this expression. Thus, to update distances after the insertion of $uv$ we write:
&lt;/p&gt;
$$d_{t+1}(x,y)=\min\set{d_t(x,y),\;d_{t}(x,u)+1+d_{t}(y,v),\;d_{t}(y,u)+1+d_{t}(v,x)}.$$&lt;p&gt;
We still need to know the distances to $u$ and $v$, so we will run BFS to get them. The algorithm looks like this and runs in $O((n+m)m)$:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;order &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;future_edges &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; edges
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {{a,b} : INF &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; {a,b} &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; edges} &lt;span style=&#34;color:#75715e&#34;&gt;# distances map for future edges&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; future_edges &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; empty:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; have_finite_distance: &lt;span style=&#34;color:#75715e&#34;&gt;# can close a cycle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;# take shortest distance edge&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		uv &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; min(future_edges, key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;d)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		future_edges&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;delete(uv)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		order&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(uv)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		u_dists &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bfs(u)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		v_dsists &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; bfs(v)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;# update remaining distances&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; xy &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; future_edges:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			d[xy] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; min(d[xy], u_dists[x]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;v_dists[y], u_dists[y]&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;v_dists[x])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; d[xy] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; INF:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				have_finite_distance &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We want to defer the revealing of new vertices (revealing edges with a previously unseen endpoint) and prioritize adding edges between vertices already present. But If all distances on future edges are infinite, we can no longer close a cycle, so we must extend our graph to a new vertex. This cannot be done greedily and requires a certain prescience - it&amp;rsquo;s not clear which new vertex will lead to short cycles in the future. A heuristic can be used here: pick the vertex with the most back-edges to the discovered subgraph. The edge will be taken arbitrarily (though this can also be improved):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;future_vertices &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; vertices
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;revealed_neighbors &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {v : &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; v &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; vertices}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		v &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; max(future_vertices, key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;revealed_neighbors)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		future_vertices&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;delete(v)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;# update futures and pick arbitrary back-edge from v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; w &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; G&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;neighbors(v):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; w &lt;span style=&#34;color:#f92672&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; future_vertices:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				u &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; w
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			revealed_neighbors[w]&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		order&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(uv)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is not an exact solution to the ordering problem, but it performs decently well for slowing down growth of terms. Unfortunately, since every new vertex reveal doubles the term count with no chance for cancellation, we&amp;rsquo;re still looking at $\geq 2^{n}$ terms in the expression.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;5-a-faster-mod-multiplication&#34;&gt;5. A Faster Mod-Multiplication
&lt;/h3&gt;&lt;p&gt;At this point, the algorithm clears Mycielski $M_{4}$ in a fraction of a second, but struggles to make it past the first ~25 edges of $M_{5}$. A closer look at performance reveals that over a third of the CPU cycles are spent on doing mod-multiplication to update the term hash entries. And no wonder, since my algorithm for doing that up to this point looks like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-C++&#34; data-lang=&#34;C++&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ui128 &lt;span style=&#34;color:#a6e22e&#34;&gt;modmult&lt;/span&gt;(ui128 a, ui128 b, ui128 m) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	ui128 result &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (b&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (b&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#75715e&#34;&gt;// result = (result + a) % m;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;			result&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;(result&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;m&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;a) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; result&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;(m&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;a) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; reuslt &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; a
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;//a = 2*a % m;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;		a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (a&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt;m&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;a) &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; a&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;(m&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;a) &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;a;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		b &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; result;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is awful and is essentially naive multiplication through addition in $O(\log b)$. The reason I did this initially was because 128-bit ints are the largest int data type in C++, so I could not cast the intermediate result $a\cdot b$ (which is up to 256 bits) to a larger type before taking mod. This was my next challenge.
&lt;div class=&#34;callout callout-problem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;▲&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Problem: &lt;em&gt;Max Precision Modular Multiplication&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Compute $(a\cdot b \mod m)$ as fast as possible given that all three integers are up to $B$ bits long, and no larger data type is available.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Fortunately for me, some clever people solved this problem back in the 80s. The method I went for is called Montgomery multiplication. I won&amp;rsquo;t go into the full detail; I think the &lt;a class=&#34;link&#34; href=&#34;https://en.wikipedia.org/wiki/Montgomery_modular_multiplication&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Wikipedia page&lt;/a&gt; explains it really well, it&amp;rsquo;s what I used to learn it. The base concept is this: we pick a radix base $r$ to be coprime to $m$, find the special &lt;em&gt;Montgomery form&lt;/em&gt; $a*b$ defined in relation to $m$ and $r$, and express our result as $x\equiv (a*b)r^{-1}\pmod m$. To find the remainder in constant time, we must use the division operation which is the most expensive arithmetic instruction; what this lets us do is push the division over to $r$ rather than an expression of $a$ or $b$. The twist here is that we pick $r$ to be a power of two, which makes division as simple as bit shifting.&lt;/p&gt;
&lt;p&gt;Montgomery speeds up my &lt;code&gt;modmult&lt;/code&gt; by a factor of ~x15, from 0.00414 to 0.00026ms on average for 128-bit ints.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;6-last-ditch-efforts&#34;&gt;6. Last ditch efforts
&lt;/h3&gt;&lt;p&gt;The Monty optimization pushes my CPU time spent on &lt;code&gt;modmult&lt;/code&gt; down to about 5%; most of the rest is the time spent on the methods of &lt;code&gt;unordered_map&lt;/code&gt; - allocation, the &lt;code&gt;[]&lt;/code&gt;get operator, &lt;code&gt;emplace&lt;/code&gt;, and a couple minor ones. My guess is that at this point the fault is in frequent cache misses - the STL hashmap allocates nodes on the heap scattered across memory. I switched to a flat, open addressing map from the &lt;a class=&#34;link&#34; href=&#34;https://github.com/greg7mdp/gtl&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GTL&lt;/a&gt; library developed by Gregory Popovitch. It actually has a pretty cool parallel hashmap template that breaks the map into 16 submaps and leverages SIMD instructions to speed up search and resizing; however, it turned out that for my use case the standard flat map was faster. I attribute this to my never needing to resize - I reserve sufficient space at initialization.&lt;/p&gt;
&lt;p&gt;Open addressing gives a nice bonus, but with all these optimizations I could not break Mycielski $M_{5}$ (23 vertices, 71 edges) or Turán $T_{15,3}$ (15 vertices, 75 edges). The space footprint still grows exponentially, and by 30-35 edges in my CPU begins stalling as most time is spent waiting for memory rather than actual computation.&lt;/p&gt;
&lt;p&gt;A strange trick I discovered here: it turned out that the &lt;code&gt;map&amp;lt;ui128, int&amp;gt;&lt;/code&gt; was considerably slower than &lt;code&gt;map&amp;lt;ui128, int64_t&amp;gt;&lt;/code&gt; - running on $M_{5}$, the first 30 edges took about 20% less time to process. Initially I guessed this is due to memory alignment rules splitting the 20-byte pairs across different 64-byte memory segments, requiring two cache lines to get a single pair; but in hindsight I don&amp;rsquo;t believe this is right - both options should require a full 32 bytes with padding to store. This is something I might investigate further.&lt;/p&gt;
&lt;p&gt;Another idea I spent considerable time on: one could discard the hashmap entirely and switch to simple vectors. The problem requires us to match terms with equal monomial hashes, so if $x_{i}f_{t}$ and $x_{j}f_{t}$ were both represented by sorted arrays, we could do a zip merge to combine them into one. This would have a benefit over the hashmap since it would give us better locality control and improve the cache hit rate. However, sorting the arrays would take a long time (I tried with radix sort, it&amp;rsquo;s theoretically $O(n)$ but still slower than the hashmap approach), so instead what would be nice to have is a sort order that would be invariant under the action of multiplying each element by $w_{j}$. That is, if $h_{1}\prec ... \prec h_{T}$ is the sorted array of hashes of terms of $f_{t}$, then for $x_{i}f_{t}$ we maintain $h_{1}w_{i}\prec ...\prec h_{T}w_{i}$ and likewise for $j$. Then we could simply perform a sorted merge. The problem is, such a total order is impossible in a finite group: suppose elements of $G$ are ordered as $g_{1}\prec ... \prec g_{n}$ and we scale by $w\in G\text{ with }w\neq e$, then $wg_{1}\neq g_{1}$ is out of order.&lt;/p&gt;
&lt;p&gt;However, I&amp;rsquo;m not entirely convinced. For one, our condition is slightly weaker than this: not all elements can appear as $w$ and they are known in advance. For another, we don&amp;rsquo;t need to have a linear order - we could use a circular one. A circular order on a set $S$ is a trinary relation $[a,b,c]$ that indicates the order in which you&amp;rsquo;d see elements moving along the circle. For instance, if $S$ are hours of the clock, $[1,4,9]$ is true but $[4,8,5]$ is not. One could imagine the list of terms as a circular array (first and last elements are considered adjacent) sorted under a circular ordering, so a sorted merge could work much the same way.&lt;/p&gt;
&lt;h4 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h4&gt;&lt;p&gt;Ultimately, there&amp;rsquo;re many things left I could still investigate to improve performance, from high level mathematical representation to hardware optimization. This was an interesting experiment, and though I&amp;rsquo;ve learned some things I had not expected to even touch on, the practical results are&amp;hellip; meager - this method only works for very small graphs. I wouldn&amp;rsquo;t recommend it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;7-verdict&#34;&gt;7. Verdict
&lt;/h3&gt;&lt;p&gt;If you need a graph colored, I advise using something less deranged.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://cseweb.ucsd.edu/~mihir/papers/inc1.pdf&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Bellare et al.&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;https://crypto.stanford.edu/pbc/notes/numbertheory/gen.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;https://crypto.stanford.edu/pbc/notes/numbertheory/gen.html&lt;/a&gt;&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</description>
        </item>
        <item>
        <title>Graph Coloring via Rings</title>
        <link>https://markovian.net/post/graph_coloring_in_ring_theory/</link>
        <pubDate>Wed, 22 Apr 2026 00:00:00 +0000</pubDate>
        
        <guid>https://markovian.net/post/graph_coloring_in_ring_theory/</guid>
        <description>&lt;h3 id=&#34;introduction&#34;&gt;Introduction
&lt;/h3&gt;&lt;p&gt;While taking a course on ring theory, I looked up some applications of it to other fields. My favorite one was this connection to graph theory, in particular a characterization of the chromatic number from the perspective of ideal theory and algebraic geometry. In fact, there are two completely separate ways of getting to it, and each provides pretty cool insights.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;background&#34;&gt;Background
&lt;/h3&gt;&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;ol&gt;
&lt;li&gt;A commutative ring $R$ is a set with two operations: addition and multiplication. As in the integers, both are associative, commutative, and multiplication distributes over addition. $R$ is closed under the operations, and contains an additive identity $0$ and a multiplicative identity $1$.&lt;/li&gt;
&lt;li&gt;A polynomial ring over a field $F$ with one variable, $F[x]$, is the set of polynomials of $x$ with coefficients in $F$. Likewise, $F[x_{1},...,x_{n}]$ is the set of such multivariate polynomials.&lt;/li&gt;
&lt;li&gt;An ideal $I\subseteq R$ of a ring is a subset such that:
&lt;ol&gt;
&lt;li&gt;$\forall a,b\in I,\;a+b\in I$;&lt;/li&gt;
&lt;li&gt;$\forall a\in I, r\in R, \;ar\in I$;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;An ideal $I$ is generated by $a\in R$, written as $I=\langle a\rangle$, if $I=\set{ar\mid r\in R}$. Likewise, $\langle a_{1},...,a_{n}\rangle=\set{\sum_{i\in[n]}a_{i}r_{i}\mid r_{i}\in R}$. Here, $\set{a_{1},...,a_{n}}$ is the basis of the ideal.&lt;/li&gt;
&lt;li&gt;For a set of polynomials $f_{1},...,f_{s}\in F[x_{1},...,x_{n}]$, define the variety $\mathbf{V}(f_{1},...,f_{s})=\set{a=(a_{1},...,a_{n})\in F^{n}\mid f_{1}(a)=...=f_{s}(a)=0}$, that is, the set of common zeroes of the polynomials.&lt;/li&gt;
&lt;/ol&gt;

  &lt;/div&gt;
&lt;/div&gt;


&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition: &lt;em&gt;Fact 1&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    In a polynomial ring, if $I=\langle f_{1},...,f_{s}\rangle$ then $\mathbf{V}(f_{1},...,f_{s})=\mathbf{V}(I)$. This means that the variety of an ideal is not determined by the chosen basis.
  &lt;/div&gt;
&lt;/div&gt;


&lt;hr&gt;
&lt;h3 id=&#34;the-first-construction&#34;&gt;The first construction
&lt;/h3&gt;&lt;p&gt;For a graph $G=(V,E)$, consider the multivariate polynomial ring $R=\mathbb{C}[V]$ (with variables being vertices). For an integer $k$, define $I_{k}=\langle v^{k}-1\mid v\in G\rangle\subseteq R$, which is an ideal. We get something interesting when we consider the variety $\mathbf{V}(I_{k})$: if some point $(a_{1},...,a_{n})\in\mathbb{C}^{n}$ is in $\mathbf{V}(I_{k})$, meaning all $v_{i}^{k}-1$ evaluate to $0$ at that point, or $a_{i}^{k}=1$, then each $a_{i}$ is a $k$-root of unity! In other words, the point assigns to each vertex of $G$ a value in $\set{1,\zeta,...,\zeta^{k-1}}$, of which there are $k$, thus we can interpret $\mathbf{V}(I_{k})$ to be the set of all $k$-colorings of $G$. This is cool, but not significant yet, since these colorings are not proper.&lt;/p&gt;
&lt;p&gt;Next, we introduce a way to &amp;ldquo;filter&amp;rdquo; these colorings. Let
&lt;/p&gt;
$$f_{G}(v_{1},...,v_{n})=\prod_{(v_{i}v_{j})\in E(G)}(v_{i}-v_{j})\quad \in \mathbb{C}[V].$$&lt;p&gt;
Here, we think of the variables as the assigned colors of vertices. It is easy to see that $f_{G}=0$ if and only if for some two vertices which share an edge, the color assignments are equal. Therefore, this polynomial vanishes exactly on the set of improper colorings.&lt;/p&gt;
&lt;div class=&#34;callout callout-theorem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Theorem: &lt;em&gt;Theorem 1&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    $G$ is $k$-colorable if and only if $f_{G}\notin I_{k}$.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Proof $(\Rightarrow  )$.&lt;/em&gt; $f_{G}\in I_{k}$ implies $f_{G}$ vanishes on $\mathbf{V}(I_{k})$; let $a=(a_{1},...,a_{n})$ be a point of the variety, then $0=f_{G}(a)$ implies that for some edge $(v_{i}v_{j})$ we get $a_{i}=a_{j}$. Since $a$ was an arbitrary point corresponding to a $k$-coloring of $G$, all $k$-colorings fail to be proper.&lt;/p&gt;
&lt;p&gt;For the reverse direction, we will need to invoke Hilbert&amp;rsquo;s Nullstellensatz.
&lt;div class=&#34;callout callout-theorem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Theorem: &lt;em&gt;Hilbert&amp;#39;s Nullstellensatz.&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    If $f\in F[x_{1},...,x_{n}]$ vanishes on $\mathbf{V}(I)$ for some ideal $I$ then $f^{m}\in I$ for some integer $m$.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Proof $(\Leftarrow  )$.&lt;/em&gt; $G$ being non-$k$-colorable implies any point of $\mathbf{V}(I_{k})$, which corresponds to a $k$-coloring, is not proper, so $f_{G}$ vanishes on it. Applying the Nullstellensatz, we know $f_{G}^{m}\in I_{k}$ for some integer $m$. We see the result follows immediately if $I_{k}$ is a radical ideal.
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Radical ideal&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    An ideal $I\subset R$ is radical if for any $a$ and integer $m$, $a^{m}\in I\Rightarrow a\in I$.
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    $I_{k}$ is a radical ideal.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;To prove this, a bit of machinery is needed. Feel free to skip this if you are content to take the above as fact. First, we use a lemma that gives an alternative characterization of radical ideals; second, I very cautiously introduce tensor product between two rings. I am by no means an expert on this, but for this proof we only need the most basic idea.
&lt;div class=&#34;callout callout-lemma&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Lemma&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Ideal $I\subset R$ is radical if and only if quotient ring $R/I$ satisfies $a^{2}=0\Rightarrow a=0$ for all its elements $a$ (this is called a reduced ring).
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;A tensor product of two polynomial rings $R[x]$ and $R[y]$ over $R$ essentially combines them into a single ring that incorporates both $x$ and $y$: $R[x]\otimes_{R}R[y]\cong R[x,y]$. Moreover, if we have $f(x)\in R[x],\;g(y)\in R[y]$, then $R[x]/\langle f\rangle \otimes_{R}R[y]/\langle g\rangle \cong R[x,y]/\langle f,g\rangle$. This can be thought of by considering the latter&amp;rsquo;s basis being composed of the product of the bases $\set{1,x,...,x^{\text{deg}(f)-1}}$ and $\set{1,y,...,y^{\text{deg}(g)-1}}$.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Proof of prop.&lt;/em&gt; Taking the above as fact, we write:
&lt;/p&gt;
$$\frac{\mathbb{C}[v_{1},...,v_{n}]}{\langle v_{1}^{k}-1,...,v_{n}^{k}-1\rangle}=\bigotimes_{i\in[n]}\frac{\mathbb{C}[v_{i}]}{\langle v_{i}^{k}-1\rangle},$$&lt;p&gt;
where the tensor product is taken over $\mathbb{C}$. Next we observe that $v^{k}-1=\prod_{j=0}^{k}(v-\zeta^{j})$ by factoring the roots in $\mathbb{C}[v]$. Since this is a PID and for $j\neq j&#39;$ the polynomials $v-\zeta^{j}$ and $v-\zeta^{j&#39;}$ are coprime, $\langle v-\zeta^{j}\rangle$ are all pairwise comaximal, thus the Chinese remainder theorem applies:
&lt;/p&gt;
$$\mathbb{C}[v]/\langle v^{k}-1\rangle\cong \prod_{j=0}^{k-1}\mathbb{C}[v]/\langle v-\zeta^{j}\rangle\cong \prod_{j=0}^{k-1}\mathbb{C}=\mathbb{C}^{k}$$&lt;p&gt;
where the second isomorphism follows due to $v-\zeta^{j}$ being linear (consider $f(v)\mapsto f(\zeta^{j})$).
Now $\mathbb{C}[V]/I_{k}=\mathbb{C}^{k}\otimes_\mathbb{C} ...\otimes_\mathbb{C} \mathbb{C}^{k}=\mathbb{C}^{k^{n}}$; this can be thought of again as multiplying the $k$ bases $n$ times. Finally, take $a=(a_{1},...,a_{k^{n}}) \in\mathbb{C}^{k^{n}}$ and suppose $a^{2}=0=(0,...,0)$. Since each component $a_{i}^{2}=0$ in the field $\mathbb{C}$, it must be that $a_{i}=0\Rightarrow a=0$. Applying the lemma above, we get $I_{k}$ is radical.&lt;/p&gt;
&lt;h4 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h4&gt;&lt;p&gt;This approach constructs the set of all $k$-colorings of $G$ as an affine variety of a carefully chosen ideal, and then filters them based on inclusion of a graph polynomial, thus we get $\chi(G)=\min\set{k\mid f_{G}\not\in I_{k}}$. The computer scientist brain, at this point, is asking: how hard is it to check whether $f_{G}$ is contained in $I_{k}$? To do this, we have to compute the Gröbner basis of the ideal - a particularly nice generating set. This is, unfortunately (but predictably), exponentially hard. This technique does not really give us a more optimal algorithm for graph coloring, but it&amp;rsquo;s an interesting perspective on a problem that we usually think of as purely combinatorial.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;the-second-construction&#34;&gt;The second construction
&lt;/h3&gt;&lt;p&gt;# todo :D&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Chromatic Number of a Random Graph</title>
        <link>https://markovian.net/post/chrom_num_rand_graph/</link>
        <pubDate>Sat, 14 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://markovian.net/post/chrom_num_rand_graph/</guid>
        <description>&lt;h3 id=&#34;introduction&#34;&gt;Introduction
&lt;/h3&gt;&lt;p&gt;This is a really clean result I quite like about bounding the deviation of the chromatic number of a random graph. It seems like something that would be incredibly difficult to even argue about, but it only takes a mildly clever random process construction combined with a generic deviation bound inequality, and the answer comes out in two lines.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;the-tricks&#34;&gt;The Tricks
&lt;/h3&gt;&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Doob martingale&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Let $X$ be a random variable with $E[X)]&lt;\infty$ and let $(\mathcal{F}_{n})$ be any filtration.
Define $(Y_{n})$ by $Y_{n}=E[X|\mathcal{F}_{n}]$.
  &lt;/div&gt;
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;$(Y_{n})$ is a martingale: $$E[Y_{n+1}|\mathcal{F}_{n}]=E[E[X|\mathcal{F}_{n+1}]|\mathcal{F}_{n}]\overset{(*)}{=}E[X|\mathcal{F}_{n}]=Y_{n}$$$(*)$ follows from projection rule, since $\mathcal{F}_{n}\subset \mathcal{F}_{n+1}$.&lt;/li&gt;
&lt;li&gt;This is also called an exposure martingale, since the filtration &amp;ldquo;exposes&amp;rdquo; information about $X$ sequentially.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this particular case, we will examine two examples below. Let $G\sim\mathcal{G}(n,p)$ be an Erdos random graph, that is, a graph on $n$ vertices where each edge has independent probability of appearing $p$.
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Edge exposure martingale&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;Let $m=\binom{n}{2}$, and fix an arbitrary ordering of possible edges $e_{1},e_{2},...,e_{m}$. Define events $A_{i}=\mathbb{1}_{\set{e_{i}\in G}}$ and let $f$ be a real-valued function defined on $n$-vertex graphs, then:
&lt;/p&gt;
$$X_{i}=E[f(G)\mid \sigma(A_{1},...,A_{i})]\quad\text{ for }i\in[0,m]$$&lt;p&gt;
is the edge exposure martingale.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Note that at $i=0$, there is no information, $X_{0}=E[f(G)]$, and at $i=m$, every edge is revealed, $\sigma(A_{1},...,A_{m})=\sigma(G)$, so $X_{m}=f(G)$.&lt;/p&gt;
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Vertex exposure martingale&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;Fix an arbitrary ordering of vertices $v_{1},...,v_{n}$. Then for $i\in[1,n]$ define
&lt;/p&gt;
$$X_{i}=E\bigg[f(G)\mid \sigma(G[v_{1},...,v_{i}])\bigg],$$&lt;p&gt;
where $G[S]$ for $S\subseteq V$ is the induced subgraph of $S$.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;As above, we can see that $X_{0}=E[f(G)]$ and $X_{n}=f(G)$.
Next, to bound the concentration of the chromatic number, we need, well, a concentration bound.
&lt;div class=&#34;callout callout-lemma&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Lemma: &lt;em&gt;Azuma-Hoeffding inequality&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;For martingale $(X_{t})$ with bounded increments $|X_{t+1}-X_{t}|\leq c_{t}\text{ a.s.}$, time $T$, and any $\varepsilon&gt;0$:
&lt;/p&gt;
$$P\bigg(|X_{T}-X_{0}|&gt;\varepsilon\bigg) \leq 2\exp\left(\frac{-2\varepsilon^{2}}{\sum_{t\leq T}c_{t}^{2}}\right).$$
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Finally, we have all the tools in place.
&lt;div class=&#34;callout callout-theorem&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Theorem: &lt;em&gt;Theorem 1&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;Let $G\sim\mathcal{G}(n,p)$. Then
&lt;/p&gt;
$$|E[\chi(G)]-\chi(G)|\leq \sqrt{\frac{n}{2}\ln n}\quad \text{a.a.s.}$$
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;Let $X_{i}$ be the vertex exposure martingale with $\chi$  as our function. Check that
&lt;/p&gt;
$$|X_{i+1}-X_{i}|=E[\chi(G)\mid \sigma(G[v_{1},.,,,v_{i},v_{i+1}])]-E[\chi(G)\mid\sigma(G[v_{1},...,v_{i}])]\leq 1$$&lt;p&gt;
since the inclusion of a single vertex can change the chromatic number by at most $1$ (i.e., it will require a new color). Thus we get our bound, and all that&amp;rsquo;s left is to put Azuma at the wheel:
&lt;/p&gt;
$$P(|E[\chi(G)]-\chi(G)|&gt;\varepsilon)=P(|X_{0}-X_{n}|&gt;\varepsilon)\leq 2\exp\left(\frac{-2\varepsilon^{2}}{n}\right).$$&lt;p&gt;
Using $\varepsilon=\sqrt{(n/2)\ln n}$ gives us $P(\cdot &gt;\varepsilon)\leq 2\exp(-\ln n)=2/n$, which means the statement holds asymptotically almost surely as $n\rightarrow \infty$. $\square$&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;conclusion&#34;&gt;Conclusion
&lt;/h3&gt;&lt;p&gt;In fact there are results which give a much stricter bound, but they&amp;rsquo;re not as clean as this. I first learned about this during HackMIT hackathon; when we were looking for an open classroom to stay in overnight, the one we wandered into had this theorem written on the chalkboard from, I&amp;rsquo;m assuming, an earlier lecture. So I guess this is now my good luck charm theorem.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Andy the Ant and Markov Chain Collapse</title>
        <link>https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/</link>
        <pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate>
        
        <guid>https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/</guid>
        <description>&lt;h3 id=&#34;1-the-problem-and-the-solution&#34;&gt;1. The problem (and the solution)
&lt;/h3&gt;&lt;p&gt;&lt;img src=&#34;https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/20260201160934.png&#34;
	width=&#34;1920&#34;
	height=&#34;1080&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;
This is a puzzle on 3Blue1Brown&amp;rsquo;s talent page posted by Jane Street. It&amp;rsquo;s uncharacteristically easy for JS, being pretty much a textbook ergodic Markov chain problem. Essentially all it takes is to observe this is a symmetric random walk on a finite undirected aperiodic graph (the pentagons and hexagons are the vertices), to which we apply the standard formula for mean recurrence time $E[T_x]=2m/d(x)$, where $m$ is the number of edges.&lt;/p&gt;
&lt;p&gt;If the problem was not so easy (directed or infinite graph, etc), we would have to build the transition matrix $M$ and solve for the stationary distribution (eigenvector) $\pi M =\pi$. This is also not hard, in principle, though we&amp;rsquo;d spent some time computing it if the number of vertices is large. This post is about a way to make it quite a bit simpler still by exploiting the structure of the problem (but really I wanted an excuse to apply group theory).&lt;/p&gt;
&lt;h3 id=&#34;2-and-the-other-part&#34;&gt;2. And the other part
&lt;/h3&gt;&lt;p&gt;What&amp;rsquo;s cool here is that the graph is not only undirected and unweighted, but also highly structurally symmetric. If we begin in the red pentagon in the picture and take two steps to the purple pentagon, it is the same as being in any of the 4 other colored pentagons that are 2 steps away from red.&lt;/p&gt;
&lt;p&gt;All we need to notice here is that the football graph is &amp;ldquo;radial&amp;rdquo;, consisting of concentric layers around the origin.
&lt;img src=&#34;https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/20260201164240.png&#34;
	width=&#34;794&#34;
	height=&#34;694&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;The black dots are the hexagon vertices&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;114&#34;
		data-flex-basis=&#34;274px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the trick: call $F=(V,E)$ the graph (for football) and let $\text{Aut}(F)$ be the automorphism group of the graph. A graph automorphism is a function $f:V\rightarrow V$ that preserves vertex adjacency, i.e. $\{v_{1}v_{2}\}\in E\Rightarrow \{f(v_{1})f(v_{2})\}\in E$. In other words, such a function simply relabels the vertices, maintaining the structure of the graph.&lt;/p&gt;
&lt;p&gt;$\text{Aut}(F)$ consists of all such functions, but for our purposes we only need some of them.
We will take $G_{0}=\text{stab(0)}\leq \text{Aut}(F)$ to be the stabilizer subgroup of $0$ (the origin pentagon vertex). What this means is any $f\in G_{0}$ is an automorphism of the graph that fixes the origin in place. The desired consequence of this is that it also fixes the relative distances of all vertices to the origin, since the adjacencies are preserved.&lt;/p&gt;
&lt;p&gt;For instance, consider the hexagon vertex between the red, blue, and green pentagon vertices on the image above; call this point $w$. An $f\in G_{0}$ must fix the origin, so $f(\text{red})=\text{red}$, but the preservation of adjacencies means that $f(w)$ is linked to $\text{red}$ under the automorphism, so $w$ must be mapped to one of the $5$ neighboring vertices around $\text{red}$. In other words, under such a relabelling, $w$ remains in the same layer.&lt;/p&gt;
&lt;p&gt;With this, we consider the orbits of the action of $G_{0}$ on $F$: two vertices $v_{1},v_{2}$ are in the same orbit if $\exists f\in G_{0}\text{ s.t. }f(v_{1})=v_{2}$. As we see in the example above, the group action cleanly partitions the vertices of the graph into these orbits based on the &amp;ldquo;layer&amp;rdquo; they&amp;rsquo;re in away from the origin.
&lt;img src=&#34;https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/20260201170336.png&#34;
	width=&#34;699&#34;
	height=&#34;668&#34;
	
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;104&#34;
		data-flex-basis=&#34;251px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s kinda cute that in this graph, due to the radial structure, the group action orbits are literal orbits around the origin vertex.&lt;/p&gt;
&lt;p&gt;We can finally collapse the graph by factoring out the symmetries and obtain the resulting Markov chain $F/G_{0}$. What we have done, essentially, is reduce the chain on the original graph (the surfaces of the football) to a chain on orbits under radial symmetry.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://markovian.net/post/andy_the_ant_and_markov_chain_collapse/20260201171351.png&#34;
	width=&#34;788&#34;
	height=&#34;387&#34;
	
	loading=&#34;lazy&#34;
	
		alt=&#34;Markov chain on orbits&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;203&#34;
		data-flex-basis=&#34;488px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;There are in total 8 orbits, with each having 5 vertices, except for 0th and 7th, which have only 1.&lt;/p&gt;
&lt;h3 id=&#34;3-conclusion&#34;&gt;3. Conclusion
&lt;/h3&gt;&lt;p&gt;This reduces our 32x32 transition matrix to an 8x8 one basically for free! The $F/G_{0}$ chain gives us an easy eigenvector calculation (the answer to the puzzle is $E_{0}[T_{0}]=36$ btw). Of course, the initial problem has a small enough state space that it is not difficult at all to solve it directly (we only save on time inputting the matrix), but it is clear that on a large Markov chain a symmetry-exploiting simplification like this can reduce the required compute by orders of magnitude.&lt;/p&gt;
</description>
        </item>
        <item>
        <title>How to Measure</title>
        <link>https://markovian.net/post/how_to_measure/</link>
        <pubDate>Tue, 30 Dec 2025 00:00:00 +0000</pubDate>
        
        <guid>https://markovian.net/post/how_to_measure/</guid>
        <description>&lt;h3 id=&#34;0-what-is-this&#34;&gt;0. What is this?
&lt;/h3&gt;&lt;p&gt;This is intended to be an in-detail walkthrough of how measure-theoretic probability works under the hood. When I took my first course on this topic, I found that there was not a lot of material that actually connected the measure theory &amp;ldquo;machine code&amp;rdquo; to probability problems. I&amp;rsquo;ve put together some explanations and examples that would&amp;rsquo;ve been useful to me when I first learned about it. This assumes knowledge of (very) basic measure theory &amp;amp; probability.&lt;/p&gt;
&lt;h3 id=&#34;1-heres-what-you-have-to-know&#34;&gt;1. Here&amp;rsquo;s what you have to know
&lt;/h3&gt;&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Measure Space&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;$(\Omega,\mathcal{F},\mu)$ is a measure (probability) space, where:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\Omega$ is a set (of outcomes);&lt;/li&gt;
&lt;li&gt;$\mathcal{F}$ is a $\sigma$-algebra on $\Omega$, consisting of subsets of $\Omega$ and satisfying the following:
&lt;ol&gt;
&lt;li&gt;$\varnothing,\Omega\in\mathcal{F}$;&lt;/li&gt;
&lt;li&gt;$A\in\mathcal{F}\Rightarrow A^{c}\in\mathcal{F}$ (stable under complement);&lt;/li&gt;
&lt;li&gt;$\set{A_{n}}\in\mathcal{F}\Rightarrow \bigcup_{n}A_{n}\in\mathcal{F}$ (stable under countable union);&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;In probability, this is the event space.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;$\mu:\mathcal{F}\rightarrow [0,\infty]$ is the measure function
&lt;ul&gt;
&lt;li&gt;$\mu$ satisfies $\sigma$-additivity, i.e.: $\mu(\bigsqcup_{n}A_{n})=\sum_{n}\mu(A_{n})$;&lt;/li&gt;
&lt;li&gt;$\mu(\varnothing)=0$;&lt;/li&gt;
&lt;li&gt;In probability, the measure is written as $P$, has codomain $[0,1]$, and $P(\Omega)=1$.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Comments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$\mathcal{F}$ is stable by (countable) intersection;&lt;/li&gt;
&lt;li&gt;$P(\varnothing)=0$ and $P(\Omega)=1$ mean that &lt;em&gt;something&lt;/em&gt; must happen;&lt;/li&gt;
&lt;li&gt;The trivial (coarsest) $\sigma$-algebra over $\Omega$ is $\set{\Omega,\varnothing}$, and the fullest (finest) is $\mathcal{P}(\Omega)$ (if $\Omega$ is countable).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Random variable&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;A (real-valued) random variable in a probability space $(\Omega,\mathcal{F},P)$ is a function $X:\Omega\rightarrow \mathbb{R}$ that is measurable, i.e., satisfies:
&lt;/p&gt;
$$\forall B\in\mathcal{B}(\mathbb{R})\quad X^{-1}(B)\in\mathcal{F}.$$&lt;p&gt;
where $\mathcal{B}(\mathbb{R})$ is the Borel $\sigma$-algebra of the real line.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you have no measure theory background, $\mathcal{B}(\mathbb{R})$ might be hard to conceptualize. It is not the powerset of $\mathbb{R}$, but it may be useful to think of it that way. The subsets of $\mathbb{R}$ which do not fall in it require nontrivial and fairly contrived constructions that will never come up in a probability application.&lt;/p&gt;
&lt;p&gt;Here are some useful identities/definitions about expectation:
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;ul&gt;
&lt;li&gt;$E[X\mid Y]=E[X\mid\sigma(Y)]$ (this is just a shorthand)&lt;/li&gt;
&lt;li&gt;$E[X]=E[E[X\mid \mathcal{A}]]$ (law of total expectation)&lt;/li&gt;
&lt;li&gt;$E[\mathbb{1}_{A}X]=E[\mathbb{1}_{A}E[X\mid\mathcal{A}]]\quad \forall A\in\mathcal{A}\quad$ (def. of conditional expectation)&lt;/li&gt;
&lt;li&gt;$E[aX+bY]=aE[X]+bE[Y]$ (linearity of expectation)&lt;/li&gt;
&lt;li&gt;$E[X\mid \mathcal{A}]=X$ when $X$ is $\mathcal{A}$-measurable and $E[X]$ when $X,\mathcal{A}$  indep.&lt;/li&gt;
&lt;li&gt;$E[XY\mid\mathcal{A}]=XE[Y\mid\mathcal{A}]$ when $X$ is $\mathcal{A}$-measurable&lt;/li&gt;
&lt;/ul&gt;

  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;h3 id=&#34;2-the-conditioner&#34;&gt;2. The conditioner
&lt;/h3&gt;&lt;p&gt;Let $(\Omega,\mathcal{F},P)$ be a probability space and let $A\in\mathcal{F}$ be an event. In this segment, we will examine what it means to condition $P(A\mid \cdot)$ on a $\sigma$-algebra.&lt;/p&gt;
&lt;div class=&#34;callout callout-note&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;●&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Note&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    I want to emphasize that when we write $P(A)$, what we mean mathematically is $E[\mathbb{1}_{A}]=\int_{\Omega}\mathbb{1}_{A}dP$, and more explicitly $\int_{\Omega}\mathbb{1}_{A}(\omega)dP(\omega)$, where $\mathbb{1}_{A}(\cdot)$ is in fact a random variable.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Let&amp;rsquo;s look at the easiest case: suppose $B\in\mathcal{F}$ is another event, then $P(A\mid B)$ is just a number. Here, we simply condition on the realization that $B$ occurs.&lt;/p&gt;
&lt;p&gt;What if we don&amp;rsquo;t know whether it does? In such a case, we would condition on $\sigma(B)$, the $\sigma$-algebra generated by this event. The interesting thing is that $P(A\mid\sigma(B))$ is in fact a random variable (a measurable function of $\omega$). Intuitively, $\sigma(B)$ gives us some information about $A$, but the information is random and dependent on where $\omega$ lands.
Since $\sigma(B)=\set{\varnothing,B,B^{c},\Omega}$ has two nontrivial possibilities, the value of $P(A\mid\sigma(B))(\omega)$ depends on whether $\omega\in B$ or $\in B^{c}$:
&lt;/p&gt;
$$P(A\mid\sigma(B))=\mathbb{1}_{B}P(A|B)+\mathbb{1}_{B^{c}}P(A|B^{c}).$$&lt;p&gt;
This works out nicely because $\set{B,B^{c}}$ is a partition of the outcome space.
We can easily extend this &amp;ndash; let $\set{B_{n}}$ be a partition, i.e., a collection of disjoint subsets of $\Omega$ that add up to the whole set. Then:
&lt;/p&gt;
$$P(A\mid\sigma\set{B_{n}})=\sum\limits_{n}\mathbb{1}_{B_{n}}P(A\mid B_{n}).$$&lt;p&gt;
We extend this further to compute the expectation of a general random variable:
&lt;/p&gt;
$$E[X\mid\sigma\set{B_{n}}]=\sum\limits_{n}\mathbb{1}_{B_{n}} E[X\mid B_{n}]=\sum\limits_{n}\mathbb{1}_{B_{n}} \frac{1}{P(B_{n})}\int_{B_{n}}XdP.$$&lt;h3 id=&#34;3-measurability&#34;&gt;3. Measurability
&lt;/h3&gt;&lt;p&gt;Let $\mathcal{H}\subset \mathcal{F}$ and $A\in \mathcal{F}$. The definition of measurable functions tells us that $A$ being $\mathcal{H}$-measurable means that $\forall B\in\mathcal{B}(\mathbb{R}), \mathbb{1}_{A}^{-1}(B)\in \mathcal{H}$. There are four cases, dictated by whether $0$ and $1$ are in $B$. If they both are or aren&amp;rsquo;t, we get that the preimage is respectively $\Omega$ or $\varnothing$, which is trivially true as $\mathcal{H}$ contains them by definition. If only $1\in B$, then the preimage is $\set{\omega\in A}$, and if $0\in B$ it is $\set{\omega\notin A}$. Note that it is sufficient for one of these to be in $\mathcal{H}$, since the other will be contained automatically as complement. Since $\sigma(A)\subseteq \mathcal{H}$, $A$ is thus completely determinable. So we have:
&lt;/p&gt;
$$A\in\mathcal{H}\Leftrightarrow \mathbb{1}_{A}(\cdot)\; \mathcal{H}\text{-measurable }\Leftrightarrow P(A\mid\mathcal{H})=E[\mathbb{1}_{A}\mid\mathcal{H}]=\mathbb{1}_{A}\in \set{0,1}\text{ a.s. }$$&lt;p&gt;Let&amp;rsquo;s now consider that $0&lt; P(A\mid\mathcal{H})&lt;1 \text{ a.s.}$ This is a fairly strong statement about the relationship of $A$ and $\mathcal{H}$; intuitively it means that the occurrence of $A$ cannot be deterministically established by any event in $\mathcal{H}$:
&lt;/p&gt;
$$\forall B\in\mathcal{H}\text{ with }P(B)&gt;0, P(A\cap B)\neq 0,1.$$&lt;p&gt;
Define $\mathcal{H&#39;}=\mathcal{H}\lor \sigma(A)$, the $\sigma$-algebra generated by including $A$ into $\mathcal{H}$. What does a $A&#39;\in\mathcal{H&#39;}$ look like? Here, $\set{A,A^{c}}$ partitions the outcomes in two, so $A&#39;$ will consists of the union of some set $B\in\mathcal{H}$ intersecting with one part, and some other set $C$ intersecting with the other: $A&#39;=(B\cap A)\cup (C\cap A^{c})\text{ with }B,C\in\mathcal{H}$. We can view the extension by $\sigma(A)$ as adding one bit of information into the event space.&lt;/p&gt;
&lt;p&gt;![[Pasted image 20251225190318.png]]
$A&#39;$ aligns exactly with $B$ in $A$, and with $C$ in $A^{c}$.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a result that simplifies the form for indeterminate events in $\mathcal{H}&#39;$:
&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;Suppose $P(A\mid\mathcal{H})\in(0,1)\text{ a.s.}$, $\mathcal{H&#39;}=\mathcal{H}\lor\sigma(A)$, and $A&#39;\in\mathcal{H&#39;}$. Then:
&lt;/p&gt;
$$P(A&#39;\mid\mathcal{H})\in (0,1)\text{ a.s.}\iff \exists B\in\mathcal{H}\text{ s.t. }A&#39;=(B\cap  A)\cup  (B^{c}\cap A^{c}).$$
  &lt;/div&gt;
&lt;/div&gt;

&lt;/p&gt;
&lt;p&gt;This gives us a characterization that an event in $\mathcal{H&#39;}$ has $0&lt; P(A&#39;\mid\mathcal{H})&lt;1$, meaning that its occurrence can never be determined from an event in $\mathcal{H}$, exactly IFF you can find $B$ and $C$ to represent $A&#39;$ with the property $B^{c}=C$. Since $B\mapsto (B\cap  A)\cup  (B^{c}\cap A^{c})$ is a bijective map, there are exactly $|\mathcal{H}|$ such indeterminate events.&lt;/p&gt;
&lt;div class=&#34;callout callout-proof&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;∎&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proof&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    &lt;p&gt;$(\Rightarrow )$ Given $0&lt; P(A&#39;\mid\mathcal{H})&lt;1\text{ a.s.}$, take $B,C\in\mathcal{H}\text{ s.t. }A&#39;=(B\cap A)\cup (C\cap A^{c})$. Then:
&lt;/p&gt;
$$\begin{align*}
P(A&#39;\mid\mathcal{H})
&amp;= P((B\cap A)\sqcup (C\cap A^{c})\mid\mathcal{H})\\
&amp;= E[\mathbb{1}_{A}\mathbb{1}_B\mid\mathcal{H}]+E[\mathbb{1}_{C}\mathbb{1}_{A^{c}}\mid\mathcal{H}]\\
&amp;= \mathbb{1}_{B}P(A\mid\mathcal{H})+\mathbb{1}_{C}P(A^{c}\mid\mathcal{H})\\
&amp;\in (0,1).
\end{align*}$$&lt;p&gt;
The $&gt;0$ implies that $B\cup C=\Omega$, and $&lt;1$ gives us $B\cap C=\varnothing$, thus they are complements.
$(\Leftarrow )$ is easy.&lt;/p&gt;

  &lt;/div&gt;
&lt;/div&gt;


&lt;h3 id=&#34;4-change-of-measure-and-independence&#34;&gt;4. Change of measure and independence
&lt;/h3&gt;&lt;p&gt;Let&amp;rsquo;s examine another angle of the property $0&lt; P(A\mid\mathcal{H})&lt;1 \text{ a.s.}$ Since $\mathcal{H}$ is an arbitrary sub-$\sigma$-algebra (not necessarily formed by a nice partition), we can&amp;rsquo;t really tell how the probability is changed by the conditioning.&lt;/p&gt;
&lt;div class=&#34;callout callout-definition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◇&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Definition: &lt;em&gt;Equivalence of measures&lt;/em&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    Let $\mu,\nu:\mathcal{F}\rightarrow \mathbb{R}_{\geq0}$. They are considered equivalent if their null sets are the same, i.e., $\forall F\in\mathcal{F},\; \mu(F)=0\iff \nu(F)=0.$
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;We can construct a measure $Q:\mathcal{F}\rightarrow [0,1]$ that is structurally equivalent to $P$, but under which $A$ is independent of $\mathcal{H}$. For this, we will &amp;ldquo;re-weigh&amp;rdquo; the measures for events in a way that gives us $Q(A\mid\mathcal{H})=Q(A)=\alpha$ for some $\alpha\in(0,1)$ of our choosing.&lt;/p&gt;
&lt;p&gt;Consider the function
&lt;/p&gt;
$$f_\alpha:\Omega\rightarrow [0,1]\text{ with }f_{\alpha}= \left(\alpha \frac{\mathbb{1}_{A}}{P(A\mid\mathcal{H})} + \left(1-\alpha\right)\frac{\mathbb{1}_{A^{c}}}{P(A^{c}\mid\mathcal{H})}\right).$$&lt;p&gt;
What does this function do? When $\omega$ lands in $A$, we scale the it by the fixed constant $\alpha$ and normalize by $P(A\mid\mathcal{H})$, which is just a &amp;ldquo;fixed&amp;rdquo; function; when it lands outside we likewise scale and normalize by the complements. With this $f$, we can construct our weighted measure of interest:
&lt;/p&gt;
$$\forall B\in\mathcal{F},\quad Q_\alpha(B):=\int_{B}fdP=E_{P}\left[\alpha \frac{\mathbb{1}_{A\cap B}}{P(A\mid\mathcal{H})} + \left(1-\alpha\right)\frac{\mathbb{1}_{A^{c}\cap B}}{P(A^{c}\mid\mathcal{H})}\right].$$&lt;p&gt;
Equivalently, this means that $f=\frac{dQ}{dP}$, the Radon-Nikodym derivative.&lt;/p&gt;
&lt;p&gt;&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    $Q_{\alpha}$ is a probability measure.
  &lt;/div&gt;
&lt;/div&gt;


&lt;em&gt;Proof.&lt;/em&gt; We show that $E_{P}[\mathbb{1}_{A}/P(A\mid\mathcal{H})] =E_{P}[1/P(A\mid\mathcal{H})E_{P}[\mathbb{1}_{A}]]=1$, and the second term is likewise $1-\alpha$. Thus $Q_{\alpha}(\Omega)=\alpha+1-\alpha=1$.&lt;/p&gt;
&lt;p&gt;&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    $Q_{\alpha}$ is equivalent to $P$.
  &lt;/div&gt;
&lt;/div&gt;


&lt;em&gt;Proof.&lt;/em&gt;
$(\Rightarrow )$ Let $N\in \mathcal{F}\text{ with }Q_{\alpha}(N)=0$.
Both terms of $f$ are nonnegative, so by $0&lt; P(A\mid\mathcal{H})&lt;1$, each of them must be $0$. $P(A\cap  N)=0\land P(A^{c}\cap N)=0\Rightarrow P(N)=0$. $(\Leftarrow )$  is trivial.&lt;/p&gt;
&lt;div class=&#34;callout callout-proposition&#34;&gt;
  &lt;div class=&#34;callout-title&#34;&gt;
    &lt;span class=&#34;callout-icon&#34;&gt;◆&lt;/span&gt;
    &lt;span class=&#34;callout-label&#34;&gt;Proposition&lt;/span&gt;
  &lt;/div&gt;
  &lt;div class=&#34;callout-content&#34;&gt;
    $Q_{\alpha}(A\mid\mathcal{H})=Q_{\alpha}(A)$.
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;Proof.&lt;/em&gt; First:
&lt;/p&gt;
$$Q_{\alpha}(A)=\alpha E\left[\frac{\mathbb{1}_{A}}{P(A\mid\mathcal{H})}\right]=\alpha E\left[ \frac{1}{P(A\mid\mathcal{H})}E[\mathbb{1}_{A}\mid\mathcal{H}] \right]=\alpha.$$&lt;p&gt;
Second, let $B\in\mathcal{H}$.
Observe that $E_{P}\left[\alpha \frac{\mathbb{1}_{A\cap B}}{P(A\mid\mathcal{H})}\right] = \alpha E\left[\frac{\mathbb{1}_{B}}{P(A\mid\mathcal{H})}E[P(A\mid\mathcal{H})] \right]=\alpha P(B)$ by law of total expectation; and the second term similarly evaluates to $(1-\alpha)P(B)$. Thus $Q_{\alpha}(B)=P(B)$.
Third, $Q_{\alpha}(A\cap B)=\alpha P(B)$, since the first term evaluates as above, and the second is $0$ since $A^{c}\cap A\cap B=\varnothing$.
Finally, $Q(A\mid B)= Q(A\cap B)/Q(B) =\alpha P(B)/P(B)=\alpha$ by definition.&lt;/p&gt;
&lt;p&gt;This construction gives us the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$Q(A)=Q(A\mid B)=\alpha\in(0,1)\;\forall B\in\mathcal{H}$, so $A$ is independent of $\mathcal{H}$ under $Q$;&lt;/li&gt;
&lt;li&gt;$Q(B)=P(B)\;\forall B\in\mathcal{H}$, so all events in $\mathcal{H}$ are invariant under this scaling;&lt;/li&gt;
&lt;li&gt;All events that happen a.s. or a.s. never are maintained (equality of measures).&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
