<?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>Algorithms on CS Theorems</title><link>https://cs.lozic.me/areas/algorithms/</link><description>Recent content in Algorithms on CS Theorems</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 17 Dec 2027 12:00:00 +0100</lastBuildDate><atom:link href="https://cs.lozic.me/areas/algorithms/index.xml" rel="self" type="application/rss+xml"/><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>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>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>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>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>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>Linearity of Expectation</title><link>https://cs.lozic.me/posts/t007-linearity-of-expectation/</link><pubDate>Fri, 27 Nov 2026 12:00:00 +0100</pubDate><guid>https://cs.lozic.me/posts/t007-linearity-of-expectation/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are analyzing a hash table. You want the expected number of buckets that end
up empty. The bucket occupancies are all tangled together — one key landing in
bucket 3 makes every other bucket slightly less likely to be chosen — and the
dependencies look like they will make the sum intractable.&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>The Pigeonhole Principle</title><link>https://cs.lozic.me/posts/t001-pigeonhole-principle/</link><pubDate>Fri, 28 Aug 2026 12:00:00 +0200</pubDate><guid>https://cs.lozic.me/posts/t001-pigeonhole-principle/</guid><description>&lt;h2 id="symptom"&gt;Symptom&lt;/h2&gt;
&lt;p&gt;You are asked to build a cache key. You have a 64-bit hash, and someone on the
team says: &amp;ldquo;collisions are basically impossible, there are eighteen quintillion
values.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Or: your service assigns short IDs to uploads, six characters of base-36, and
you are wondering when you need to worry.&lt;/p&gt;</description></item></channel></rss>