<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>L2 on CS Theorems</title><link>https://cs.lozic.me/difficulties/l2/</link><description>Recent content in L2 on CS Theorems</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 24 Dec 2027 12:00:00 +0100</lastBuildDate><atom:link href="https://cs.lozic.me/difficulties/l2/index.xml" rel="self" type="application/rss+xml"/><item><title>Mutual Exclusion Without Hardware Support</title><link>https://cs.lozic.me/posts/t052-mutual-exclusion-without-hardware-support/</link><pubDate>Fri, 24 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t052-mutual-exclusion-without-hardware-support/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Two threads increment a shared counter a million times each. The final value is
1,374,522.&lt;/p&gt;
&lt;p&gt;You know why: the increment is a read, an add, and a write, and the interleavings
lose updates. The fix is a lock. But a lock is provided by your runtime, which
gets it from the OS, which gets it from a hardware instruction — &lt;code&gt;lock xchg&lt;/code&gt;,
&lt;code&gt;cmpxchg&lt;/code&gt;, &lt;code&gt;ldrex&lt;/code&gt;/&lt;code&gt;strex&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Work Stealing and Fair Scheduling</title><link>https://cs.lozic.me/posts/t109-work-stealing-and-fair-scheduling/</link><pubDate>Fri, 17 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t109-work-stealing-and-fair-scheduling/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You parallelize a recursive algorithm. Quicksort, or a tree traversal, or a
divide-and-conquer numeric kernel. You have 16 cores and a central task queue,
and you measure a speedup of 3.&lt;/p&gt;</description></item><item><title>Resolution and CDCL</title><link>https://cs.lozic.me/posts/t050-resolution-and-cdcl/</link><pubDate>Fri, 10 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t050-resolution-and-cdcl/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;SAT is NP-complete (T027). It is &lt;em&gt;the&lt;/em&gt; NP-complete problem, the one everything
else reduces to. The textbook conclusion is that you should avoid it.&lt;/p&gt;
&lt;p&gt;Meanwhile: a hardware verification team routinely discharges instances with two
million variables and ten million clauses in under a minute. Package managers
solve dependency resolution with SAT. Every bounded model checker (T049) is a SAT
solver with a front end. The planning community abandoned custom search for
SAT encodings and got faster.&lt;/p&gt;</description></item><item><title>Register Allocation Is Graph Coloring</title><link>https://cs.lozic.me/posts/t107-register-allocation-is-graph-coloring/</link><pubDate>Fri, 03 Dec 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t107-register-allocation-is-graph-coloring/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write a function with forty local variables. The x86-64 machine it runs on
has sixteen general-purpose registers, two of which are effectively spoken for.&lt;/p&gt;
&lt;p&gt;Something must decide which values live in registers and which live in memory.
Get it right and the function runs at register speed. Get it wrong and every
operation costs a load and a store, and on a modern machine an L1 hit is around
4 cycles against roughly 1 for a register, with an L2 miss costing 12 to 20 and
a trip to DRAM 200 or more.&lt;/p&gt;</description></item><item><title>Hoare Logic and Weakest Preconditions</title><link>https://cs.lozic.me/posts/t048-hoare-logic-and-weakest-preconditions/</link><pubDate>Fri, 19 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t048-hoare-logic-and-weakest-preconditions/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You changed a loop. It looks right. The tests pass. You are not confident.&lt;/p&gt;
&lt;p&gt;The reason you are not confident is that the tests check a finite set of inputs
and the loop runs on all of them. You know this. So you stare at the loop and
try to convince yourself by simulating it in your head, which works for three
iterations and then stops working.&lt;/p&gt;</description></item><item><title>Codd's Theorem</title><link>https://cs.lozic.me/posts/t069-codds-theorem/</link><pubDate>Fri, 05 Nov 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t069-codds-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AND&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Berlin&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You did not say whether to scan the table or use an index. You did not say which
of the two predicates to apply first. You did not say anything about join order,
because there is no join, but if there were you would not have said that either.&lt;/p&gt;</description></item><item><title>Parametricity: Theorems for Free</title><link>https://cs.lozic.me/posts/t045-parametricity-theorems-for-free/</link><pubDate>Fri, 15 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t045-parametricity-theorems-for-free/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Someone hands you a function and no source:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;::&lt;/span&gt; &lt;span class="n"&gt;forall&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;How much can you say about it? The instinct is nothing: any implementation is
possible, that is what polymorphism means.&lt;/p&gt;</description></item><item><title>Progress and Preservation</title><link>https://cs.lozic.me/posts/t044-progress-and-preservation/</link><pubDate>Fri, 01 Oct 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t044-progress-and-preservation/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Well-typed programs don&amp;rsquo;t go wrong.&amp;rdquo; You have heard it. You have probably said
it. And then:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// NullPointerException&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That program is well typed and it went wrong. So either the slogan is false or
it means something narrower than it sounds. It means something narrower, and the
narrowness is precisely specifiable.&lt;/p&gt;</description></item><item><title>Hindley–Milner and Principal Types</title><link>https://cs.lozic.me/posts/t043-hindley-milner-and-principal-types/</link><pubDate>Fri, 24 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t043-hindley-milner-and-principal-types/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write this in OCaml or Haskell and annotate nothing:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ocaml" data-lang="ocaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;rec&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;xs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The compiler reports &lt;code&gt;('a -&amp;gt; 'b) -&amp;gt; 'a list -&amp;gt; 'b list&lt;/code&gt;. It found the most
general possible type, on its own, with no hints, and it will reject any call
that does not fit.&lt;/p&gt;</description></item><item><title>The Curry–Howard Correspondence</title><link>https://cs.lozic.me/posts/t042-the-curry-howard-correspondence/</link><pubDate>Fri, 17 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t042-the-curry-howard-correspondence/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You write a function signature and it looks like a sentence:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-haskell" data-lang="haskell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;::&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read it aloud: if $a$ implies $b$, and $b$ implies $c$, then $a$ implies $c$.
That is transitivity of implication, a rule of logic you have known since school.
And the implementation, &lt;code&gt;f g h = h . g&lt;/code&gt;, is the &lt;em&gt;proof&lt;/em&gt; of it: given evidence for
$a$, apply the first, then apply the second.&lt;/p&gt;</description></item><item><title>The Lambda Calculus and the Y Combinator</title><link>https://cs.lozic.me/posts/t041-the-lambda-calculus-and-the-y-combinator/</link><pubDate>Fri, 10 Sep 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t041-the-lambda-calculus-and-the-y-combinator/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are writing a language, or a configuration evaluator, or a template engine,
and you need recursion. So you add a &lt;code&gt;letrec&lt;/code&gt; form, and now the evaluator needs
to bind a name before the value it names exists. You reach for a mutable cell, a
placeholder that gets patched after construction, and something about it feels
like cheating.&lt;/p&gt;</description></item><item><title>The CAP Theorem and PACELC</title><link>https://cs.lozic.me/posts/t066-the-cap-theorem-and-pacelc/</link><pubDate>Fri, 27 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t066-the-cap-theorem-and-pacelc/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Someone in a design review says &amp;ldquo;we&amp;rsquo;re AP, not CP,&amp;rdquo; and the room nods.&lt;/p&gt;
&lt;p&gt;Nobody asks which consistency, which availability, or what happens when there is
no partition, which is 99.9% of the time. The phrase has done the work of a
decision without containing one.&lt;/p&gt;</description></item><item><title>Paxos, Raft, and the Shape of Consensus</title><link>https://cs.lozic.me/posts/t064-paxos-raft-and-the-shape-of-consensus/</link><pubDate>Fri, 20 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t064-paxos-raft-and-the-shape-of-consensus/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your database has two nodes that both believe they are the primary.&lt;/p&gt;
&lt;p&gt;Each has been accepting writes. Each has a coherent log. Neither has crashed.
The old primary was declared dead by a health check that timed out during a GC
pause, a new one was elected, and then the old one woke up still holding what it
thinks is a valid lease. You now have two divergent histories of the same data
and no principled way to merge them, because both contain acknowledged writes
that customers were told had succeeded.&lt;/p&gt;</description></item><item><title>The Myhill–Nerode Theorem</title><link>https://cs.lozic.me/posts/t040-the-myhill-nerode-theorem/</link><pubDate>Fri, 06 Aug 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t040-the-myhill-nerode-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need to prove a language is not regular. Everyone points you at the pumping
lemma, and you spend an afternoon losing to it.&lt;/p&gt;
&lt;p&gt;The statement is a nest of quantifiers: for every regular language there exists
a pumping length $p$ such that for every string $w$ with $|w| \ge p$ there exists
a decomposition $w = xyz$ with $|xy| \le p$ and $|y| &amp;gt; 0$ such that for all $i
\ge 0$, $xy^i z$ is in the language. To use it you negate all of that and play a
game against an adversary who picks $p$ and the decomposition while you pick $w$
and $i$.&lt;/p&gt;</description></item><item><title>Kleene's Theorem: Regexes Are Finite Automata</title><link>https://cs.lozic.me/posts/t039-kleenes-theorem-regexes-are-finite-automata/</link><pubDate>Fri, 30 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t039-kleenes-theorem-regexes-are-finite-automata/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service goes down. CPU pinned at 100% on one core, no memory growth, no
error logs, requests timing out.&lt;/p&gt;
&lt;p&gt;The cause is a validation regex — something reasonable-looking like
&lt;code&gt;^(a+)+$&lt;/code&gt;, or in real life a URL or email validator with nested quantifiers —
matched against a 30-character string that happens not to match. The engine is
exploring an exponential number of ways to split the input among the nested
groups, and it will get there eventually, some time after the heat death of
everything.&lt;/p&gt;</description></item><item><title>The Exponential Time Hypothesis and Fine-Grained Complexity</title><link>https://cs.lozic.me/posts/t037-the-exponential-time-hypothesis-and-fine-grained-complexity/</link><pubDate>Fri, 16 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t037-the-exponential-time-hypothesis-and-fine-grained-complexity/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a string algorithm. Edit distance between two sequences, the classic
dynamic program, $O(n^2)$ time. It has been in production for years.&lt;/p&gt;
&lt;p&gt;Now the inputs are genome-scale. At $n = 10^5$ characters, $n^2 = 10^{10}$
operations, about &lt;strong&gt;10 seconds&lt;/strong&gt; at a billion ops per second. At $n = 10^6$ it is
$10^{12}$ operations, about &lt;strong&gt;1000 seconds&lt;/strong&gt; — seventeen minutes for one pair of
strings. You need to do a million pairs.&lt;/p&gt;</description></item><item><title>The No-Cloning Theorem</title><link>https://cs.lozic.me/posts/t110-the-no-cloning-theorem/</link><pubDate>Fri, 02 Jul 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t110-the-no-cloning-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have spent this whole part learning that information wants to be copied.
Entropy bounds compression (T018), Huffman hits the bound (T020), redundancy
buys error correction (T021). Every result so far treats &amp;ldquo;make another copy&amp;rdquo; as
the free operation — the thing you do without thinking, the reason &lt;code&gt;cp&lt;/code&gt; has no
interesting failure modes.&lt;/p&gt;</description></item><item><title>The Nyquist–Shannon Sampling Theorem</title><link>https://cs.lozic.me/posts/t106-the-nyquist-shannon-sampling-theorem/</link><pubDate>Fri, 25 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t106-the-nyquist-shannon-sampling-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your dashboard shows CPU utilization averaging 40%, comfortably under budget.
Users are reporting timeouts. You add more logging and the average stays at 40%.&lt;/p&gt;
&lt;p&gt;Eventually someone captures a one-second trace and finds the truth: the service
spikes to 100% for eight seconds out of every ninety, and your monitoring
samples every sixty seconds. The spikes are real, periodic, and invisible.&lt;/p&gt;</description></item><item><title>The Byzantine Generals Problem</title><link>https://cs.lozic.me/posts/t063-the-byzantine-generals-problem/</link><pubDate>Fri, 18 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t063-the-byzantine-generals-problem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;A node in your cluster is not down. It is worse than down.&lt;/p&gt;
&lt;p&gt;Its disk is returning corrupted blocks that pass the checksum because the
checksum is corrupted too. Or a NIC with a firmware bug is duplicating and
reordering frames. Or a bad memory module flipped a bit in a config value and
the node now believes it owns a shard it does not. Or someone has root on one
machine.&lt;/p&gt;</description></item><item><title>Vector Clocks and Causal Consistency</title><link>https://cs.lozic.me/posts/t059-vector-clocks-and-causal-consistency/</link><pubDate>Fri, 04 Jun 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t059-vector-clocks-and-causal-consistency/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You took the lesson from T058 and stopped trusting wall clocks. Every event now
carries a Lamport timestamp, and your writes are ordered by it. The vanishing
updates stopped.&lt;/p&gt;</description></item><item><title>Parameterized Complexity (FPT and W[1])</title><link>https://cs.lozic.me/posts/t033-parameterized-complexity-fpt-and-w1/</link><pubDate>Fri, 21 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t033-parameterized-complexity-fpt-and-w1/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your problem is NP-hard, and yet it keeps being easy.&lt;/p&gt;
&lt;p&gt;You are computing a minimum vertex cover to select monitoring points in a
network. Twelve thousand nodes. NP-hard, per T028, so you brace for the worst —
and the answer comes back in under a second, every time. The cover is small,
around thirty nodes, because the network is sparse and the interesting nodes are
few.&lt;/p&gt;</description></item><item><title>Approximation Algorithms and Ratios</title><link>https://cs.lozic.me/posts/t032-approximation-algorithms-and-ratios/</link><pubDate>Fri, 14 May 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t032-approximation-algorithms-and-ratios/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have accepted that the problem is NP-hard. Now what?&lt;/p&gt;
&lt;p&gt;The literature offers a wall of results with numbers attached: 2-approximation,
$\ln n$-approximation, PTAS, FPTAS, 0.878. Nobody explains what these numbers
buy you, how they are proved, or how they connect to the heuristic you already
wrote. Meanwhile your greedy heuristic is running in production and you have no
idea whether it is within 5% or a factor of 50 of optimal, because &lt;strong&gt;you cannot
compute the optimum to compare against&lt;/strong&gt; — that was the whole problem.&lt;/p&gt;</description></item><item><title>Streaming Lower Bounds and Sketching</title><link>https://cs.lozic.me/posts/t105-streaming-lower-bounds-and-sketching/</link><pubDate>Fri, 30 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t105-streaming-lower-bounds-and-sketching/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Product wants the daily unique-visitor count. You have a firehose of events.&lt;/p&gt;
&lt;p&gt;The obvious implementation is a set. Add each visitor ID, report the size. At a
billion distinct IDs, eight bytes each, that is 8 GB before any hash table
overhead, and in practice a &lt;code&gt;HashSet&lt;/code&gt; will cost you two to three times that.
Per day. Per dimension you want to slice by. Multiply by country, by platform,
by campaign, and the memory bill is absurd for a number nobody looks at past two
significant figures.&lt;/p&gt;</description></item><item><title>Universal Hashing</title><link>https://cs.lozic.me/posts/t104-universal-hashing/</link><pubDate>Fri, 23 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t104-universal-hashing/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service went down under a hash collision attack.&lt;/p&gt;
&lt;p&gt;Somebody noticed your web framework put POST parameters into a hash table, found
thousands of distinct keys colliding under its hash function, and posted a form
with 20,000 of them. Every insert walked a chain. Quadratic behaviour, one CPU
pinned per request, service dead. This actually happened, across PHP, Python,
Ruby, Java and .NET in 2011, and again against Rust&amp;rsquo;s default &lt;code&gt;HashMap&lt;/code&gt; before
it switched to SipHash.&lt;/p&gt;</description></item><item><title>Minimum Description Length and Occam's Razor</title><link>https://cs.lozic.me/posts/t025-minimum-description-length-and-occams-razor/</link><pubDate>Fri, 16 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t025-minimum-description-length-and-occams-razor/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You fit a model. It scores 94% on training data and 71% on held-out data.&lt;/p&gt;
&lt;p&gt;So you simplify: fewer parameters, more regularization. Training drops to 88%,
held-out rises to 84%. You simplify further and both drop. Somewhere in there
was an optimum, and you found it by trial and error, with a validation set and
patience.&lt;/p&gt;</description></item><item><title>Kolmogorov Complexity</title><link>https://cs.lozic.me/posts/t023-kolmogorov-complexity/</link><pubDate>Fri, 09 Apr 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t023-kolmogorov-complexity/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have two files, each exactly one megabyte.&lt;/p&gt;
&lt;p&gt;The first is a megabyte of the byte &lt;code&gt;0x00&lt;/code&gt;. The second is a megabyte from
&lt;code&gt;/dev/urandom&lt;/code&gt;. Gzip the first and you get a few hundred bytes. Gzip the second
and you get slightly more than a megabyte, because the header costs something and
there is nothing to exploit.&lt;/p&gt;</description></item><item><title>Karp's 21 Problems</title><link>https://cs.lozic.me/posts/t028-karps-21-problems/</link><pubDate>Fri, 26 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t028-karps-21-problems/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Three weeks into a project, you are still trying to write an exact algorithm.&lt;/p&gt;
&lt;p&gt;The problem is yours and it looks specific: assign delivery vans to routes such
that every stop is covered, no van exceeds its capacity, and the total distance
is minimized. Nothing in the literature matches it exactly. So you keep going —
better data structures, smarter pruning, a cleverer greedy pass with a repair
step — and each version works on your test set and falls over on production
data.&lt;/p&gt;</description></item><item><title>The Cook–Levin Theorem</title><link>https://cs.lozic.me/posts/t027-the-cook-levin-theorem/</link><pubDate>Fri, 19 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t027-the-cook-levin-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You know reductions (T017): to prove your problem hard, reduce a known-hard
problem to it. Fine. But that begs the obvious question, and it is the question
a sharp colleague asks the first time you use the technique.&lt;/p&gt;</description></item><item><title>The Hamming Bound and Error-Correcting Codes</title><link>https://cs.lozic.me/posts/t022-the-hamming-bound/</link><pubDate>Fri, 05 Mar 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t022-the-hamming-bound/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are specifying ECC memory for a fleet. The vendor quotes SECDED: single
error correct, double error detect, at 8 check bits per 64 data bits. That is
12.5% overhead, and someone in the room asks the obvious question — why not
correct two errors? Or three? What does it cost?&lt;/p&gt;</description></item><item><title>Huffman Coding Is Optimal</title><link>https://cs.lozic.me/posts/t020-huffman-coding-is-optimal/</link><pubDate>Fri, 19 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t020-huffman-coding-is-optimal/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are compressing a log file. The symbols are wildly skewed: &lt;code&gt;INFO&lt;/code&gt; is 90% of
the lines, &lt;code&gt;WARN&lt;/code&gt; is 9%, &lt;code&gt;ERROR&lt;/code&gt; and &lt;code&gt;FATAL&lt;/code&gt; split the rest. Fixed-width
two-bit codes give you exactly 2 bits per symbol, and entropy (T018) says the
floor is about 0.53 bits. There is a factor of four sitting there.&lt;/p&gt;</description></item><item><title>Balls Into Bins and the Power of Two Choices</title><link>https://cs.lozic.me/posts/t009-balls-into-bins/</link><pubDate>Fri, 12 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t009-balls-into-bins/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have 100 backends and a load balancer hashing request IDs to pick one.
Expected load per backend is exactly 1%, and you have checked the hash is good.&lt;/p&gt;</description></item><item><title>Reductions</title><link>https://cs.lozic.me/posts/t017-reductions/</link><pubDate>Fri, 05 Feb 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t017-reductions/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a new problem. Your build system needs to decide whether two
configuration files can ever produce conflicting outputs. Or your linter needs to
decide whether a regex with backreferences can match a string of a given length.
Or your scheduler needs to decide whether a set of periodic tasks is feasible.&lt;/p&gt;</description></item><item><title>Godel's Incompleteness Theorems</title><link>https://cs.lozic.me/posts/t015-godels-incompleteness-theorems/</link><pubDate>Fri, 22 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t015-godels-incompleteness-theorems/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The proof assistant is failing you. You are trying to verify a piece of
concurrent code in Coq or Lean, and the tool will not accept your termination
argument. A colleague says, without much conviction, &amp;ldquo;well, Gödel — you can&amp;rsquo;t
prove everything anyway.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Kleene's Recursion Theorem</title><link>https://cs.lozic.me/posts/t014-kleenes-recursion-theorem/</link><pubDate>Fri, 15 Jan 2027 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t014-kleenes-recursion-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Write a program that prints its own source code. No file I/O, no reading
&lt;code&gt;__file__&lt;/code&gt;, no cheating.&lt;/p&gt;
&lt;p&gt;The first attempt fails instantly. To print the source you must contain the
source, and then the containing text is also part of the source, so you must
contain that too. The regress is obviously infinite, and most people conclude
after ten minutes that the task is impossible.&lt;/p&gt;</description></item><item><title>Concentration Bounds (Chernoff/Hoeffding)</title><link>https://cs.lozic.me/posts/t008-concentration-bounds/</link><pubDate>Fri, 18 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t008-concentration-bounds/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Your service handles four billion requests a month and you want the error rate.
Computing it exactly means a job over four billion log lines. Someone suggests
sampling ten thousand of them.&lt;/p&gt;</description></item><item><title>Amortized Analysis and the Potential Method</title><link>https://cs.lozic.me/posts/t006-amortized-analysis-and-the-potential-method/</link><pubDate>Fri, 11 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t006-amortized-analysis-and-the-potential-method/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You have a dynamic array. &lt;code&gt;push&lt;/code&gt; writes one element and bumps a counter, which
is clearly $O(1)$ — except when the array is full, in which case it allocates a
new buffer of twice the size, copies every element across, and frees the old
one. That is $O(n)$.&lt;/p&gt;</description></item><item><title>Adversary Arguments</title><link>https://cs.lozic.me/posts/t005-adversary-arguments/</link><pubDate>Fri, 04 Dec 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t005-adversary-arguments/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You need the largest and second-largest element of an array. The obvious way is
two passes: $n-1$ comparisons for the max, then $n-2$ for the max of the rest.
That is $2n - 3$.&lt;/p&gt;</description></item><item><title>The Omega(n log n) Comparison-Sort Bound</title><link>https://cs.lozic.me/posts/t003-comparison-sort-lower-bound/</link><pubDate>Fri, 20 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t003-comparison-sort-lower-bound/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Every general-purpose sort in every standard library is $O(n \log n)$.
Timsort, introsort, pdqsort, merge sort, heapsort. Decades of work by extremely
motivated people, an enormous amount of money riding on it, and they all land on
the same exponent.&lt;/p&gt;</description></item><item><title>Shannon Entropy and the Source Coding Theorem</title><link>https://cs.lozic.me/posts/t018-shannon-entropy-and-source-coding/</link><pubDate>Fri, 13 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t018-shannon-entropy-and-source-coding/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You gzip a log file and it drops to 8% of its original size. You gzip the
already-gzipped file and it gets slightly &lt;em&gt;bigger&lt;/em&gt;. Somebody asks why, and the
honest answer you have is &amp;ldquo;because it&amp;rsquo;s already compressed,&amp;rdquo; which is a
restatement, not a reason.&lt;/p&gt;</description></item><item><title>Rice's Theorem</title><link>https://cs.lozic.me/posts/t011-rices-theorem/</link><pubDate>Fri, 16 Oct 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t011-rices-theorem/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;The security team asks for a scanner with no false positives and no false
negatives. Every piece of malware caught, nothing legitimate quarantined.&lt;/p&gt;
&lt;p&gt;The platform team asks whether the analyzer can flag every function that
performs I/O, so the pure ones can be cached automatically.&lt;/p&gt;</description></item><item><title>Gustafson's Law and the Universal Scalability Law</title><link>https://cs.lozic.me/posts/t082-gustafsons-law-and-the-usl/</link><pubDate>Fri, 25 Sep 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t082-gustafsons-law-and-the-usl/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;Two measurements that both look like they break the previous post.&lt;/p&gt;
&lt;p&gt;The first: your Spark job takes four hours on ten nodes. You move it to a
hundred nodes and it still takes four hours — but it is now processing ten times
the data. &lt;a href="https://cs.lozic.me/posts/t081-amdahls-law/"&gt;T081&lt;/a&gt; said the ceiling was
20×, and nobody hit a ceiling.&lt;/p&gt;</description></item></channel></rss>