<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bimal Kshetri</title>
    <description>The latest articles on DEV Community by Bimal Kshetri (@bimal-py).</description>
    <link>https://dev.to/bimal-py</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2164980%2F036f507c-b6fb-4ccd-adb2-1a7f2023544f.jpg</url>
      <title>DEV Community: Bimal Kshetri</title>
      <link>https://dev.to/bimal-py</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bimal-py"/>
    <language>en</language>
    <item>
      <title>Building an LRU Cache in Python: The Data Structure Behind Every Cache</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:39:35 +0000</pubDate>
      <link>https://dev.to/bimal-py/building-an-lru-cache-in-python-the-data-structure-behind-every-cache-38dd</link>
      <guid>https://dev.to/bimal-py/building-an-lru-cache-in-python-the-data-structure-behind-every-cache-38dd</guid>
      <description>&lt;p&gt;A cache is a small box holding copies of expensive things: rows you already fetched from a database, files you already read from disk, results you already computed. Filling it is easy. The whole difficulty is what happens when it is full — something has to go, and throwing out the wrong thing means paying for the expensive work again.&lt;/p&gt;

&lt;p&gt;Least recently used, or LRU, is the policy almost everything settles on: discard the entry nobody has touched for the longest. It is a bet that the recent past predicts the near future, and on real workloads that bet pays.&lt;/p&gt;

&lt;p&gt;The interesting part is the implementation, because both of the obvious data structures fail. A dict finds a value instantly but has no idea which key is stalest. A list kept in recency order knows exactly which key is stalest, but finding an entry inside it means walking it. What makes every operation O(1) is a hash map and a doubly linked list wired together — and once you have seen it you will recognise it inside CPython's &lt;code&gt;functools.lru_cache&lt;/code&gt;, inside MySQL's buffer pool, and behind the eviction setting of every cache server you have configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;An LRU cache has a fixed capacity — say three entries — and two operations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get(key)&lt;/code&gt; returns the stored value, or a default if the key is not there.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;put(key, value)&lt;/code&gt; stores a value, evicting the least recently used entry first if the key is new and the cache is already full.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both operations count as a &lt;em&gt;use&lt;/em&gt;. That single rule is what makes the structure work: after a successful &lt;code&gt;get&lt;/code&gt; or any &lt;code&gt;put&lt;/code&gt;, that key becomes the most recently used one, and every entry that was ahead of it slides one place closer to the exit. Entries that were already colder than it do not move.&lt;/p&gt;

&lt;p&gt;So on every operation the cache must answer three questions, and it must answer all three in constant time or the whole thing is pointless:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this key present, and what is its value?&lt;/li&gt;
&lt;li&gt;Given a key that is present, mark it as the most recently used.&lt;/li&gt;
&lt;li&gt;Which entry is the least recently used, so it can be dropped?&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why a dict on its own fails
&lt;/h3&gt;

&lt;p&gt;A Python dict answers question 1 perfectly and question 3 not at all, because a dict has no notion of "oldest use".&lt;/p&gt;

&lt;p&gt;You can bolt one on: store a counter alongside each value and bump it on every access, which handles question 2 as well. But question 3 then means finding the smallest counter, and with no ordering maintained anywhere that is a scan of every entry. One eviction from a 100,000-entry cache reads 100,000 counters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why a list in recency order fails
&lt;/h3&gt;

&lt;p&gt;The opposite approach is a plain list of keys, most recently used first. Question 3 becomes free — the answer is the last element. Questions 1 and 2 become expensive, because to move a key to the front you first have to find it, and finding a value in a list means comparing your way along it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-naive-list.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-naive-list.svg" alt="A recency-ordered list where reading the coldest key requires scanning to the far end, then shifting every element right to reinsert it at the front" width="1000" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It gets worse than the scan. Deleting from position &lt;code&gt;i&lt;/code&gt; in a Python list shifts every later element one slot left, and &lt;code&gt;insert(0, key)&lt;/code&gt; shifts every single element one slot right. One read of one key costs three separate linear passes. The code below counts the scan alone: a thousand reads of a thousand-entry cache examine a million list positions, and that total grows with the square of the cache size.&lt;/p&gt;

&lt;h3&gt;
  
  
  The two structures together
&lt;/h3&gt;

&lt;p&gt;Look again at what each question needs. Question 1 is a hash map's entire job — that is what dicts are for.&lt;/p&gt;

&lt;p&gt;Question 2 is "remove this item from the middle of an ordered sequence and reattach it at the front". In an array-backed list that is O(n) because of the shifting. In a &lt;strong&gt;doubly linked list&lt;/strong&gt; it is a handful of pointer assignments, because each node holds a reference to both of its neighbours and can splice itself out without anyone searching for it. The catch is the phrase &lt;em&gt;if you already hold the node&lt;/em&gt;. Question 3 is "give me the last node", which the same list answers immediately as long as you keep a reference to the end.&lt;/p&gt;

&lt;p&gt;The trick that joins them: &lt;strong&gt;the hash map does not store the values, it stores the nodes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-structure.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-structure.svg" alt="A doubly linked list of cache nodes running from a head sentinel through C, B and A to a tail sentinel" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-hash-map.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-hash-map.svg" alt="A hash map whose keys point at nodes in the linked list rather than at values" width="1000" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One dict lookup turns a key into the exact node, and from that node the list operations are pure pointer work with no searching at all. The dict never stores any ordering. The list is never searched. Each structure does only the one thing it is fast at, and together they cover all three questions in constant time.&lt;/p&gt;

&lt;h3&gt;
  
  
  The sentinels
&lt;/h3&gt;

&lt;p&gt;One more piece separates clean code from a nest of special cases.&lt;/p&gt;

&lt;p&gt;A linked list normally forces you to ask, on every insert and removal: is this the first node? The last? The only one? Is the list empty? Each answer needs different pointer updates, and each is a place to get it wrong.&lt;/p&gt;

&lt;p&gt;The fix is to permanently allocate two nodes holding no data — a &lt;strong&gt;head sentinel&lt;/strong&gt; and a &lt;strong&gt;tail sentinel&lt;/strong&gt; — and keep every real node strictly between them. Now every real node has a neighbour on both sides, the first node is always &lt;code&gt;head.next&lt;/code&gt;, the last is always &lt;code&gt;tail.prev&lt;/code&gt;, and an empty cache is just the two sentinels pointing at each other. Every branch disappears. Two nodes of wasted memory buy you code with no edge cases in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take a cache with capacity 3 and run seven operations through it. The rightmost column is the recency list, most recently used first — exactly what the linked list holds.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Returns&lt;/th&gt;
&lt;th&gt;Cache, most recent first&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put("A", 1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put("B", 2)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;B A&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put("C", 3)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C B A&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get("A")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;A C B&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;put("D", 4)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;— evicts &lt;code&gt;B&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;D A C&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get("B")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;D A C&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get("C")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C D A&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rows are worth slowing down on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get("A")&lt;/code&gt; moves A from the back to the front.&lt;/strong&gt; Before the call the list is &lt;code&gt;C B A&lt;/code&gt;, so A is the last real node and its neighbours are B and the tail sentinel. The unlink is two assignments: B's &lt;code&gt;next&lt;/code&gt; now points at the tail sentinel, and the tail sentinel's &lt;code&gt;prev&lt;/code&gt; now points at B. The relink is four more: A's &lt;code&gt;prev&lt;/code&gt; becomes the head sentinel, A's &lt;code&gt;next&lt;/code&gt; becomes C, the head sentinel's &lt;code&gt;next&lt;/code&gt; becomes A, and C's &lt;code&gt;prev&lt;/code&gt; becomes A. B and C never move in memory. Only pointers change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-after-get.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-after-get.svg" alt="The recency list after get(A), with A relinked directly behind the head sentinel" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;put("D", 4)&lt;/code&gt; evicts B.&lt;/strong&gt; The cache already holds three entries, so something must go, and the coldest entry is whatever sits immediately before the tail sentinel — B, untouched since it was inserted. Notice that &lt;code&gt;get("A")&lt;/code&gt; on the previous line is what saved A: without that read, A would have been at the back and A would have gone instead. That is the entire policy, in one step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-after-evict.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-after-evict.svg" alt="The recency list after put(D, 4), with D at the front and B gone from the tail" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last two rows cover the remaining cases. &lt;code&gt;get("B")&lt;/code&gt; misses, because B was evicted, and a miss changes nothing — you cannot promote an entry that is not there. &lt;code&gt;get("C")&lt;/code&gt; hits and pulls C to the front, so D and A each slide one place towards eviction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Start with the version that is correct but wrong: a dict for the values, a list for the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NaiveLRUCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;An LRU cache built from a dict plus a list kept in recency order.

    It behaves correctly. It is still the wrong design, and the counter
    attached to it shows exactly why.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;  &lt;span class="c1"&gt;# most recently used first, coldest last
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scanned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# list positions examined, so the cost is visible
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# a linear scan — this is the problem
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scanned&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# shifts every later element one step left
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# shifts every element one step right
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# already present, so this is just a recency refresh
&lt;/span&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;  &lt;span class="c1"&gt;# the tail is the coldest key
&lt;/span&gt;            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="n"&gt;naive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NaiveLRUCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Read every key once, in order. The puts left key 0 at the far end, and each
# read drags the next key you are about to ask for to the far end in turn, so
# every read is a full-length scan.
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1,000 reads of a 1,000-entry cache: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scanned&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; list positions examined&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1,000 reads of a 1,000-entry cache: 1,000,000 list positions examined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A million positions examined for a thousand reads, and that is only the &lt;code&gt;index&lt;/code&gt; scan — the &lt;code&gt;del&lt;/code&gt; and the &lt;code&gt;insert(0, ...)&lt;/code&gt; each shift up to a thousand more pointers on top. Multiply the cache size by a thousand and the per-read cost multiplies by a thousand too.&lt;/p&gt;

&lt;p&gt;Here is the real thing. It is longer, and every extra line buys constant time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections.abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;One cache entry, and its two neighbours in the recency list.

    The key is stored on the node as well as in the dict. Without it, finding
    the coldest node would not tell you which dict entry to delete.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;__slots__&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prev&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;next&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_Node | None&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_Node | None&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LRUCache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A fixed-capacity cache that discards the least recently used entry.

    Two structures kept in step:

    * a dict mapping each key to its node, for O(1) lookup;
    * a doubly linked list of those nodes in recency order, most recently
      used first, for O(1) reordering and O(1) eviction.

    Both get and put count as a use and move the entry to the front.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capacity must be at least 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="c1"&gt;# Two sentinel nodes holding no data. They exist so that every real
&lt;/span&gt;        &lt;span class="c1"&gt;# node always has a neighbour on both sides, which removes every
&lt;/span&gt;        &lt;span class="c1"&gt;# "is this the first or last node?" branch from the methods below.
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Detach a node by making its two neighbours point past it.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_link_front&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Insert a node between the head sentinel and the current first node.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
        &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_link_front&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Overwriting an existing key is a use, not an insertion, so
&lt;/span&gt;            &lt;span class="c1"&gt;# nothing is evicted and the size does not change.
&lt;/span&gt;            &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_link_front&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;coldest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prev&lt;/span&gt;  &lt;span class="c1"&gt;# the node immediately before the tail sentinel
&lt;/span&gt;            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;coldest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;coldest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_link_front&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__contains__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;  &lt;span class="c1"&gt;# deliberately not a use: no reordering
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Walk the list, for demonstrations. Real callers never need this.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;


&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LRUCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get A -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;doomed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;D&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;put D=4 (drop &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;doomed&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get B -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;show&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;get C -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;put A=1              A
put B=2              B A
put C=3              C B A
get A -&amp;gt; 1           A C B
put D=4 (drop B)     D A C
get B -&amp;gt; None        D A C
get C -&amp;gt; 3           C D A
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the table from the walkthrough, produced by the code rather than by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;self.entries&lt;/code&gt; holds nodes, not values.&lt;/strong&gt; This is the one line to remember. &lt;code&gt;self.entries.get(key)&lt;/code&gt; hands you the node, and from the node you get the value, the two neighbours, and the ability to move it — without touching any other entry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sentinels erase the edge cases.&lt;/strong&gt; &lt;code&gt;_unlink&lt;/code&gt; is two assignments with no &lt;code&gt;if&lt;/code&gt; in sight, because &lt;code&gt;node.prev&lt;/code&gt; and &lt;code&gt;node.next&lt;/code&gt; are never &lt;code&gt;None&lt;/code&gt; for a real node. &lt;code&gt;_link_front&lt;/code&gt; is four assignments for the same reason. Write either method without sentinels and you need branches for "the list is empty", "the node is first", "the node is last" and "the node is the only one".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A promotion is the two methods back to back.&lt;/strong&gt; &lt;code&gt;_unlink&lt;/code&gt; then &lt;code&gt;_link_front&lt;/code&gt;: six pointer writes, in that order, whether the cache holds 3 entries or 3 million. They are not always paired — eviction calls &lt;code&gt;_unlink&lt;/code&gt; on the coldest node and never relinks it, and a brand-new node goes straight to &lt;code&gt;_link_front&lt;/code&gt; with nothing to unlink.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The node stores its own key,&lt;/strong&gt; which looks redundant until you evict. Eviction starts from the list — &lt;code&gt;self.tail.prev&lt;/code&gt; — and ends at the dict, and the only way from a node to its dict entry is the key the node carries. Drop that field and the dict grows for ever while the list stays capped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;put&lt;/code&gt; checks for an existing key before it checks capacity.&lt;/strong&gt; If a full cache evicted first and only then noticed the key was already present, it would have thrown away a live entry to make room for one that needed no room.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-put-path.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Flru-cache-put-path.svg" alt="The steps a put takes: check for an existing key, evict from the tail only if full, then link the new node at the front" width="1000" height="652"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get&lt;/code&gt; takes a &lt;code&gt;default&lt;/code&gt; instead of returning &lt;code&gt;None&lt;/code&gt;.&lt;/strong&gt; If &lt;code&gt;None&lt;/code&gt; is a legal value to cache, a bare &lt;code&gt;None&lt;/code&gt; return is indistinguishable from a miss and your code recomputes that entry every time. Passing a private sentinel object as the default is the standard way out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;__contains__&lt;/code&gt; does not promote,&lt;/strong&gt; because checking whether something is cached is not the same as using it. That is a design decision rather than a law, but make it on purpose and document it: a caller who assumes the opposite gets a different eviction pattern. And &lt;code&gt;capacity &amp;lt; 1&lt;/code&gt; is rejected up front, because a capacity of zero breaks the sentinel invariant: the first &lt;code&gt;put&lt;/code&gt; sees a full cache, reads &lt;code&gt;self.tail.prev&lt;/code&gt; for the coldest node, and gets the &lt;em&gt;head sentinel&lt;/em&gt; — the one node that has no &lt;code&gt;prev&lt;/code&gt;. &lt;code&gt;_unlink&lt;/code&gt; then raises &lt;code&gt;AttributeError&lt;/code&gt; on &lt;code&gt;node.prev.next&lt;/code&gt;. Rejecting the capacity is cheaper than teaching &lt;code&gt;_unlink&lt;/code&gt; to defend itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The version you would actually write at work
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;collections.OrderedDict&lt;/code&gt; already contains exactly this doubly linked list, implemented in C. &lt;code&gt;move_to_end&lt;/code&gt; does the promotion and &lt;code&gt;popitem&lt;/code&gt; does the eviction, so the whole cache collapses to a dozen lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderedDictLRU&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same cache in a fraction of the lines, using OrderedDict.

    Note the flipped convention: here the most recently used entry sits at
    the *end*, because that is where assigning a new key puts it.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_to_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# O(1): unlinks and relinks one node
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Hashable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;move_to_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# a new key is already last; an old one is not
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popitem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# drop from the cold end
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="n"&gt;compact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderedDictLRU&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;D&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;B&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;C&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderedDict:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from scratch:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys_mru_first&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OrderedDict: ['C', 'D', 'A']
from scratch: ['C', 'D', 'A']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same sequence, same final order. The trap is the &lt;code&gt;move_to_end&lt;/code&gt; call inside &lt;code&gt;put&lt;/code&gt;: assigning to a key that already exists updates the value but does &lt;strong&gt;not&lt;/strong&gt; reorder it, so that line is doing real work even though it looks redundant for new keys. A plain &lt;code&gt;dict&lt;/code&gt; can do this too, since deleting a key and reinserting it moves it to the end and dicts have kept insertion order since Python 3.7 — but &lt;code&gt;move_to_end&lt;/code&gt; relinks one node instead of leaving a tombstone in the dict's table for a later resize to clean up.&lt;/p&gt;

&lt;h3&gt;
  
  
  The version you should usually reach for
&lt;/h3&gt;

&lt;p&gt;If what you are caching is the return value of a function, do not build a cache at all. &lt;code&gt;functools.lru_cache&lt;/code&gt; is a decorator that wraps any function whose arguments are hashable, and it is the same structure again — a dict of keys to links, plus a circular doubly linked list of those links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;lru_cache&lt;/span&gt;


&lt;span class="nd"&gt;@lru_cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Naive recursive Fibonacci: exponential uncached, linear with a cache.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_info&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1548008755920
CacheInfo(hits=58, misses=61, maxsize=128, currsize=61)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Naive &lt;code&gt;fib(n)&lt;/code&gt; makes 2 × F(n+1) − 1 calls, so &lt;code&gt;fib(60)&lt;/code&gt; undecorated is about five trillion of them. With the decorator, 61 misses do the real work and 58 hits come straight from the cache. &lt;code&gt;cache_info()&lt;/code&gt; is the part people forget: it tells you whether the cache is earning its memory, and a hit count near zero means you are paying for bookkeeping and getting nothing. &lt;code&gt;cache_clear()&lt;/code&gt; empties it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;functools.cache&lt;/code&gt; is the unbounded variant, identical to &lt;code&gt;lru_cache(maxsize=None)&lt;/code&gt;. With no ceiling there is nothing to evict, so it drops the linked list entirely and becomes a plain dict lookup — faster, and dangerous in exactly the way you would expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;


&lt;span class="nd"&gt;@cache&lt;/span&gt;  &lt;span class="c1"&gt;# shorthand for lru_cache(maxsize=None): unbounded, nothing is ever evicted
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;collatz_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;How many halve-or-triple steps it takes to reach 1.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;collatz_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;collatz_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;collatz_steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cache_info&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;178
CacheInfo(hits=998, misses=2228, maxsize=None, currsize=2228)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;999 starting values, and the cache holds 2,228 entries — the recursion cached every intermediate value it passed through as well. On a long-running process fed unbounded input, that is a memory leak with a decorator on it. Use &lt;code&gt;functools.cache&lt;/code&gt; when the set of possible arguments is small and known, and &lt;code&gt;lru_cache(maxsize=...)&lt;/code&gt; when it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;get&lt;/code&gt;: O(1).&lt;/strong&gt; A hit is one dict lookup, then &lt;code&gt;_unlink&lt;/code&gt; (two pointer writes) and &lt;code&gt;_link_front&lt;/code&gt; (four). Six writes, every time, with no loop anywhere — the count does not depend on how many entries the cache holds. A miss is one dict lookup and nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;put&lt;/code&gt;: O(1).&lt;/strong&gt; Existing key: one dict lookup, one value assignment, the same six pointer writes. New key into a full cache: one dict lookup, two writes to unlink the coldest node, one dict delete, one node allocation, one dict insert, four writes to link the new node at the front. A fixed amount of work, still no loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eviction: O(1).&lt;/strong&gt; This is what the tail sentinel buys. &lt;code&gt;self.tail.prev&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; the least recently used node; nothing is searched, compared or sorted to find it.&lt;/p&gt;

&lt;p&gt;The honest caveat is the dict. Python dict operations are O(1) on average, not in the worst case: if every key hashes to the same bucket, lookups degrade to a linear probe sequence, which needs pathological or attacker-chosen keys. Amortisation is involved too — a dict that has seen many inserts and deletes occasionally rebuilds its table, O(capacity) work on one unlucky insert, spread over all the ones that did not trigger it.&lt;/p&gt;

&lt;p&gt;Compare the two implementations directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cache capacity&lt;/th&gt;
&lt;th&gt;Naive list: work per read&lt;/th&gt;
&lt;th&gt;Hash map + linked list&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;up to 3,000 element visits&lt;/td&gt;
&lt;td&gt;6 pointer writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;up to 300,000 element visits&lt;/td&gt;
&lt;td&gt;6 pointer writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;up to 3,000,000 element visits&lt;/td&gt;
&lt;td&gt;6 pointer writes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The naive column is &lt;code&gt;index&lt;/code&gt;, then the shift from &lt;code&gt;del&lt;/code&gt;, then the shift from &lt;code&gt;insert(0, ...)&lt;/code&gt;, each up to one full pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(capacity).&lt;/strong&gt; Not O(number of distinct keys ever seen) — that is the whole selling point of a bounded cache. Each entry costs one &lt;code&gt;_Node&lt;/code&gt;, one dict slot, and the key and value objects themselves. The &lt;code&gt;__slots__&lt;/code&gt; declaration matters more than it looks: on the CPython build used here, &lt;code&gt;sys.getsizeof&lt;/code&gt; reports 64 bytes for a four-slot node against 48 bytes plus a 104-byte instance dictionary — 152 bytes — for the same class without it. At a capacity of 100,000 entries that one line saves about 8.8 MB. The two sentinels add two more nodes, for ever, regardless of capacity.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use LRU when&lt;/strong&gt; access has temporal locality — the same keys coming back soon after they were last used — and entries cost roughly the same to produce and take roughly the same space. Web sessions, database rows, rendered templates, compiled patterns and decoded images are all recency-friendly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not hand-roll it in Python.&lt;/strong&gt; Reach for &lt;code&gt;functools.lru_cache&lt;/code&gt; if you are caching function results, and &lt;code&gt;OrderedDict&lt;/code&gt; if you need a key-value store with your own hooks. Write the node-and-sentinel version only when you need something the standard library will not give you: an eviction callback, per-entry time-to-live, a budget in bytes rather than entries, or a policy that is not quite LRU. Otherwise the C implementations win on speed and on lines of code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use LRU for scan-shaped workloads.&lt;/strong&gt; Its one spectacular failure mode is cycling repeatedly through slightly more distinct keys than the cache can hold. With capacity 1,000 and a loop over 1,001 keys, every request evicts the entry you are about to ask for next and the hit rate is exactly zero. Random eviction on that same loop hits about 99% of the time, because a randomly chosen victim is almost never the key you need next — LRU is beaten here by picking without looking. This is not hypothetical; it is what a full table scan does to a database cache. Look instead at MRU, at segmented LRU, or at admission policies that refuse to cache a key on its first sighting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not put an in-process cache in front of shared state you do not own.&lt;/strong&gt; Eight worker processes means eight independent caches, eight copies of the memory, and eight chances to serve a value another worker has already invalidated. Shared caching needs a shared cache: Redis or memcached.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This implementation is not thread-safe.&lt;/strong&gt; Two threads interleaving inside &lt;code&gt;_unlink&lt;/code&gt; corrupt the list. Every operation must hold one lock — CPython's own pure-Python &lt;code&gt;lru_cache&lt;/code&gt; allocates an &lt;code&gt;RLock&lt;/code&gt; for exactly this reason, with the comment "because linkedlist updates aren't threadsafe".&lt;/p&gt;

&lt;h3&gt;
  
  
  Other eviction policies, honestly
&lt;/h3&gt;

&lt;p&gt;LRU is a default, not a law. Each alternative beats it on some real workload.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FIFO&lt;/strong&gt; evicts whatever was inserted longest ago and never promotes on a read, so there is no bookkeeping on the hot path at all — just a queue. Measurably worse than LRU on most workloads, and measurably cheaper. CPython's &lt;code&gt;re&lt;/code&gt; module takes this deal: compiled patterns live in a plain dict capped at 512 entries, and when it fills, the oldest inserted entry is deleted. Insertion-ordered dicts make that a one-liner, and pattern reuse is not recency-shaped enough to justify more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LFU&lt;/strong&gt; evicts the least frequently used entry. It beats LRU when popularity is stable and loses badly when it shifts, because an item that was hugely popular last week keeps its enormous count and squats in the cache for ever. Practical LFU implementations therefore decay their counters over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random&lt;/strong&gt; picks a victim at random. Zero metadata, zero per-access work, immune to the scan pathology, and on many workloads within a few percentage points of LRU's hit rate. If bookkeeping is your bottleneck, this is a serious answer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MRU&lt;/strong&gt; evicts the most recently used entry. Backwards, until you meet the cyclic scan: looping over 1,001 keys with room for 1,000, MRU keeps throwing away the same slot and hits on about 99% of requests where LRU hits on none. Random eviction lands within a point of it on that same loop, so MRU is the specialist here, not the only escape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Belady's optimal algorithm&lt;/strong&gt; evicts the entry whose next use is furthest in the future. It needs the future, so it cannot be implemented — but it can be computed afterwards on a recorded trace, which makes it the yardstick every other policy is measured against.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrids&lt;/strong&gt; are what modern systems ship: ARC, 2Q, segmented LRU and W-TinyLFU (used by the Caffeine cache library for Java) all mix recency with frequency and add scan resistance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;functools.lru_cache&lt;/code&gt;&lt;/strong&gt; is this post's data structure in the standard library, and memoising an expensive pure function is what most Python programmers use it for. Read &lt;code&gt;functools.py&lt;/code&gt; and you will find a dict mapping keys to links, where each link is a four-element list holding &lt;code&gt;prev&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;result&lt;/code&gt;, joined into a circular doubly linked list. The sentinel trick is there too, in a slicker form: a single &lt;code&gt;root&lt;/code&gt; list initialised to point at itself, so the newest entry is always &lt;code&gt;root[PREV]&lt;/code&gt; and the coldest is always &lt;code&gt;root[NEXT]&lt;/code&gt;. On eviction it reuses the evicted link object as the new root rather than allocating a fresh one. Most CPython builds use a C implementation of the same design and fall back to that Python version when it is unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis&lt;/strong&gt; exposes LRU as &lt;code&gt;maxmemory-policy allkeys-lru&lt;/code&gt; — and does not implement exact LRU. A true recency list would cost two extra pointers on every key plus a list update on every access, across millions of keys. Instead each object header carries a 24-bit clock, and on eviction Redis samples a few random keys (&lt;code&gt;maxmemory-samples&lt;/code&gt;, default 5), evicts the oldest of the sample, and keeps a pool of good candidates between rounds. The Redis documentation publishes hit-rate comparisons showing that sampling 10 keys lands very close to exact LRU. Redis 4.0 added &lt;code&gt;allkeys-lfu&lt;/code&gt; beside it. The honest lesson: the exact structure is what you learn, and an approximation is what ships when memory per key is the constraint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MySQL's InnoDB buffer pool&lt;/strong&gt; splits its LRU list into a young and an old sublist, with the boundary 3/8 of the way down by default (&lt;code&gt;innodb_old_blocks_pct&lt;/code&gt; is 37). Newly read pages enter at that midpoint rather than at the head, so a one-off full table scan fills the old sublist and drains out again without displacing the hot pages — a deliberate patch for the scan pathology above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPU caches&lt;/strong&gt; approximate as well. Exact LRU across a 16-way set-associative cache needs enough state to encode an ordering of 16 lines, which is 45 bits per set; the tree-based pseudo-LRU used in real silicon needs 15. Multiply by every set in every cache in every core and the reason is obvious. Operating systems do the same thing one level up: Linux keeps active and inactive page lists with reference bits rather than a strict ordering, and PostgreSQL's shared buffers use a clock sweep with usage counters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memcached&lt;/strong&gt; has used a segmented LRU since version 1.5, with hot, warm and cold segments and a background crawler moving items between them, again because plain LRU spent too much work promoting on every read. &lt;strong&gt;Browsers and CDNs&lt;/strong&gt; evict cached responses under memory and disk pressure, where recency is the dominant signal, usually mixed with object size and remaining freshness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Not promoting on &lt;code&gt;get&lt;/code&gt;.&lt;/strong&gt; Leave the &lt;code&gt;_unlink&lt;/code&gt; and &lt;code&gt;_link_front&lt;/code&gt; pair out of &lt;code&gt;get&lt;/code&gt; and you have quietly built a FIFO cache. It works, it passes casual tests, and it evicts entries you are using constantly. Reading is a use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating a value without moving it.&lt;/strong&gt; With &lt;code&gt;OrderedDict&lt;/code&gt;, &lt;code&gt;self.entries[key] = value&lt;/code&gt; on an existing key updates the value and leaves the order untouched. The unconditional &lt;code&gt;move_to_end&lt;/code&gt; after the assignment is what makes it correct.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evicting from the list but not the dict.&lt;/strong&gt; The dict is the only thing keeping the evicted node alive, so forgetting &lt;code&gt;del self.entries[coldest.key]&lt;/code&gt; gives you a dict that grows without limit and a &lt;code&gt;len()&lt;/code&gt; that no longer matches the list. This is the bug the &lt;code&gt;key&lt;/code&gt; field on the node exists to prevent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evicting before checking whether the key is present.&lt;/strong&gt; A &lt;code&gt;put&lt;/code&gt; on an existing key needs no space, so evicting first throws away a live entry for nothing and leaves the cache one entry short.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Returning &lt;code&gt;None&lt;/code&gt; to mean "not cached".&lt;/strong&gt; If &lt;code&gt;None&lt;/code&gt; is a value you legitimately store, every read of it counts as a miss and the expensive work runs every time. Use an explicit sentinel object as the default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming &lt;code&gt;functools.lru_cache&lt;/code&gt; is free on methods.&lt;/strong&gt; The cache lives on the function object, shared by every instance, and each entry holds a reference to the &lt;code&gt;self&lt;/code&gt; it was called with — so a decorated method keeps its instance alive for as long as the entry survives. For per-instance caching use &lt;code&gt;functools.cached_property&lt;/code&gt;, or hold the cache on the instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching on unhashable arguments.&lt;/strong&gt; &lt;code&gt;lru_cache&lt;/code&gt; builds its key from the arguments, so a list or dict argument raises &lt;code&gt;TypeError&lt;/code&gt;. Convert to a tuple or a frozenset at the boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add a &lt;code&gt;peek(key)&lt;/code&gt; method that returns a value without promoting it, and show that &lt;code&gt;keys_mru_first()&lt;/code&gt; is unchanged after calling it.&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;on_evict&lt;/code&gt; callback, invoked with the key and value of every entry the cache drops, and use it to count evictions over a workload.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;resize(new_capacity)&lt;/code&gt; that evicts from the tail until the cache fits, and returns how many entries it dropped.&lt;/li&gt;
&lt;li&gt;Run one fixed sequence of key requests through your LRU cache and through a FIFO variant (identical, but &lt;code&gt;get&lt;/code&gt; does not promote), and compare the hit counts. Then find a sequence where FIFO wins.&lt;/li&gt;
&lt;li&gt;Implement an LFU cache that evicts the least frequently used entry, breaking ties by recency, and find one request pattern where it beats LRU and one where it is far worse.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;An LRU cache is the clearest example in this series of two data structures covering each other's weakness. The hash map cannot order anything; the linked list cannot search. Point the map at the list's nodes and every operation — lookup, promotion, eviction — becomes a fixed number of pointer writes, whatever the capacity. The sentinels keep that code free of branches, and the key stored on each node keeps the two structures in step.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;get&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) average — one dict lookup, then six pointer writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;put&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) average — the same, plus at most one eviction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worst case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — only if the dict degenerates on pathological keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eviction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) — always the node before the tail sentinel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(capacity) — one node and one dict slot per entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hash map plus a doubly linked list with two sentinels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;By recency, most recently used first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thread-safe&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No as written — wrap every operation in one lock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Access repeats, and memory has a hard ceiling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The workload cycles through more keys than the cache holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;functools.lru_cache&lt;/code&gt;, Redis, the InnoDB buffer pool, CPU caches (approximated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;functools.lru_cache&lt;/code&gt; / &lt;code&gt;functools.cache&lt;/code&gt;, or &lt;code&gt;OrderedDict.move_to_end&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Write it once from scratch so the mechanism is yours, then use &lt;code&gt;functools.lru_cache&lt;/code&gt; for the rest of your career. And keep the failure mode in mind: the moment your access pattern turns into a scan, the most-recently-used entry is the one you should be throwing away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash Tables in Python&lt;/a&gt; — where the O(1) lookup half of this structure comes from, and what "average case" is hiding.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/linked-lists" rel="noopener noreferrer"&gt;Linked Lists in Python&lt;/a&gt; — singly, doubly and circular lists built from scratch, including the sentinel trick used above.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/queues-and-deques" rel="noopener noreferrer"&gt;Queues and Deques in Python&lt;/a&gt; — &lt;code&gt;collections.deque&lt;/code&gt; is the same doubly linked list, written in C.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;Dynamic Programming Explained&lt;/a&gt; — memoisation is the single biggest use of &lt;code&gt;lru_cache&lt;/code&gt;, and this is why it works.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting arguments behind every complexity claim here.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Prefix Sums in Python: Answering Range Queries in Constant Time</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:39:29 +0000</pubDate>
      <link>https://dev.to/bimal-py/prefix-sums-in-python-answering-range-queries-in-constant-time-2gnf</link>
      <guid>https://dev.to/bimal-py/prefix-sums-in-python-answering-range-queries-in-constant-time-2gnf</guid>
      <description>&lt;p&gt;You have an array of a million daily sales figures and a dashboard that keeps asking "what did we take between day 12,000 and day 40,000?". Answering that by adding up 28,000 numbers is 28,000 additions per question. Ask forty different questions and you have done a million additions, most of them re-adding the same values you already added a moment ago.&lt;/p&gt;

&lt;p&gt;Prefix sums remove that repetition with one move: compute every cumulative total once, up front, then answer any range sum with a single subtraction. Building the table costs O(n). Every query afterwards costs O(1) — two array reads and a minus sign — and that cost does not change whether the range covers 3 elements or 3 million.&lt;/p&gt;

&lt;p&gt;The idea takes thirty seconds to write. The off-by-one takes most people an hour to stop getting wrong, so that is where this post spends its time first. After that, the same trick extends in three directions: rectangles in a 2D grid, range &lt;em&gt;updates&lt;/em&gt; instead of range queries, and the hash-map version that counts subarrays summing to a target in linear time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Take an array, and write down a second array that answers one question at every position: what is the total of everything before here?&lt;/p&gt;

&lt;p&gt;That second array is the prefix sum array. For &lt;code&gt;nums = [3, 1, 4, 1, 5, 9, 2, 6]&lt;/code&gt; it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index      0    1    2    3    4    5    6    7    8
nums            3    1    4    1    5    9    2    6
prefix     0    3    4    8    9   14   23   25   31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it a column at a time. &lt;code&gt;prefix[0]&lt;/code&gt; is 0, because the total of no numbers is zero. &lt;code&gt;prefix[1]&lt;/code&gt; is 3, the total of the first one element. &lt;code&gt;prefix[5]&lt;/code&gt; is 14, the total of the first five: 3 + 1 + 4 + 1 + 5. The rule that builds the whole thing is one line — &lt;code&gt;prefix[i + 1] = prefix[i] + nums[i]&lt;/code&gt; — so each entry is its left neighbour plus one new value.&lt;/p&gt;

&lt;p&gt;Two details look pedantic and are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The prefix array holds n + 1 entries, not n.&lt;/strong&gt; Eight input numbers give nine cumulative totals. The extra one is the leading zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;prefix[i]&lt;/code&gt; does not include &lt;code&gt;nums[i]&lt;/code&gt;.&lt;/strong&gt; It is the total of everything strictly to the left of index &lt;code&gt;i&lt;/code&gt;. So a prefix entry describes the &lt;em&gt;gap&lt;/em&gt; before an element rather than the element itself, which is exactly why the array needs one more slot than the input: there are nine gaps around eight numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-build.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-build.svg" alt="The input array of eight values above its nine-entry prefix array, with each prefix cell equal to its left neighbour plus the value above it" width="1000" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the payoff. The sum of &lt;code&gt;nums[left..right]&lt;/code&gt;, inclusive at both ends, is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prefix[right + 1] - prefix[left]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prefix[right + 1]&lt;/code&gt; counts everything up to and including &lt;code&gt;nums[right]&lt;/code&gt;; &lt;code&gt;prefix[left]&lt;/code&gt; counts everything strictly before &lt;code&gt;nums[left]&lt;/code&gt;. Both start from the front of the array, so subtracting cancels the shared front section and leaves exactly the elements from &lt;code&gt;left&lt;/code&gt; to &lt;code&gt;right&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the leading zero matters
&lt;/h3&gt;

&lt;p&gt;Suppose you skip the sentinel and build a prefix array the same length as the input, where &lt;code&gt;prefix[i]&lt;/code&gt; is the total of the first &lt;code&gt;i + 1&lt;/code&gt; elements. The query then reads &lt;code&gt;prefix[right] - prefix[left - 1]&lt;/code&gt;, and that expression is broken at &lt;code&gt;left = 0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In most languages &lt;code&gt;prefix[-1]&lt;/code&gt; throws. In Python it does something worse: it returns the &lt;em&gt;last&lt;/em&gt; element, which is the total of the entire array. The range sum comes back wrong, no exception is raised, and nothing points at the cause. You then patch it with &lt;code&gt;if left == 0&lt;/code&gt; and carry that special case everywhere.&lt;/p&gt;

&lt;p&gt;The leading zero deletes the special case instead of handling it. &lt;code&gt;prefix[0]&lt;/code&gt; is the sum of an empty range, which genuinely is zero, so &lt;code&gt;left = 0&lt;/code&gt; needs no different treatment from &lt;code&gt;left = 5&lt;/code&gt;. That is the shape of a &lt;strong&gt;sentinel&lt;/strong&gt;: one extra entry so the ordinary rule covers the boundary too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take the same array and ask for &lt;code&gt;nums[2..5]&lt;/code&gt; — the values &lt;code&gt;4, 1, 5, 9&lt;/code&gt;, which add up to 19.&lt;/p&gt;

&lt;p&gt;Look up two numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;prefix[6] = 23&lt;/code&gt;. That is &lt;code&gt;3 + 1 + 4 + 1 + 5 + 9&lt;/code&gt;, the first six elements, which is everything up to and including &lt;code&gt;nums[5]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prefix[2] = 4&lt;/code&gt;. That is &lt;code&gt;3 + 1&lt;/code&gt;, everything strictly before &lt;code&gt;nums[2]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subtract: 23 − 4 = 19. The &lt;code&gt;3 + 1&lt;/code&gt; at the front appears in both totals, so it cancels, and what is left is &lt;code&gt;4 + 1 + 5 + 9&lt;/code&gt;. One subtraction, no loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-query.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-query.svg" alt="The prefix row with entries 2 and 6 marked, and the four input values they fence off highlighted below" width="1000" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now the awkward case, &lt;code&gt;nums[0..3]&lt;/code&gt;: values &lt;code&gt;3, 1, 4, 1&lt;/code&gt;, total 9. The query is &lt;code&gt;prefix[4] - prefix[0]&lt;/code&gt;, which is 9 − 0 = 9. Nothing special happened. The zero at the front absorbed the boundary, exactly as designed.&lt;/p&gt;

&lt;p&gt;One more, the single-element range &lt;code&gt;nums[7..7]&lt;/code&gt;: &lt;code&gt;prefix[8] - prefix[7]&lt;/code&gt; is 31 − 25 = 6, which is &lt;code&gt;nums[7]&lt;/code&gt;. It works because adjacent prefix entries differ by exactly the element between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The naive version first, so there is something to measure against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;range_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Add up nums[left..right] inclusive, one element at a time.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;


&lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;19
9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct, and the cost of every call is proportional to the width of the range. Here is the version that pays that cost once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return prefix[i] = the sum of the first i values of nums.

    The result has len(nums) + 1 entries. prefix[0] is 0 — the sum of no
    values at all — and that sentinel is what makes every query a single
    subtraction with no special case for a range starting at index 0.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sum of nums[left..right] inclusive, from a prefix array.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0, 3, 4, 8, 9, 14, 23, 25, 31]
19
9
6
31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are the three cases from the walkthrough plus the whole array, and none needed a branch.&lt;/p&gt;

&lt;p&gt;You rarely have to write &lt;code&gt;build_prefix&lt;/code&gt; yourself. The standard library has had this since Python 3.2, and the &lt;code&gt;initial&lt;/code&gt; argument that supplies the sentinel since 3.8:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;accumulate&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;accumulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;accumulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;accumulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;build_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[3, 4, 8, 9, 14, 23, 25, 31]
[0, 3, 4, 8, 9, 14, 23, 25, 31]
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;accumulate&lt;/code&gt; is a C-level loop, so it is faster than the Python &lt;code&gt;for&lt;/code&gt; loop above and it is the version to reach for at work. Write &lt;code&gt;build_prefix&lt;/code&gt; by hand only while you are learning it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;[0] * (len(nums) + 1)&lt;/code&gt;&lt;/strong&gt; is the n + 1 rule made concrete. Allocating the exact size up front also means the loop only ever assigns to slots that already exist, so no &lt;code&gt;append&lt;/code&gt; and no resizing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;prefix[index + 1] = prefix[index] + value&lt;/code&gt;&lt;/strong&gt; is the one-line rule from the walkthrough. The &lt;code&gt;+ 1&lt;/code&gt; on the left is what shifts the whole array right by one and leaves the sentinel untouched at index 0. Every entry is computed from the entry immediately before it, which is why one pass is enough — the work is never repeated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;prefix[right + 1] - prefix[left]&lt;/code&gt;&lt;/strong&gt; is asymmetric on purpose, and that asymmetry is the whole off-by-one. The left end is &lt;em&gt;exclusive&lt;/em&gt; in the prefix array and inclusive in the range, so it needs no adjustment; the right end is inclusive in the range, so it needs the &lt;code&gt;+ 1&lt;/code&gt; to move past it. For half-open ranges — &lt;code&gt;nums[left:right]&lt;/code&gt;, matching Python slicing — the expression is the symmetric &lt;code&gt;prefix[right] - prefix[left]&lt;/code&gt;. Pick one convention, put it in the docstring, and never mix the two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases fall out of the arithmetic.&lt;/strong&gt; An empty input gives &lt;code&gt;[0]&lt;/code&gt;, and there is no valid query to run against it. A range covering everything is &lt;code&gt;prefix[n] - prefix[0]&lt;/code&gt;, which the zero collapses to just &lt;code&gt;prefix[n]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The values do not have to be positive.&lt;/strong&gt; Nothing above assumes it — negative numbers and floats both work, though see the float warning further down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Build: O(n).&lt;/strong&gt; The loop body runs exactly once per input element and does one addition and one store each time. Eight elements, eight additions. A million elements, a million additions. There is no nesting and no repeated work, so the count is exactly n.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query: O(1).&lt;/strong&gt; Two list index operations and one subtraction. Python lists are contiguous arrays of pointers, so indexing is a constant-time address calculation, not a walk. Three operations regardless of n and — the interesting part — regardless of how wide the range is. A range of 999,999 elements costs the same as a range of one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(n).&lt;/strong&gt; One extra list of n + 1 integers. That is a real cost, not a rounding error: for 10 million values you are holding a second 10-million-entry list.&lt;/p&gt;

&lt;p&gt;There is no best or worst case to separate out here. The build loop runs exactly n iterations whatever the values are — no early exit, no branch that depends on the data — and a query does the same two lookups and one subtraction every time. Best, average and worst are the same bound, which is unusual and is exactly what makes the technique easy to reason about.&lt;/p&gt;

&lt;p&gt;The trade only pays off when there are enough queries. Counting it exactly, for q queries whose ranges have total length L:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Operations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Naive&lt;/td&gt;
&lt;td&gt;L additions, worst case q × n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prefix sums&lt;/td&gt;
&lt;td&gt;n additions to build, then q subtractions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;range_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;range_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;naive_additions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;naive:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naive_additions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; additions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prefix: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; additions to build + &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; subtractions &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;= &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;naive:  23 additions
prefix: 8 additions to build + 5 subtractions = 13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eight elements and five queries is already a win, and the gap widens fast. With n = 1,000,000 and 100,000 queries averaging 30,000 elements each, the naive approach performs about 3 billion additions and the prefix approach performs 1.1 million operations — roughly 2,700 times fewer.&lt;/p&gt;

&lt;p&gt;The other direction is worth stating too. For a &lt;strong&gt;single&lt;/strong&gt; query over the whole array, the naive loop does n additions while the prefix version does n additions &lt;em&gt;plus&lt;/em&gt; an allocation &lt;em&gt;plus&lt;/em&gt; a subtraction — strictly worse. This is an amortisation: cheap queries bought with an upfront payment, and the break-even point is when L, the total length of all the queries, exceeds n + q.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two dimensions: sums over a rectangle
&lt;/h2&gt;

&lt;p&gt;The same idea works on a grid, and the payoff is bigger because a naive rectangle sum is quadratic in the rectangle's side length.&lt;/p&gt;

&lt;p&gt;Take this 4 × 4 grid, and ask for the total of the rectangle from row 1 to row 2 and column 1 to column 3:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-grid.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-grid.svg" alt="A four by four grid with the six cells of the query rectangle highlighted" width="1000" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build a table &lt;code&gt;P&lt;/code&gt; where &lt;code&gt;P[r][c]&lt;/code&gt; is the sum of every cell strictly above row &lt;code&gt;r&lt;/code&gt; and strictly left of column &lt;code&gt;c&lt;/code&gt; — the whole top-left block. Same convention as before: pad with a zero row and a zero column, so &lt;code&gt;P&lt;/code&gt; is 5 × 5 for a 4 × 4 grid.&lt;/p&gt;

&lt;p&gt;Each entry is built from three neighbours:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;P[r+1][c+1] = grid[r][c] + P[r][c+1] + P[r+1][c] - P[r][c]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The block above and the block to the left both contain the block that is above &lt;em&gt;and&lt;/em&gt; to the left, so that corner block gets counted twice and has to come off once. This is &lt;strong&gt;inclusion-exclusion&lt;/strong&gt;, and it appears again in the query.&lt;/p&gt;

&lt;p&gt;To read a rectangle from &lt;code&gt;(top, left)&lt;/code&gt; to &lt;code&gt;(bottom, right)&lt;/code&gt; inclusive, take the big block ending at its far corner, cut off the band above, cut off the band to the left, then add back the top-left block you have now removed twice:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;P[bottom+1][right+1] - P[top][right+1] - P[bottom+1][left] + P[top][left]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-rectangle.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-rectangle.svg" alt="The five by five prefix table with the four corner entries used by the inclusion-exclusion formula marked" width="1000" height="406"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_prefix_2d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return prefix[r][c] = the sum of the block above and left of (r, c).

    Both dimensions get a leading zero row/column, so no query needs a
    boundary check.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cols&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# The block above and the block to the left overlap in the corner
&lt;/span&gt;            &lt;span class="c1"&gt;# block, so it is counted twice and has to come off once.
&lt;/span&gt;            &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rectangle_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sum of the inclusive rectangle from (top, left) to (bottom, right).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;grid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;prefix_2d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_prefix_2d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prefix_2d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rectangle_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix_2d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rectangle_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix_2d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rectangle_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix_2d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0, 0, 0, 0, 0]
[0, 3, 3, 4, 8]
[0, 8, 14, 18, 24]
[0, 9, 17, 21, 28]
[0, 13, 22, 26, 34]
14
34
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the first answer by hand: the rectangle holds 6, 3, 2 on row 1 and 2, 0, 1 on row 2, which is 14. The formula gives 28 − 8 − 9 + 3 = 14. The second answer, 34, is the total of all sixteen cells; the third is the single cell at (2, 2), which is 0.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost.&lt;/strong&gt; Building touches each of the &lt;code&gt;rows × cols&lt;/code&gt; cells once and does three additions and subtractions per cell, so the build is O(rows × cols) — linear in the size of the grid, which is the least possible since every cell has to be read at least once. Each query is four lookups and three arithmetic operations: O(1), independent of the rectangle's area. Space is O(rows × cols) for the second table, and that is the real limit — a 10,000 × 10,000 grid needs a 100-million-entry prefix table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The inverse problem: difference arrays
&lt;/h2&gt;

&lt;p&gt;Prefix sums make many range &lt;em&gt;reads&lt;/em&gt; cheap. Turn the problem around — many range &lt;em&gt;writes&lt;/em&gt;, one read at the end — and the same relationship solves it backwards.&lt;/p&gt;

&lt;p&gt;Say you have 8 slots, all starting at zero, and three updates: add 2 to indices 1 through 4, add 3 to indices 0 through 2, add 1 to indices 5 through 7. Done naively each update loops over its range, so k updates of average width w cost k × w writes.&lt;/p&gt;

&lt;p&gt;Instead, record only the &lt;em&gt;changes&lt;/em&gt;. Keep an array &lt;code&gt;diff&lt;/code&gt; where &lt;code&gt;diff[i]&lt;/code&gt; is how much the value jumps between index &lt;code&gt;i - 1&lt;/code&gt; and index &lt;code&gt;i&lt;/code&gt;. Adding &lt;code&gt;amount&lt;/code&gt; across &lt;code&gt;[left, right]&lt;/code&gt; needs exactly two jumps: a step up at &lt;code&gt;left&lt;/code&gt;, and a step back down just past &lt;code&gt;right&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diff[left]      += amount     # from here on, everything is `amount` higher
diff[right + 1] -= amount     # past the right edge, take it back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two writes per update, whatever the width. Then a single running sum over &lt;code&gt;diff&lt;/code&gt; reconstructs the real values — and a running sum is a prefix sum, which is the whole point: the difference array is the inverse operation, so summing it undoes it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-difference.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-difference.svg" alt="Three range updates each writing two cells of the difference array, and the running sum that recovers the eight real values" width="1000" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that &lt;code&gt;diff&lt;/code&gt; needs n + 1 slots again, this time for the opposite reason: an update whose right edge is the last index writes to &lt;code&gt;diff[n]&lt;/code&gt;, one past the end of the real data. That slot exists only to be ignored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Record each range update as two boundary changes, in O(1) per update.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;        &lt;span class="c1"&gt;# from here on, every value is `amount` higher
&lt;/span&gt;        &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;   &lt;span class="c1"&gt;# past the right edge, give it back
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Turn a difference array into the real values with one running sum.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;accumulate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;apply_updates_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The obvious version: touch every index inside every range.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;


&lt;span class="n"&gt;updates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_difference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;apply_updates_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[3, 2, 0, -3, 0, -1, 0, 0, -1]
[3, 5, 5, 2, 2, 1, 1, 1]
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the running sum across &lt;code&gt;[3, 2, 0, -3, 0, -1, 0, 0]&lt;/code&gt;: 3, 5, 5, 2, 2, 1, 1, 1. Adding the three updates by hand gives &lt;code&gt;[3, 3, 3, 0, 0, 0, 0, 0]&lt;/code&gt; plus &lt;code&gt;[0, 2, 2, 2, 2, 0, 0, 0]&lt;/code&gt; plus &lt;code&gt;[0, 0, 0, 0, 0, 1, 1, 1]&lt;/code&gt;, and those totals match — which is what the &lt;code&gt;True&lt;/code&gt; on the last line asserts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost.&lt;/strong&gt; k updates at two writes each is O(k), then one pass to resolve is O(n): &lt;strong&gt;O(n + k)&lt;/strong&gt; total, against O(n × k) in the worst case for the naive loop. The catch is that you cannot read a value until you have resolved, so this only works when every update arrives before any read. That restriction is exactly what makes it cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  The interview classic: subarray sum equals k
&lt;/h2&gt;

&lt;p&gt;"How many contiguous subarrays add up to k?" is the problem that makes prefix sums click, because the naive answer is O(n²) and the fix is one hash map.&lt;/p&gt;

&lt;p&gt;Every subarray &lt;code&gt;nums[i..j]&lt;/code&gt; has sum &lt;code&gt;prefix[j + 1] - prefix[i]&lt;/code&gt;. So asking for subarrays that sum to &lt;code&gt;k&lt;/code&gt; is asking for &lt;strong&gt;pairs of prefix entries that differ by exactly k&lt;/strong&gt;, with the earlier one first. Rewrite the condition:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;prefix[j + 1] - prefix[i] = k&lt;/code&gt;, therefore &lt;code&gt;prefix[i] = prefix[j + 1] - k&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So walk the array once, keeping a running prefix sum. At each step, the number of subarrays ending here is the number of earlier prefix values equal to &lt;code&gt;running - k&lt;/code&gt;. Keep a count of every prefix value seen so far in a dictionary, and that lookup is O(1) on average.&lt;/p&gt;

&lt;p&gt;Try it on &lt;code&gt;[3, 4, 7, 2, -3, 1, 4]&lt;/code&gt; with k = 7. The running prefix sums, sentinel included, are &lt;code&gt;[0, 3, 7, 14, 16, 13, 14, 18]&lt;/code&gt;, and exactly three ordered pairs differ by 7:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-subarray-count.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fprefix-sums-subarray-count.svg" alt="The eight running prefix sums with the three pairs that differ by seven marked in turn" width="1000" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those three pairs are the three subarrays: &lt;code&gt;[3, 4]&lt;/code&gt;, &lt;code&gt;[7]&lt;/code&gt;, and &lt;code&gt;[7, 2, -3, 1]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_subarrays_with_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Count the contiguous subarrays of nums whose values add up to target.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;        &lt;span class="c1"&gt;# the empty prefix, so a whole prefix can match on its own
&lt;/span&gt;    &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="c1"&gt;# Every earlier prefix equal to running - target closes a subarray here.
&lt;/span&gt;        &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;


&lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;count_subarrays_with_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;count_subarrays_with_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;count_subarrays_with_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3
2
6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[1, 1, 1]&lt;/code&gt; with k = 2 has two answers, the first pair and the second. &lt;code&gt;[0, 0, 0]&lt;/code&gt; with k = 0 has six, because all six subarrays sum to zero — three of length 1, two of length 2, one of length 3. That last case is what catches people who try to count with pointers instead.&lt;/p&gt;

&lt;p&gt;Watch the counter run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  prefix=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;want &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  seen &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; time(s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;i=0  value=  3  prefix=  3  want  -4  seen 0 time(s)
i=1  value=  4  prefix=  7  want   0  seen 1 time(s)
i=2  value=  7  prefix= 14  want   7  seen 1 time(s)
i=3  value=  2  prefix= 16  want   9  seen 0 time(s)
i=4  value= -3  prefix= 13  want   6  seen 0 time(s)
i=5  value=  1  prefix= 14  want   7  seen 1 time(s)
i=6  value=  4  prefix= 18  want  11  seen 0 time(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hit at &lt;code&gt;i=1&lt;/code&gt; is the one that needs &lt;code&gt;counts[0] = 1&lt;/code&gt;. There, &lt;code&gt;running&lt;/code&gt; is 7 and the algorithm looks for an earlier prefix of 0 — the sentinel, the empty prefix before the array starts. Without seeding the map you lose every subarray beginning at index 0, and the bug is invisible on test data whose answers all start later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost.&lt;/strong&gt; One pass, with one dictionary lookup and one dictionary write per element. Python dictionaries give O(1) &lt;em&gt;average&lt;/em&gt; lookup, so the whole thing is &lt;strong&gt;O(n) average time and O(n) space&lt;/strong&gt;. The map holds at most 2n + 1 entries: each step stores the prefix it just computed, and reading &lt;code&gt;counts[running - target]&lt;/code&gt; on a &lt;code&gt;defaultdict&lt;/code&gt; also inserts a zero for the value it failed to find.&lt;/p&gt;

&lt;p&gt;Average is not a guarantee, and it is worth knowing why. In CPython a non-negative integer hashes to itself reduced modulo &lt;code&gt;2**61 - 1&lt;/code&gt;, and that is not randomised, so prefix sums chosen to share a hash really do drive each dictionary operation towards O(n) and the whole scan towards O(n²). Ordinary data never does that, but quote the O(n) as an average, not as a worst case.&lt;/p&gt;

&lt;p&gt;This matters because the &lt;a href="https://bimalkhatri.com.np/blogs/sliding-window-technique" rel="noopener noreferrer"&gt;sliding window technique&lt;/a&gt; solves the same problem in O(1) space when every value is positive, since growing the window then always grows the sum. Add one negative number and that monotonicity is gone and the window breaks. Prefix sums plus a &lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;hash map&lt;/a&gt; do not care about signs at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use prefix sums when the data is fixed and you will query it many times.&lt;/strong&gt; Log analysis, dashboard ranges over historical data, sensor readings, precomputed lookup tables. The pattern to recognise: "given an array and q queries, each asking for a range".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use a difference array when the updates all come before the reads&lt;/strong&gt; — bulk range increments, interval booking counts, timeline aggregation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the values change between queries.&lt;/strong&gt; Updating &lt;code&gt;nums[i]&lt;/code&gt; invalidates every prefix entry from &lt;code&gt;i + 1&lt;/code&gt; onwards, so a single element change costs O(n) to repair. If reads and writes interleave, reach for a &lt;strong&gt;Fenwick tree&lt;/strong&gt; (binary indexed tree) or a &lt;strong&gt;segment tree&lt;/strong&gt;: both give O(log n) point updates and O(log n) range sums. A prefix array is the degenerate case of those structures where you have decided updates never happen and bought O(1) queries with that promise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for a handful of queries.&lt;/strong&gt; One or two range sums over a large array are cheaper computed directly than by building an n-entry table first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be careful with floats.&lt;/strong&gt; Prefix sums of floating-point numbers accumulate rounding error along the whole array, and then the query subtracts two large, nearly equal totals — catastrophic cancellation, where the leading digits cancel and the error hiding beneath them becomes the answer's leading digits. For an accurate total of floats use &lt;code&gt;math.fsum&lt;/code&gt;, which adds them exactly and rounds only once at the end. For range sums of floats, know that the error is proportional to the magnitude of the &lt;em&gt;whole prefix&lt;/em&gt;, not just the range you asked about.&lt;/p&gt;

&lt;p&gt;Integer overflow is not a Python problem — Python integers grow as needed — but it is the classic bug when you port this to C++, Java or Rust. A prefix sum over 100,000 values of up to a billion reaches 10^14, which overshoots the signed 32-bit maximum by a factor of about 46,000.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Integral images in computer vision.&lt;/strong&gt; A summed-area table is a 2D prefix sum, introduced by Frank Crow in 1984 for texture mapping and made famous by the Viola-Jones face detector in 2001. Every feature Viola-Jones scores is a difference of rectangle sums, and the integral image makes each of those rectangle sums four table lookups whatever the rectangle's size. Its detector holds a few thousand features arranged as a cascade of stages, and because most windows are rejected by the first stages only a handful of features are ever evaluated on a given window. A rectangle sum that costs the same at every scale is what made real-time face detection possible on 2001 hardware. OpenCV exposes it as &lt;code&gt;cv2.integral&lt;/code&gt; to this day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running totals in databases and dataframes.&lt;/strong&gt; &lt;code&gt;SUM(amount) OVER (ORDER BY day ROWS UNBOUNDED PRECEDING)&lt;/code&gt; in PostgreSQL, SQL Server or BigQuery is a prefix sum computed by the engine in one pass. So are &lt;code&gt;pandas.Series.cumsum&lt;/code&gt; and &lt;code&gt;numpy.cumsum&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weighted random choice in the Python standard library.&lt;/strong&gt; &lt;code&gt;random.choices(population, weights=...)&lt;/code&gt; calls &lt;code&gt;itertools.accumulate&lt;/code&gt; on the weights to build a prefix array, then uses &lt;code&gt;bisect&lt;/code&gt; to binary-search it for each pick. That is a prefix sum turning a weighted draw into a &lt;a href="https://bimalkhatri.com.np/blogs/binary-search" rel="noopener noreferrer"&gt;binary search&lt;/a&gt; over cumulative totals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallel computing.&lt;/strong&gt; Prefix sum is one of the fundamental parallel primitives, known there as &lt;strong&gt;scan&lt;/strong&gt;. CUDA's Thrust library ships &lt;code&gt;inclusive_scan&lt;/code&gt; and &lt;code&gt;exclusive_scan&lt;/code&gt;, and they are the standard way to compute output offsets when many threads write variable-length results into one buffer: each thread's write position is the prefix sum of the sizes before it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Dropping the &lt;code&gt;+ 1&lt;/code&gt; in the query.&lt;/strong&gt; &lt;code&gt;prefix[right] - prefix[left]&lt;/code&gt; gives you &lt;code&gt;nums[left..right-1]&lt;/code&gt; — one element short at the right end. Fix: decide whether your range is inclusive or half-open, write it in the docstring, and test a single-element range, which fails loudly under both mistakes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the prefix array without the sentinel.&lt;/strong&gt; Then &lt;code&gt;left = 0&lt;/code&gt; needs &lt;code&gt;prefix[-1]&lt;/code&gt;, and in Python that silently reads the last element instead of raising. Always allocate n + 1 and start at zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Making the difference array length n.&lt;/strong&gt; An update ending at the last index writes &lt;code&gt;diff[n]&lt;/code&gt;, so a length-n array raises &lt;code&gt;IndexError&lt;/code&gt; on exactly the ranges that reach the end — often the last case anyone tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the corner in 2D.&lt;/strong&gt; Both the build step and the rectangle query need a corner term, and the signs are opposite: the build &lt;em&gt;subtracts&lt;/em&gt; the block it counted twice, the query &lt;em&gt;adds&lt;/em&gt; it back. Leave it out of the query and the answer is too small by the top-left block, which is correct-looking whenever &lt;code&gt;top&lt;/code&gt; or &lt;code&gt;left&lt;/code&gt; is 0 and wrong everywhere else. Test a rectangle that touches neither edge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missing &lt;code&gt;counts[0] = 1&lt;/code&gt; in the subarray counter.&lt;/strong&gt; Every subarray that starts at index 0 is silently skipped. Test with &lt;code&gt;[k]&lt;/code&gt; — a one-element array whose only element is the target, whose answer must be 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mutating the input after building the prefix array.&lt;/strong&gt; The prefix array is a snapshot. It goes stale the moment &lt;code&gt;nums&lt;/code&gt; changes, and nothing warns you. If the data is mutable, either rebuild or use a Fenwick tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Given an array and a list of &lt;code&gt;(left, right)&lt;/code&gt; queries, return the &lt;em&gt;average&lt;/em&gt; of each range in O(1) per query.&lt;/li&gt;
&lt;li&gt;Find an index where the sum of everything to its left equals the sum of everything to its right, in one pass over the array.&lt;/li&gt;
&lt;li&gt;Given an array of only 0s and 1s, find the longest subarray with equal counts of each — map every 0 to −1 and look for two prefix sums that are equal.&lt;/li&gt;
&lt;li&gt;Given a list of flight bookings, each &lt;code&gt;(first_flight, last_flight, seats)&lt;/code&gt;, return the total seats booked on every flight using a difference array.&lt;/li&gt;
&lt;li&gt;Count the submatrices of a grid whose values sum to a target, by fixing a pair of rows and running the 1D subarray counter along the columns between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Prefix sums are the cheapest possible trade: one linear pass up front buys constant-time range sums forever after, as long as the data stops changing. Store n + 1 cumulative totals with a zero at the front, and every query is &lt;code&gt;prefix[right + 1] - prefix[left]&lt;/code&gt; with no boundary cases to remember. The same relationship read backwards gives difference arrays, and the same relationship read as "pairs of prefix values k apart" gives the linear-time subarray counter.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — exactly one addition per element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) — two lookups and one subtraction, any range width&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — changing one value invalidates every later prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — a second array of n + 1 totals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2D build / query&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(rows × cols) / O(1) — four lookups per rectangle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difference array&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) per range update, O(n) once to resolve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subarray counting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) average time and O(n) space with a hash map&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List / array with O(1) indexing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The array is fixed and total query length exceeds n&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Values change between queries — use a Fenwick or segment tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integral images in face detection, SQL running totals, &lt;code&gt;random.choices&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;itertools.accumulate(nums, initial=0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Learn the n + 1 convention once and it will keep paying out, because the same off-by-one shows up in difference arrays, in 2D tables, and in every sliding-window variant that tracks a running total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/sliding-window-technique" rel="noopener noreferrer"&gt;The Sliding Window Technique&lt;/a&gt; — the other way to make subarray problems linear, and where it stops working.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/two-pointers-technique" rel="noopener noreferrer"&gt;The Two Pointers Technique&lt;/a&gt; — the third member of this family of linear-scan tricks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash Tables in Python&lt;/a&gt; — why the dictionary lookup in the subarray counter is O(1), from the inside.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/arrays-and-dynamic-arrays" rel="noopener noreferrer"&gt;Arrays and Dynamic Arrays&lt;/a&gt; — why indexing a Python list is constant time in the first place.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting arguments used above, done properly.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Sliding Window Technique in Python: Subarray Problems Made Linear</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:38:57 +0000</pubDate>
      <link>https://dev.to/bimal-py/the-sliding-window-technique-in-python-subarray-problems-made-linear-3843</link>
      <guid>https://dev.to/bimal-py/the-sliding-window-technique-in-python-subarray-problems-made-linear-3843</guid>
      <description>&lt;p&gt;A list of 100,000 numbers contains 5,000,050,000 contiguous runs. If your first instinct for "find the best subarray" is to look at each of them, you have written an algorithm that will not finish this afternoon. A sliding window answers the same question in fewer than 200,000 steps, because it never touches any element more than twice: once when it joins the window, once when it leaves.&lt;/p&gt;

&lt;p&gt;A window is just a contiguous run, held by two indices: &lt;code&gt;left&lt;/code&gt; and &lt;code&gt;right&lt;/code&gt;. The technique is a rule about how those indices are allowed to move. The right edge steps forward and one element joins the window. The left edge steps forward and one element leaves. Nothing else ever happens, neither edge ever goes backwards, and the quantity you care about — a sum, a character count, a set of distinct values — is patched up as elements join and leave instead of being recomputed from the elements still inside.&lt;/p&gt;

&lt;p&gt;Two things about this pattern get skipped almost everywhere, and both are here. The first is why the variable-size version is still linear despite having a &lt;code&gt;while&lt;/code&gt; loop nested inside a &lt;code&gt;for&lt;/code&gt; loop, which looks quadratic and is not. The second is the precondition: sliding windows are only correct when shrinking from the left can actually repair an invalid window. Break that condition — an array containing negative numbers is the standard way to break it — and the algorithm does not crash, it just returns the wrong answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fixed-size windows: add one, remove one
&lt;/h3&gt;

&lt;p&gt;Start with the simplest version, where the width is handed to you. Given the readings &lt;code&gt;[2, 1, 5, 1, 3, 2]&lt;/code&gt;, what is the largest sum of 3 consecutive values?&lt;/p&gt;

&lt;p&gt;The obvious approach adds up each window from scratch. There are &lt;code&gt;n - k + 1&lt;/code&gt; windows of width k in a list of n values, and each one costs k reads, so the bill is &lt;code&gt;(n - k + 1) * k&lt;/code&gt; reads. For n of 100,000 and k of 1,000 that is 99,001,000 reads to answer one question about a list you could scan nine hundred and ninety times over in the same effort.&lt;/p&gt;

&lt;p&gt;The waste is obvious once you line two neighbouring windows up. The window at indices 1 to 3 shares indices 1 and 2 with the window at indices 0 to 2. Only one value left and one value arrived. So carry the sum forward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subtract the value that just fell off the left edge,&lt;/li&gt;
&lt;li&gt;add the value that just arrived at the right edge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two operations per step, regardless of how wide the window is.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-fixed-window.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-fixed-window.svg" alt="Six numbered cells with a three-wide window shown in four positions, each step subtracting the value leaving on the left and adding the value entering on the right" width="1000" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Variable-size windows: expand, then shrink
&lt;/h3&gt;

&lt;p&gt;Most real problems do not tell you the width. Instead they give you a rule the window must obey — no repeated characters, at most 2 distinct values, a sum no greater than 10 — and ask for the longest (or shortest) run that obeys it.&lt;/p&gt;

&lt;p&gt;The loop is always the same three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move &lt;code&gt;right&lt;/code&gt; forward by one. The new element joins the window.&lt;/li&gt;
&lt;li&gt;While the window breaks the rule, move &lt;code&gt;left&lt;/code&gt; forward by one, evicting the leftmost element.&lt;/li&gt;
&lt;li&gt;The window is valid again. Record it if it beats the best seen so far.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-expand-shrink.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-expand-shrink.svg" alt="The variable-size loop drawn as boxes: move the right edge in, test the rule, shrink from the left while the window is invalid, then record it" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2 is where the correctness lives, and it rests on one assumption that is worth stating out loud because almost nobody does:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a window is invalid, every window that contains it is invalid too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the &lt;strong&gt;monotonicity precondition&lt;/strong&gt;, and it does two separate jobs. For a fixed right edge it makes validity a threshold: there is some index where the window becomes valid, everything at or after it is valid, and everything before it is not — so shrinking is guaranteed to reach that threshold rather than overshooting past it. For a fixed left edge it says an invalid window cannot be rescued by taking in more on the right, and that is what lets &lt;code&gt;left&lt;/code&gt; stay put: once a left edge has been evicted, no window starting there will ever be valid again, at any later right edge. Both halves are needed, and both fall out of the one sentence above.&lt;/p&gt;

&lt;p&gt;Check it on "no repeated characters": if a run contains two copies of the letter &lt;code&gt;a&lt;/code&gt;, then any longer run containing it also contains both copies, so it is invalid too. It holds. Check it on "sum at most 10" with non-negative values: adding values on either side can only make the sum bigger, never smaller, so a run that is already over the limit stays over. It holds — as long as the values are non-negative. Hold that thought.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The fixed window, by hand
&lt;/h3&gt;

&lt;p&gt;Take &lt;code&gt;[2, 1, 5, 1, 3, 2]&lt;/code&gt; with k of 3. The first window is added up in full; every window after that is one subtraction and one addition.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Window&lt;/th&gt;
&lt;th&gt;Values&lt;/th&gt;
&lt;th&gt;Sum&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;indices 0 to 2&lt;/td&gt;
&lt;td&gt;2, 1, 5&lt;/td&gt;
&lt;td&gt;8, added in full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indices 1 to 3&lt;/td&gt;
&lt;td&gt;1, 5, 1&lt;/td&gt;
&lt;td&gt;8 − 2 + 1 = 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indices 2 to 4&lt;/td&gt;
&lt;td&gt;5, 1, 3&lt;/td&gt;
&lt;td&gt;7 − 1 + 3 = 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;indices 3 to 5&lt;/td&gt;
&lt;td&gt;1, 3, 2&lt;/td&gt;
&lt;td&gt;9 − 5 + 2 = 6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The answer is 9. The whole list was read once, plus three extra reads for the values leaving — 9 reads against the brute force's 12. At six elements that is a shrug. At 100,000 elements with k of 1,000 it is 199,000 reads against 99,001,000.&lt;/p&gt;

&lt;h3&gt;
  
  
  The variable window, by hand
&lt;/h3&gt;

&lt;p&gt;Now the harder shape. Find the length of the longest substring of &lt;code&gt;"abcabcbb"&lt;/code&gt; with no repeated character.&lt;/p&gt;

&lt;p&gt;Keep a dict &lt;code&gt;last_seen&lt;/code&gt; mapping each character to the most recent index it appeared at, and a &lt;code&gt;start&lt;/code&gt; index for the left edge. For each character you read, if it was last seen &lt;em&gt;inside the current window&lt;/em&gt;, the left edge jumps to just past that earlier copy. That is a shrink of several positions in one move, which is fine — &lt;code&gt;left&lt;/code&gt; only ever goes forward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;i=0&lt;/code&gt;, read &lt;code&gt;a&lt;/code&gt;. Window &lt;code&gt;"a"&lt;/code&gt;, length 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=1&lt;/code&gt;, read &lt;code&gt;b&lt;/code&gt;. Window &lt;code&gt;"ab"&lt;/code&gt;, length 2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=2&lt;/code&gt;, read &lt;code&gt;c&lt;/code&gt;. Window &lt;code&gt;"abc"&lt;/code&gt;, length 3. Best so far.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=3&lt;/code&gt;, read &lt;code&gt;a&lt;/code&gt;. Last seen at index 0, which is inside the window, so &lt;code&gt;start&lt;/code&gt; jumps to 1. Window &lt;code&gt;"bca"&lt;/code&gt;, length 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=4&lt;/code&gt;, read &lt;code&gt;b&lt;/code&gt;. Last seen at index 1, inside. &lt;code&gt;start&lt;/code&gt; jumps to 2. Window &lt;code&gt;"cab"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=5&lt;/code&gt;, read &lt;code&gt;c&lt;/code&gt;. Last seen at index 2, inside. &lt;code&gt;start&lt;/code&gt; jumps to 3. Window &lt;code&gt;"abc"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=6&lt;/code&gt;, read &lt;code&gt;b&lt;/code&gt;. Last seen at index 4, inside. &lt;code&gt;start&lt;/code&gt; jumps to 5. Window &lt;code&gt;"cb"&lt;/code&gt;, length 2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;i=7&lt;/code&gt;, read &lt;code&gt;b&lt;/code&gt;. Last seen at index 6, inside. &lt;code&gt;start&lt;/code&gt; jumps to 7. Window &lt;code&gt;"b"&lt;/code&gt;, length 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-no-repeat.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-no-repeat.svg" alt="The string a b c a b c b b with the valid window drawn for each of the eight read positions, shrinking whenever a repeat arrives" width="1000" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The answer is 3. Count the movement: the right edge took eight steps, and the left edge moved on five of those steps, travelling 0 to 1 to 2 to 3 to 5 to 7 — seven positions in total. Fifteen moves for an eight-character string, against a ceiling of 2n = 16. No substring was ever built or rescanned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The naive fixed-size version first, so there is something honest to compare against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;max_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Largest sum of k consecutive values, recomputing every window in full.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k must be between 1 and len(values)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="n"&gt;readings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max_sum_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct, and it re-reads k values every step for no reason. The window version keeps the running sum instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;max_sum_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Largest sum of k consecutive values, in a single pass.

    The first window is added up in full. After that each step adds the value
    entering on the right and subtracts the one leaving on the left, so keeping
    the sum correct costs two operations no matter how wide the window is.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;k must be between 1 and len(values)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Summed by index rather than sum(values[:k]) so that no k-element slice is
&lt;/span&gt;    &lt;span class="c1"&gt;# ever built; the whole point is that this function holds nothing but a total.
&lt;/span&gt;    &lt;span class="n"&gt;window_sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window_sum&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;window_sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;window_sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max_sum_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max_sum_window&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;max_sum_window&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9
7 -5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gap between them is arithmetic, not opinion. The naive version reads &lt;code&gt;(n - k + 1) * k&lt;/code&gt; values; the window version reads k for the first window and 2 for every step after.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;k&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;naive reads&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;window reads&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;naive_reads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;
    &lt;span class="n"&gt;window_reads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naive_reads&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;window_reads&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        n       k     naive reads  window reads
    1,000     100          90,100         1,900
  100,000   1,000      99,001,000       199,000
1,000,000  10,000   9,900,010,000     1,990,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the variable-size version, plus a traced copy that prints the walkthrough above so you can check the hand trace against the machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Length of the longest substring of text with no repeated character.

    last_seen maps a character to the most recent index it appeared at. When
    the character entering on the right is already inside the window, the
    window&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s left edge jumps to just past that earlier copy.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# The repeat is inside the window, so every window that keeps both
&lt;/span&gt;            &lt;span class="c1"&gt;# copies is invalid. Skip left past the older one in one move.
&lt;/span&gt;            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_unique_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same algorithm, printing the window after every character read.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;moved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;moved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;repeat at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, start jumps to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;i=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; read &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  window [&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; best=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;moved&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;moved&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;longest_unique_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abcabcbb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;i=0 read a  window [0:1] = a    best=1
i=1 read b  window [0:2] = ab   best=2
i=2 read c  window [0:3] = abc  best=3
i=3 read a  window [1:4] = bca  best=3  repeat at 0, start jumps to 1
i=4 read b  window [2:5] = cab  best=3  repeat at 1, start jumps to 2
i=5 read c  window [3:6] = abc  best=3  repeat at 2, start jumps to 3
i=6 read b  window [5:7] = cb   best=3  repeat at 4, start jumps to 5
i=7 read b  window [7:8] = b    best=3  repeat at 6, start jumps to 7
3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;values[right] - values[right - k]&lt;/code&gt;&lt;/strong&gt; is the add-one-remove-one update, written as a single expression. When the right edge sits at index &lt;code&gt;right&lt;/code&gt;, the window covers the k indices ending there, so the value that just fell out is exactly k positions back. Getting that offset wrong by one is the classic bug in this line, and it does not raise — it silently sums the wrong window.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first window is summed in full, outside the loop.&lt;/strong&gt; That is the only place &lt;code&gt;sum()&lt;/code&gt; appears. If you see &lt;code&gt;sum()&lt;/code&gt; inside the loop, the running total is not running and the algorithm is back to quadratic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;if previous &amp;gt;= start&lt;/code&gt;&lt;/strong&gt; is the guard that makes the left edge one-directional. Without it, &lt;code&gt;start&lt;/code&gt; is set to &lt;code&gt;previous + 1&lt;/code&gt; whenever the character has &lt;em&gt;ever&lt;/em&gt; been seen, including when that sighting was before the current window — which drags the left edge backwards and invents substrings that were never valid. The shortest input that exposes it is &lt;code&gt;"abba"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_unique_unguarded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same code with the `previous &amp;gt;= start` guard removed.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;last_seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_unique_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check every substring. Used only to confirm the fast version.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abba&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abcabcbb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pwwkew&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bbbbb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="si"&gt;!r:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; window=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;longest_unique&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unguarded=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;longest_unique_unguarded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brute=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;longest_unique_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'abba'     window=2 unguarded=3 brute=2
'abcabcbb' window=3 unguarded=3 brute=3
'pwwkew'   window=3 unguarded=3 brute=3
'bbbbb'    window=1 unguarded=1 brute=1
''         window=0 unguarded=0 brute=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trace &lt;code&gt;"abba"&lt;/code&gt; to see it. At &lt;code&gt;i=2&lt;/code&gt; the second &lt;code&gt;b&lt;/code&gt; arrives, so &lt;code&gt;start&lt;/code&gt; moves to 2 and the window is &lt;code&gt;"b"&lt;/code&gt;. At &lt;code&gt;i=3&lt;/code&gt; the &lt;code&gt;a&lt;/code&gt; arrives; it was last seen at index 0, which is &lt;em&gt;behind&lt;/em&gt; the left edge, so it is not in the window at all and nothing should move. The unguarded version sets &lt;code&gt;start&lt;/code&gt; back to 1, claims the window &lt;code&gt;"bba"&lt;/code&gt; has length 3, and reports 3 for a string whose answer is 2. Every other test agrees, which is exactly why this bug survives code review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases fall out of the arithmetic.&lt;/strong&gt; An empty string never enters the loop and returns 0. A string of identical characters shrinks on every step and returns 1. In the fixed-size version, &lt;code&gt;k&lt;/code&gt; equal to &lt;code&gt;len(values)&lt;/code&gt; gives an empty &lt;code&gt;range(k, len(values))&lt;/code&gt;, so the initial sum is the answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two more windows worth memorising
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Minimum window substring
&lt;/h3&gt;

&lt;p&gt;Given a text and a target, find the shortest substring of the text containing every character of the target, counting repeats. This is the problem that teaches the &lt;strong&gt;need/have counter&lt;/strong&gt;: a dict of required counts, a dict of current counts, and a single integer &lt;code&gt;matched&lt;/code&gt; recording how many distinct required characters are currently present at full strength. The window is valid exactly when &lt;code&gt;matched&lt;/code&gt; equals the number of distinct characters in the target — one integer comparison, not a dict comparison.&lt;/p&gt;

&lt;p&gt;There is one structural difference from every window so far. For a &lt;em&gt;longest&lt;/em&gt; answer you shrink until the window is valid, then record. For a &lt;em&gt;shortest&lt;/em&gt; answer you record while the window is still valid, then shrink to try for better. Same loop, opposite placement of the recording line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;min_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Shortest substring of text containing every character of target.

    need holds the required count of each character. matched counts how many
    distinct required characters currently sit in the window at full strength,
    so the window is valid exactly when matched equals len(need).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;

    &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="c1"&gt;# Shrink while the window is still valid: a valid window can only get
&lt;/span&gt;        &lt;span class="c1"&gt;# shorter by losing characters from the left, so record before evicting.
&lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;best_length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;best_start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best_length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;have&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;need&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;best_length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;best_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;best_start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;best_length&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;min_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ADOBECODEBANC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;min_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;min_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nf"&gt;repr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;min_window&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BANC
'' 'a' 'b'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;"ADOBECODEBANC"&lt;/code&gt; the window becomes valid three separate times. The first valid window is &lt;code&gt;"ADOBEC"&lt;/code&gt; at indices 0 to 5, length 6. It is recorded, then the leading &lt;code&gt;A&lt;/code&gt; is evicted and the window is invalid again until the &lt;code&gt;A&lt;/code&gt; at index 10 arrives. That second stretch shrinks all the way down to &lt;code&gt;"CODEBA"&lt;/code&gt;, length 6 — a tie, so nothing is recorded — before its &lt;code&gt;C&lt;/code&gt; is evicted. The final &lt;code&gt;C&lt;/code&gt; at index 12 makes it valid a last time, and shrinking from &lt;code&gt;"ODEBANC"&lt;/code&gt; gives &lt;code&gt;"EBANC"&lt;/code&gt; at length 5 and then &lt;code&gt;"BANC"&lt;/code&gt; at length 4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-min-window.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-min-window.svg" alt="The text ADOBECODEBANC with four highlighted windows spread across the three stretches where the window is valid, ending at the four-character answer BANC" width="1000" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note the &lt;code&gt;have[leaving] &amp;lt; need[leaving]&lt;/code&gt; test on eviction. Dropping a &lt;code&gt;B&lt;/code&gt; when the window holds two of them does not invalidate anything, so &lt;code&gt;matched&lt;/code&gt; must only fall when the count actually drops below what is required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Longest run with at most k distinct values
&lt;/h3&gt;

&lt;p&gt;The same skeleton with a plain counter. The rule is &lt;code&gt;len(counts)&lt;/code&gt; being at most k, and the one detail that catches people is that a key whose count reaches zero must be deleted, because &lt;code&gt;len()&lt;/code&gt; counts keys, not non-zero keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_k_distinct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Length of the longest run containing at most k distinct values.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# A key left at zero would still be counted by len(counts).
&lt;/span&gt;                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;window_travel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return (best length, steps right took, steps left took).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;right_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;right_steps&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;leaving&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;left_steps&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_steps&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;window_travel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  k=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  best=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  right moved &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;right_steps&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  left moved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;left_steps&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  total &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;right_steps&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;left_steps&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  (2n = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;longest_at_most_k_distinct&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;longest_at_most_k_distinct&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;longest_at_most_k_distinct&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n=      5  k=2  best= 3  right moved       5  left moved      3  total       8  (2n = 10)
n=100,000  k=5  best=10  right moved 100,000  left moved 99,992  total 199,992  (2n = 200,000)
3 3 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;collections.Counter&lt;/code&gt; will do the counting dict for you, but it does not delete keys that hit zero, so &lt;code&gt;len()&lt;/code&gt; on a &lt;code&gt;Counter&lt;/code&gt; after a decrement is not the distinct count. Either &lt;code&gt;del&lt;/code&gt; the key yourself, as above, or track the distinct count in a separate integer.&lt;/p&gt;

&lt;p&gt;This function is also the building block for the harder question "how many runs contain &lt;strong&gt;exactly&lt;/strong&gt; k distinct values". Counting runs with exactly k distinct is counting runs with at most k, minus counting runs with at most k − 1 — the same window run twice, with a running total of window lengths instead of a maximum.&lt;/p&gt;

&lt;h2&gt;
  
  
  The precondition, and where sliding window breaks
&lt;/h2&gt;

&lt;p&gt;Here is the failure everyone should see once. The problem: find the longest run whose sum is at most a given limit. The window version is four lines and it is correct on non-negative input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Longest run whose sum is at most limit. Correct only for non-negative values.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_sum_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check every run. Slow, but right on any input.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;running&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="n"&gt;non_negative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;with_a_negative&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;longest_at_most_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;non_negative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_sum_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;non_negative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;longest_at_most_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with_a_negative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_sum_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;with_a_negative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;agree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;longest_at_most_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;longest_at_most_sum_brute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;non-negative samples agree:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 3
2 4
non-negative samples agree: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two hundred non-negative inputs agree. &lt;code&gt;[2, 5, -5, 1]&lt;/code&gt; with a limit of 3 does not: the window says 2, the truth is 4.&lt;/p&gt;

&lt;p&gt;Trace it. At &lt;code&gt;right = 1&lt;/code&gt; the window is &lt;code&gt;[2, 5]&lt;/code&gt; with a sum of 7, over the limit, so the shrink loop evicts the 2 (sum 5, still over) and then the 5 (sum 0), leaving &lt;code&gt;left&lt;/code&gt; at 2 with an empty window. From there the best it can ever report is a run starting at index 2, and the answer — the whole array, which sums to exactly 3 — starts at index 0. That start was thrown away and &lt;code&gt;left&lt;/code&gt; cannot go back.&lt;/p&gt;

&lt;p&gt;The monotonicity precondition is what failed. With a negative in the array, removing a value from the left can &lt;em&gt;increase&lt;/em&gt; the sum, so "invalid" no longer implies "invalid for every earlier left edge". There is no threshold to shrink towards, and the entire justification for a one-directional left pointer collapses. No exception, no warning, just a smaller number than the right one.&lt;/p&gt;

&lt;p&gt;The repair is not a patched window, it is a different algorithm. Write &lt;code&gt;prefix[i]&lt;/code&gt; for the sum of the first i values; then the sum of a run from i to j is &lt;code&gt;prefix[j + 1] - prefix[i]&lt;/code&gt;, and the question becomes a search over prefix values rather than a walk over window edges. &lt;a href="https://bimalkhatri.com.np/blogs/prefix-sums" rel="noopener noreferrer"&gt;Prefix sums&lt;/a&gt; covers that shift. The related question "is there a run summing to exactly S", which sliding windows also cannot answer once negatives appear, is solved in O(n) by storing every prefix sum in a &lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;hash table&lt;/a&gt; and looking up &lt;code&gt;prefix - S&lt;/code&gt; at each step.&lt;/p&gt;

&lt;p&gt;The same caution applies whenever the rule is not monotone under shrinking. "Longest run whose product is at most P" breaks the moment a zero or a value below 1 appears, for exactly the same reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fixed window: O(n) time, O(1) space.&lt;/strong&gt; The first window costs k reads. Each of the remaining &lt;code&gt;n - k&lt;/code&gt; steps costs exactly two reads and one comparison. Total: &lt;code&gt;k + 2(n - k)&lt;/code&gt;, which is at most 2n, so &lt;strong&gt;O(n)&lt;/strong&gt;. The naive version is &lt;code&gt;(n - k + 1) * k&lt;/code&gt;, which is O(n · k) — and when k is proportional to n, that is O(n²).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable window: O(n) time, amortised.&lt;/strong&gt; This is the argument to be able to give, because the code contains a &lt;code&gt;while&lt;/code&gt; loop inside a &lt;code&gt;for&lt;/code&gt; loop and that shape is quadratic in most other contexts.&lt;/p&gt;

&lt;p&gt;Count the two edges separately instead of counting loop iterations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;right&lt;/code&gt; advances exactly once per outer iteration, so it takes exactly n steps over the whole run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;left&lt;/code&gt; starts at 0, only ever increases, and never passes n. So &lt;code&gt;left&lt;/code&gt; takes &lt;strong&gt;at most n steps in total, across the entire run&lt;/strong&gt; — not per outer iteration, in total.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step of either edge does a constant amount of work: one dict update, one comparison, one arithmetic operation. So the total work is bounded by &lt;code&gt;n + n = 2n&lt;/code&gt; constant-cost steps, which is &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One caveat worth naming rather than glossing over. A dict update is O(1) on average, not guaranteed — a set of keys that all hash to the same bucket drags a single lookup out to O(n). So the counting windows are O(n) &lt;em&gt;expected&lt;/em&gt;, not O(n) come what may. The sum-based windows hold no dict and carry no such caveat.&lt;/p&gt;

&lt;p&gt;The reason the nested loop does not multiply is that the two loops share one budget. A nested loop is quadratic when the inner count is independent of the outer loop's progress — an inner loop that runs n times for each of n outer iterations. Here every inner iteration permanently consumes one of the n moves &lt;code&gt;left&lt;/code&gt; will ever make. A single outer iteration can trigger a long shrink, but that shrink is stolen from the budget of every later iteration. The measurement above says it plainly: on 100,000 elements, &lt;code&gt;right&lt;/code&gt; moved 100,000 times and &lt;code&gt;left&lt;/code&gt; moved 99,992 times, for 199,992 total moves against a ceiling of 200,000.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-pointer-travel.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsliding-window-technique-pointer-travel.svg" alt="A five-row grid showing the window's position at each step, with the left edge moving forward three times and the right edge five times across the whole run" width="1000" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space.&lt;/strong&gt; O(1) for the sum-based windows: two indices and a running total. The counting windows hold a dict, bounded by the number of distinct values that can be inside a valid window — at most &lt;code&gt;k + 1&lt;/code&gt; keys for the at-most-k-distinct version, at most one key per distinct character for &lt;code&gt;longest_unique&lt;/code&gt; (128 for plain ASCII text, so effectively constant), and at most one key per distinct character of the target for &lt;code&gt;min_window&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Against the brute force, on a list of n items:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Items&lt;/th&gt;
&lt;th&gt;Runs to check&lt;/th&gt;
&lt;th&gt;Window pointer moves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;5,050&lt;/td&gt;
&lt;td&gt;200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;500,500&lt;/td&gt;
&lt;td&gt;2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;50,005,000&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;5,000,050,000&lt;/td&gt;
&lt;td&gt;200,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The left column is &lt;code&gt;n(n + 1) / 2&lt;/code&gt;, the number of contiguous runs in a list of n items. Ten times the data costs a hundred times the work there and ten times the work on the right.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it when&lt;/strong&gt; three things are true: the answer is a contiguous run, the property you are testing can be updated in constant time when one element joins and one leaves, and shrinking from the left can restore validity. Sums, counts, character frequencies and "at most k of something" all satisfy the second condition. Maximums do not, which is the one common exception worth knowing — when the largest value in the window is the one leaving, you cannot recover the new largest in O(1) without extra structure. That problem, sliding window maximum, is solved with a monotonic &lt;code&gt;collections.deque&lt;/code&gt; holding indices in decreasing value order, and it is still O(n) overall because each index is pushed and popped once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the answer is not contiguous.&lt;/strong&gt; "Longest increasing subsequence" and "longest common subsequence" allow gaps, and no window can express a gap. Those are &lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;dynamic programming&lt;/a&gt; problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the answer is a pair of positions rather than a range.&lt;/strong&gt; That is the &lt;a href="https://bimalkhatri.com.np/blogs/two-pointers-technique" rel="noopener noreferrer"&gt;two pointers technique&lt;/a&gt;, a close relative where the two indices usually move towards each other rather than both rightwards, and where what sits between them is irrelevant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the rule is not monotone under shrinking&lt;/strong&gt;, as shown above. Sums with negatives, products with zeros or fractions, and any rule where removing an element can make things worse are all disqualified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for arbitrary range queries either.&lt;/strong&gt; If the question is "the sum of indices 300 to 700" asked a thousand times over a fixed array, a window has nothing to slide along; build a prefix-sum array once and answer each query in O(1).&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TCP flow control.&lt;/strong&gt; The receiver advertises a window size, and the sender may have at most that many unacknowledged bytes in flight. As acknowledgements arrive the window's left edge advances and new bytes become sendable on the right. It is the same two-edge structure, running on every connection your machine has open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DEFLATE, the algorithm behind gzip, zlib and PNG.&lt;/strong&gt; Its LZ77 stage encodes repeated data as back-references into a sliding window of the previous 32 KB of output. Data older than 32 KB has fallen out of the window and can no longer be referenced, which is precisely the trade that keeps the compressor's memory constant regardless of file size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rolling hashes.&lt;/strong&gt; &lt;a href="https://bimalkhatri.com.np/blogs/rabin-karp-string-matching" rel="noopener noreferrer"&gt;Rabin-Karp&lt;/a&gt; slides a fixed-width window over the text and updates the hash by removing the leaving character's contribution and adding the entering one's — the add-one-remove-one update on a hash instead of a sum. Content-defined chunking in backup and deduplication systems uses the same rolling window to decide where to cut a file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stream processing.&lt;/strong&gt; Apache Flink and Kafka Streams both expose windowing as a first-class operator: tumbling windows that never overlap, and sliding or hopping windows that advance by less than their width. The aggregation kept per window is maintained incrementally as records enter and expire.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API rate limiting.&lt;/strong&gt; The sliding-window-counter algorithm approximates a rolling limit by blending the current fixed window's count with a weighted share of the previous window's, which gives per-client memory of two integers instead of one timestamp per request. Cloudflare has published this approach for its rate limiter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Signal processing.&lt;/strong&gt; The short-time Fourier transform behind every spectrogram slides a fixed-width window along a waveform, usually with overlap, and transforms each position independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recomputing the property inside the loop.&lt;/strong&gt; Writing &lt;code&gt;sum(values[left:right + 1])&lt;/code&gt; or &lt;code&gt;len(set(text[left:right + 1]))&lt;/code&gt; inside the loop is the single most common way to write a sliding window that is secretly O(n²). The whole technique is the incremental update; if you recompute, you have kept the code and thrown away the algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;if&lt;/code&gt; instead of &lt;code&gt;while&lt;/code&gt; to shrink.&lt;/strong&gt; One eviction is often not enough. On &lt;code&gt;[1, 2, 1, 3]&lt;/code&gt; with k of 2, when the &lt;code&gt;3&lt;/code&gt; arrives the window holds three distinct values; evicting index 0 removes a &lt;code&gt;1&lt;/code&gt; but another &lt;code&gt;1&lt;/code&gt; remains at index 2, so the count is still 3 and a second eviction is required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting the left edge move backwards.&lt;/strong&gt; The &lt;code&gt;"abba"&lt;/code&gt; bug above. Any expression that assigns &lt;code&gt;left&lt;/code&gt; a value without taking a maximum against its current value should be treated as suspect until you have proved the new value cannot be smaller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaving zero-count keys in the dict.&lt;/strong&gt; &lt;code&gt;counts[value] -= 1&lt;/code&gt; without the &lt;code&gt;del&lt;/code&gt; when it reaches zero means &lt;code&gt;len(counts)&lt;/code&gt; keeps counting a value that is no longer in the window, and the window never shrinks enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recording the answer in the wrong place.&lt;/strong&gt; For a longest-window problem, record after the shrink loop, when the window is valid. For a shortest-window problem, record inside the shrink loop, before evicting. Swapping them gives answers that are plausible and wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mixing up the two length conventions.&lt;/strong&gt; With both ends inclusive the length is &lt;code&gt;right - left + 1&lt;/code&gt;. With &lt;code&gt;right&lt;/code&gt; exclusive it is &lt;code&gt;right - left&lt;/code&gt;. Pick one and use it in every line, including the initial &lt;code&gt;best&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming a window applies because the problem mentions subarrays.&lt;/strong&gt; Check the monotonicity precondition first. If shrinking cannot repair an invalid window, no amount of careful coding will save it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Given a list of numbers and a width k, return the largest average of any k consecutive values.&lt;/li&gt;
&lt;li&gt;Given a string, find the length of the longest substring containing at most two distinct characters.&lt;/li&gt;
&lt;li&gt;Given a list of non-negative integers and a target, find the length of the shortest run whose sum is at least the target, returning 0 if none exists.&lt;/li&gt;
&lt;li&gt;Given two strings, report every index in the first where an anagram of the second begins, using a fixed-width window of character counts.&lt;/li&gt;
&lt;li&gt;Given a list and a width k, return the maximum of every window of that width, keeping the whole thing O(n) with a &lt;code&gt;collections.deque&lt;/code&gt; of indices held in decreasing value order.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;A sliding window replaces recomputation with repair. Fixed-size windows patch a running total with one addition and one subtraction; variable-size windows grow on the right and shrink on the left until the rule holds again. The linear bound comes from counting edge movements rather than loop iterations — each of the two edges makes at most n forward moves over the whole run, so the nested &lt;code&gt;while&lt;/code&gt; costs 2n steps in total and not n² — and the correctness comes from a precondition that is easy to state and easy to forget: shrinking from the left must be able to restore validity. Verify that condition before you write the loop, because when it fails the code still runs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (fixed window)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — k reads for the first window, then 2 per step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (variable window)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) amortised — each edge makes at most n forward moves, so at most 2n steps total&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (brute force)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n²) — a list of n items has n(n + 1) / 2 contiguous runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) for sum windows; O(d) for counting windows, d = distinct values a valid window may hold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precondition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shrinking from the left must restore validity; fails on sums with negative values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List or string with O(1) indexing, plus a dict for the counting variants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The answer is a contiguous run and the property updates in O(1) as elements join and leave&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Values can be negative for a sum rule, or the answer allows gaps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TCP flow control, DEFLATE's 32 KB back-reference window, rolling hashes, Flink and Kafka Streams windowing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;collections.deque(maxlen=k)&lt;/code&gt; for a fixed-width buffer, &lt;code&gt;collections.Counter&lt;/code&gt; for the counts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/two-pointers-technique" rel="noopener noreferrer"&gt;The Two Pointers Technique&lt;/a&gt; — the sibling pattern, for when the answer is a pair of positions rather than a run.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/prefix-sums" rel="noopener noreferrer"&gt;Prefix Sums&lt;/a&gt; — what to reach for when negatives break the window, and how to answer range queries in O(1).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash Tables&lt;/a&gt; — why the counting dicts above are O(1) per update on average, and the prefix-sum lookup trick for negatives.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/rabin-karp-string-matching" rel="noopener noreferrer"&gt;Rabin-Karp&lt;/a&gt; — a rolling hash is the add-one-remove-one update applied to a hash function.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the amortised counting argument used here, from first principles.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Two Pointers Technique in Python: Turning O(n ) Into O(n)</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:38:51 +0000</pubDate>
      <link>https://dev.to/bimal-py/the-two-pointers-technique-in-python-turning-on2-into-on-188l</link>
      <guid>https://dev.to/bimal-py/the-two-pointers-technique-in-python-turning-on2-into-on-188l</guid>
      <description>&lt;p&gt;A nested loop over every pair in a list of 1,000 items inspects 499,500 pairs. Two pointers walking that same list inspect at most 999. That gap is not a tuning detail — it is the difference between code you can run on a million items and code you cannot, and it usually costs about four lines.&lt;/p&gt;

&lt;p&gt;Two pointers is a pattern rather than a single algorithm. You keep two indices into one sequence and move them under a rule that guarantees each index only ever travels in one direction. Because neither index ever backtracks, the total number of moves is bounded by the length of the sequence, and a problem that looked like it needed every pair collapses into a single sweep.&lt;/p&gt;

&lt;p&gt;The catch is that the rule has to be provably safe. Moving a pointer throws away a large set of candidate answers unseen, and that is only legal if none of them could have been the answer. Most of this post is those proofs, because they are the hard part. Once you have the proof, the code writes itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Take a list. Put one index at position 0 and another at position &lt;code&gt;len(values) - 1&lt;/code&gt;. Look at the pair they point at. Based on what you see, move one of them inwards. Repeat until they meet.&lt;/p&gt;

&lt;p&gt;That is the first shape, &lt;strong&gt;converging pointers&lt;/strong&gt;. The second puts both indices near the front and moves both rightwards under different conditions: one reads every element, the other advances only when something interesting happens. That is &lt;strong&gt;same-direction pointers&lt;/strong&gt;, and the fast-and-slow pair used to find cycles in a linked list is the same shape with a fixed speed ratio.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-two-shapes.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-two-shapes.svg" alt="Two arrays showing the converging shape with left and right pointers at opposite ends, and the same-direction shape with a read pointer ahead of a write pointer" width="1000" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both shapes share the property that makes the pattern fast: each pointer moves one way only. In the converging shape &lt;code&gt;left&lt;/code&gt; starts at 0 and only increases, &lt;code&gt;right&lt;/code&gt; starts at &lt;code&gt;len(values) - 1&lt;/code&gt; and only decreases, and the loop stops the instant they meet. Every turn moves at least one of them, so the body runs at most n − 1 times. If the body costs a constant amount, the algorithm is &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That counting argument is easy. The safety argument is not: why is it correct to move on rather than back up and try the pairs you skipped? The answer differs per problem, and it always comes from some ordering property of the data — which is why most converging-pointer problems need sorted input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Given a list sorted in ascending order, find two positions whose values add up to a target. Take &lt;code&gt;[1, 3, 4, 6, 8, 11]&lt;/code&gt; and a target of &lt;code&gt;10&lt;/code&gt;, with &lt;code&gt;left&lt;/code&gt; at index 0 and &lt;code&gt;right&lt;/code&gt; at index 5.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1 + 11 = 12&lt;/code&gt;. Too big. Move &lt;code&gt;right&lt;/code&gt; in to index 4.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1 + 8 = 9&lt;/code&gt;. Too small. Move &lt;code&gt;left&lt;/code&gt; in to index 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3 + 8 = 11&lt;/code&gt;. Too big. Move &lt;code&gt;right&lt;/code&gt; in to index 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3 + 6 = 9&lt;/code&gt;. Too small. Move &lt;code&gt;left&lt;/code&gt; in to index 2.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;4 + 6 = 10&lt;/code&gt;. Found it, at indices 2 and 3.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-pair-sum.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-pair-sum.svg" alt="The list 1, 3, 4, 6, 8, 11 across five steps, with the left and right pointers closing in on the pair 4 and 6" width="1000" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five steps. There are 15 distinct pairs in a six-element list, and a nested loop would have stumbled onto this answer on its tenth. But the count is not the interesting bit — the interesting bit is &lt;em&gt;why the skipped pairs never needed checking at all&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why discarding a whole row is safe
&lt;/h3&gt;

&lt;p&gt;Draw every pair as a table. Rows are the left value, columns are the right value, each cell holds their sum. Only cells where the left index is smaller than the right index are real pairs; the rest are blank.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-search-grid.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-search-grid.svg" alt="A table of every pair sum for the list, with the five examined cells forming a staircase and the pruned rows and columns marked" width="1000" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This table is sorted along both axes, and that follows directly from the list being sorted. Move right along a row: the left value is fixed while the right value grows, so the sums grow. Move down a column: the right value is fixed while the left value grows, so the sums grow again.&lt;/p&gt;

&lt;p&gt;Now look at where the algorithm starts — the &lt;strong&gt;top-right corner&lt;/strong&gt;. That cell is simultaneously the largest sum in its row and the smallest sum in its column. Everything follows from that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the corner is &lt;strong&gt;too big&lt;/strong&gt;, it is the smallest entry in its column, so every other sum in that column is bigger still. Nothing there can be the target. Delete the whole column, which is exactly what &lt;code&gt;right -= 1&lt;/code&gt; does.&lt;/li&gt;
&lt;li&gt;If the corner is &lt;strong&gt;too small&lt;/strong&gt;, it is the largest entry in its row, so every other sum in that row is smaller still. Delete the whole row, which is exactly what &lt;code&gt;left += 1&lt;/code&gt; does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each step retires a full row or column without looking inside it. There are only n rows and n columns to retire, so the walk ends within 2n steps even though the table holds n(n − 1) / 2 cells. The path is a staircase from the corner down to the answer — the same walk used to search a matrix whose rows and columns are both sorted.&lt;/p&gt;

&lt;p&gt;Keep both halves of that in your head. "Two pointers is O(n)" is a fact about the loop; "each move retires a row or a column" is why the loop is allowed to skip that much.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Start with the version that needs no cleverness, so there is something to compare against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Postponed annotation evaluation, so `X | None` works on Python 3.9 too.
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pair_sum_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return the indices of the first pair that adds to target, or None.

    Works on any list, sorted or not, because it checks every pair.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pair_sum_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pair_sum_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(2, 3)
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the two-pointer version, plus a traced copy that prints the walkthrough above so you can check the hand trace against the machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pair_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Find two indices in an ascending-sorted list whose values add to target.

    Returns the pair of indices, or None if no such pair exists. Runs in O(n)
    time and O(1) space. The list must already be sorted; this does not check.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Even the largest partner still in play was too small, so no
&lt;/span&gt;            &lt;span class="c1"&gt;# remaining pair that uses values[left] can reach the target.
&lt;/span&gt;            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Even the smallest partner still in play was too large.
&lt;/span&gt;            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pair_sum_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same algorithm, printing every step it takes.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;too small, move left in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;verdict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;too big, move right in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; right=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; + &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;verdict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pair_sum_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pair_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;left=0 right=5   1 + 11 = 12  too big, move right in
left=0 right=4   1 +  8 =  9  too small, move left in
left=1 right=4   3 +  8 = 11  too big, move right in
left=1 right=3   3 +  6 =  9  too small, move left in
left=2 right=3   4 +  6 = 10  found
(2, 3)
None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gap between the two grows fast. Here both versions hunt for a target that does not exist in a sorted list of 1,000 even numbers, so neither can exit early, and the counters record how many pairs each one actually inspects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;How many pairs the nested loops inspect.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;looks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;looks&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;looks&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;looks&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_two_pointers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;How many pairs the converging pointers inspect.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;looks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;looks&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;looks&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;looks&lt;/span&gt;


&lt;span class="n"&gt;evens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;count_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;count_two_pointers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;499500 999
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;while left &amp;lt; right&lt;/code&gt;&lt;/strong&gt; is strict, not &lt;code&gt;left &amp;lt;= right&lt;/code&gt;. With &lt;code&gt;&amp;lt;=&lt;/code&gt; the loop eventually reaches a state where both indices point at the same element, and &lt;code&gt;pair_sum&lt;/code&gt; would report that element paired with itself — a real bug when the target is exactly twice some value in the list. Strictness also gives the termination proof: the gap shrinks by at least one every turn and the loop stops at zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The three-way branch&lt;/strong&gt; is the staircase walk. Equality is the answer, &lt;code&gt;total &amp;lt; target&lt;/code&gt; deletes a row, &lt;code&gt;total &amp;gt; target&lt;/code&gt; deletes a column. Exactly one pointer moves per branch, which keeps both claims true: the loop always makes progress, and it never discards a row and a column in the same step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nothing is stored.&lt;/strong&gt; Two integers and a sum, whatever the size of the list, so extra space is O(1). That is often the real reason to prefer it over a lookup table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases fall out of the arithmetic.&lt;/strong&gt; An empty list gives &lt;code&gt;left = 0&lt;/code&gt; and &lt;code&gt;right = -1&lt;/code&gt;, so the loop never runs. A one-element list gives &lt;code&gt;left = 0&lt;/code&gt; and &lt;code&gt;right = 0&lt;/code&gt;, likewise. Neither needs a guard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sorted precondition is not checked&lt;/strong&gt;, deliberately: checking costs O(n) and defeats the point of a function you call inside a loop. On unsorted input it does not raise, it silently returns the wrong answer — which is worse, so document the precondition and mean it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other variants worth knowing
&lt;/h2&gt;

&lt;p&gt;The pair sum is the cleanest example, but the shape shows up in a dozen disguises. These are the ones worth being able to write from memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Palindromes
&lt;/h3&gt;

&lt;p&gt;The same converging walk, without the arithmetic. Compare the ends, step inwards, stop the moment they disagree. There is nothing to prove beyond the definition: a string is a palindrome exactly when every character matches its mirror, and the loop checks each mirror pair once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_palindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;True if text reads the same both ways, ignoring case and punctuation.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isalnum&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A man, a plan, a canal: Panama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;race a car&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="si"&gt;!r:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;is_palindrome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'A man, a plan, a canal: Panama'   True
'race a car'                       False
'ab'                               False
'a'                                True
''                                 True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing this as &lt;code&gt;cleaned == cleaned[::-1]&lt;/code&gt; is shorter and, in CPython, faster — the reversal runs in C. The two-pointer version wins on space, because it never builds the second copy, and it bails out on the first mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reversing in place
&lt;/h3&gt;

&lt;p&gt;Swap the ends, then close in. The loop runs &lt;code&gt;len(values) // 2&lt;/code&gt; times, exactly the minimum number of swaps needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reverse_in_place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Reverse a list by swapping the two ends and closing inwards.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;


&lt;span class="n"&gt;letters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abcdef&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;reverse_in_place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;digits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;reverse_in_place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fedcba
[5, 4, 3, 2, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In real Python you would write &lt;code&gt;values.reverse()&lt;/code&gt;, which is this loop implemented in C. Write it once by hand anyway; it is the smallest version of the pattern there is.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing duplicates from a sorted list, in place
&lt;/h3&gt;

&lt;p&gt;This is the same-direction shape. One pointer, &lt;code&gt;read&lt;/code&gt;, visits every index. Another, &lt;code&gt;last_kept&lt;/code&gt;, marks the end of the answer being built at the front of the same list. Because the list is sorted, equal values are always adjacent, so one comparison against the last value kept decides whether the current one is new.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;remove_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Compact a sorted list in place so that values[:kept] are its distinct
    values, and return kept.

    Everything from index kept onwards is left as whatever happened to be
    there and must be ignored. O(n) time, O(1) extra space.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;last_kept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;read&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="c1"&gt;# values is sorted, so a repeat can only ever sit next to the value it
&lt;/span&gt;        &lt;span class="c1"&gt;# repeats. One comparison against the last value kept is enough.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;last_kept&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;last_kept&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;last_kept&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;last_kept&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;


&lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;kept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;remove_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;longer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;kept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;remove_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;longer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longer&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;remove_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 [1, 2, 3] [1, 2, 3, 2, 3, 3]
5 [0, 1, 2, 3, 4]
0 []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-dedupe.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-dedupe.svg" alt="The list 1, 1, 1, 2, 3, 3 being compacted in place, with the read pointer scanning ahead of the kept pointer at each comparison" width="1000" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the printed tail: &lt;code&gt;sample&lt;/code&gt; ends up as &lt;code&gt;[1, 2, 3, 2, 3, 3]&lt;/code&gt;. The function does not shorten the list, it only guarantees the first &lt;code&gt;kept&lt;/code&gt; entries are correct, and forgetting that is the most common bug in this variant. The write pointer can never overtake the read pointer — &lt;code&gt;last_kept&lt;/code&gt; starts behind and advances at most once per turn — so nothing is overwritten before it has been read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fast and slow
&lt;/h3&gt;

&lt;p&gt;Give the two same-direction pointers a fixed speed ratio instead of a condition and you get a different tool. Advance &lt;code&gt;slow&lt;/code&gt; one node per turn and &lt;code&gt;fast&lt;/code&gt; two, and when &lt;code&gt;fast&lt;/code&gt; falls off the end, &lt;code&gt;slow&lt;/code&gt; sits at the midpoint. Run the same pair inside a &lt;a href="https://bimalkhatri.com.np/blogs/linked-lists" rel="noopener noreferrer"&gt;linked list&lt;/a&gt; that loops back on itself and they must collide, because &lt;code&gt;fast&lt;/code&gt; closes the gap by exactly one node per turn, so inside a loop of length L they meet within L turns. That is Floyd's tortoise and hare.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A singly linked list node, here only to demonstrate fast and slow.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;
        &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;middle_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The value halfway along a linked list, found in a single pass.

    slow advances one node per turn and fast advances two, so fast reaches
    the end after slow has covered exactly half the nodes.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;slow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Floyd&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s tortoise and hare.

    fast closes the gap on slow by exactly one node per turn, so once both are
    inside a loop of length L they must collide within L turns.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;slow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="n"&gt;fast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;slow&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;fast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="n"&gt;odd_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_list&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;even_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_list&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;middle_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;odd_list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;middle_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;even_list&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;middle_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;has_cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;odd_list&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;looped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_list&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;looped&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;
&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;looped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;  &lt;span class="c1"&gt;# the last node now points back at the second
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;has_cycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;looped&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3 3 None
False
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A list of even length has two middles and this version returns the second, which is why &lt;code&gt;[1, 2, 3, 4]&lt;/code&gt; prints &lt;code&gt;3&lt;/code&gt;. Cycle detection is where the shape really earns its keep: the obvious alternative, a set holding every node already visited, costs O(n) memory. Two pointers cost two.&lt;/p&gt;

&lt;h3&gt;
  
  
  Container with most water
&lt;/h3&gt;

&lt;p&gt;Treat each entry in a list of heights as a vertical line and pick the two lines that hold the most water between them. The area of a pair is the distance between them times the height of the shorter one, because water spills over the shorter side.&lt;/p&gt;

&lt;p&gt;This is the variant worth studying, because the input is &lt;strong&gt;not sorted&lt;/strong&gt; and the proof still works. Start at the two ends, the widest pair possible. Whichever line is shorter caps the area of every pair it belongs to, and every pair it has left is narrower than the one just measured. Its best remaining area is therefore strictly less than the area already recorded, so it can be discarded — safely, permanently, without looking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-container.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-container.svg" alt="Four steps of the container problem showing the shorter line being retired at each step and the best area of 49 found on the second step" width="1000" height="744"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;max_water&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The most water two of the vertical lines can hold between them.

    The area of a pair is the distance between the lines times the height of
    the shorter one, because water spills over the shorter side.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;area&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;area&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# The shorter line caps every rectangle it appears in, and every pair
&lt;/span&gt;        &lt;span class="c1"&gt;# it has left is narrower than this one, so it can never do better.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;max_water_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Check all n(n - 1) / 2 pairs. Used here only to verify the fast one.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="n"&gt;best&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;heights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max_water&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="n"&gt;agree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;max_water&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;max_water_brute_force&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches the brute force on all 300 profiles:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agree&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;49
matches the brute force on all 300 profiles: True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checking against the quadratic version is not a proof, but it is the right habit. When a linear algorithm claims to match an exhaustive one, run both on a few hundred inputs before believing yourself.&lt;/p&gt;

&lt;h3&gt;
  
  
  3-sum: fix one, two-pointer the rest
&lt;/h3&gt;

&lt;p&gt;Find every distinct triple that adds to zero. The exhaustive version is three nested loops, O(n³). The fix is to stop treating it as a three-dimensional problem: sort the list, fix the first value, and what remains is "find two values that add to minus the fixed one" — the pair sum you already have.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-three-sum.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ftwo-pointers-technique-three-sum.svg" alt="A four-step flow: sort the list, fix the first value, sweep the rest with two pointers, skip equal values" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sorting does double duty. It makes the inner sweep legal, and it puts equal values next to each other so duplicate triples are cheap to skip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;three_sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every distinct triple of values that adds to zero, smallest first.

    Sorting is what makes the inner two-pointer sweep legal, and it is also
    what makes duplicates cheap to skip: equal values end up side by side.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;numbers_sorted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;triples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;  &lt;span class="c1"&gt;# three numbers all above zero cannot add to zero
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;  &lt;span class="c1"&gt;# this value has already been the fixed element
&lt;/span&gt;
        &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;triples&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;numbers_sorted&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;triples&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;three_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;three_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;three_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;three_sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[(-1, -1, 2), (-1, 0, 1)]
[(0, 0, 0)]
[]
[(-2, 0, 2), (-2, 1, 1)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;break&lt;/code&gt; on the first positive value is a small win worth understanding: once the smallest of the three is above zero, all three are, so no triple from here on can reach zero. On a list of mostly positive numbers that ends the outer loop almost immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The converging sweep is O(n).&lt;/strong&gt; Each turn does a fixed amount of work — one addition, one comparison, one increment — and moves exactly one pointer. The two pointers share a single budget: the gap between them starts at n − 1 and shrinks by one per turn, so the loop runs at most n − 1 times. There is no hidden cost inside the body. That n − 1 is the worst case, and every miss pays it in full, because the loop only stops when the pointers meet. The best case is O(1) — the first pair tried, smallest plus largest, is already the target. A hit stops the moment it is found, so on average you pay about half the sweep, which is still O(n).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space is O(1) for the sweep itself.&lt;/strong&gt; Two indices and a running total, regardless of input size. Two of the variants above break that, and the break is in the code rather than in the idea. &lt;code&gt;is_palindrome&lt;/code&gt; builds a cleaned list before it starts, so it holds O(n); skip the non-alphanumerics inline instead and it is O(1) again. &lt;code&gt;three_sum&lt;/code&gt; calls &lt;code&gt;sorted()&lt;/code&gt;, which returns a new list, so it holds O(n) as well — and then O(n) more for the triples it collects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sorting, when you need it, dominates.&lt;/strong&gt; If the input arrives unsorted, the honest bound for the pair sum is O(n log n) for the sort plus O(n) for the sweep, which is O(n log n). The log comes from the sort: &lt;a href="https://bimalkhatri.com.np/blogs/merge-sort" rel="noopener noreferrer"&gt;merge sort&lt;/a&gt; halves the problem repeatedly, giving log₂ n levels with all n items touched at each level. Timsort, the algorithm behind Python's &lt;code&gt;sorted()&lt;/code&gt;, gets to the same bound a different way — it merges runs that are already in order rather than halving — but its worst case is also O(n log n). Sorting costs memory too, since &lt;code&gt;sorted()&lt;/code&gt; builds a whole new list. Do not quote O(n) time or O(1) space for a function that sorts first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3-sum is O(n²).&lt;/strong&gt; The outer loop fixes each of n values in turn, and each one costs an O(n) sweep: n × n. The &lt;code&gt;sorted()&lt;/code&gt; call adds O(n log n), which n² swallows. That is a big improvement on the O(n³) triple loop, but it is still quadratic — 10,000 inputs means roughly 100 million inner steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fast and slow is O(n) time and O(1) space.&lt;/strong&gt; &lt;code&gt;fast&lt;/code&gt; advances two nodes per turn and falls off the end within n / 2 turns; with a cycle it collides within one extra lap.&lt;/p&gt;

&lt;p&gt;Here is the comparison that matters, on the pair sum with the input already sorted:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Items&lt;/th&gt;
&lt;th&gt;Pairs a nested loop checks&lt;/th&gt;
&lt;th&gt;Steps two pointers take&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;4,950&lt;/td&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;499,500&lt;/td&gt;
&lt;td&gt;999&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;49,995,000&lt;/td&gt;
&lt;td&gt;9,999&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;td&gt;4,999,950,000&lt;/td&gt;
&lt;td&gt;99,999&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ten times the data costs a hundred times the work on the left and ten times on the right. At 100,000 items that is exactly 50,000 times fewer steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it when&lt;/strong&gt; the input is sorted (or you can sort it cheaply), the answer is a &lt;em&gt;pair of positions&lt;/em&gt; rather than a range, and extra space has to stay constant. Sorted arrays, strings compared from both ends, in-place compaction and merging two sorted sequences are all natural fits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it on unsorted input just because the shape is familiar.&lt;/strong&gt; For "do two values add to this target" on unsorted data, one pass with a set is better: O(n) expected time and no sort at all. It costs O(n) memory, and that is the trade. Two pointers only win when the data is already sorted or memory is genuinely tight. &lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash tables&lt;/a&gt; explain why that lookup is O(1) on average — average is the whole guarantee, and keys chosen to collide can drag a single lookup back to O(n).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the answer is a contiguous run.&lt;/strong&gt; That is the &lt;a href="https://bimalkhatri.com.np/blogs/sliding-window-technique" rel="noopener noreferrer"&gt;sliding window&lt;/a&gt;, which is a two-pointer method too — both ends move rightwards — but the question is different. A window cares about everything &lt;em&gt;between&lt;/em&gt; its ends: the sum of the run, the number of distinct characters in it, whether it is still valid. Converging pointers care about the pair itself and mostly ignore what sits between them. If the answer is a subarray or substring, reach for a window; if it is two positions, reach for two pointers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the property you rely on is not monotonic.&lt;/strong&gt; The pattern rests entirely on "moving this pointer can only change things in one predictable direction". If moving &lt;code&gt;left&lt;/code&gt; could send the sum either way, the safety proof collapses and the algorithm returns wrong answers without complaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Merging sorted sequences.&lt;/strong&gt; The merge step of &lt;a href="https://bimalkhatri.com.np/blogs/merge-sort" rel="noopener noreferrer"&gt;merge sort&lt;/a&gt; is two pointers, one into each of two sorted halves, each advancing when its value is taken. The same walk is a &lt;strong&gt;sort-merge join&lt;/strong&gt; in relational databases: PostgreSQL's Merge Join advances a cursor over each of two sorted inputs, taking whichever key is smaller. It is also how LSM-tree storage engines such as RocksDB and LevelDB compact several sorted files into one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CPython's string stripping.&lt;/strong&gt; &lt;code&gt;str.strip()&lt;/code&gt; walks an index forward from the start while the character is whitespace, then walks a second index backward from the end under the same test, and slices between them. It is a converging pair with a very simple rule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mark-compact garbage collectors.&lt;/strong&gt; Sliding compaction walks the heap with a read pointer and a write pointer, copying every live object down over the dead ones, exactly like the in-place deduplication above. The write pointer trails the read pointer, so no live object is overwritten before it has been moved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pollard's rho factorisation&lt;/strong&gt; uses Floyd's tortoise and hare to detect the cycle in the pseudo-random sequence it generates. That is what lets it run in constant memory instead of storing every value it has seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Feeding it unsorted input.&lt;/strong&gt; &lt;code&gt;pair_sum([8, 1, 3], 4)&lt;/code&gt; returns &lt;code&gt;None&lt;/code&gt;. Trace it: &lt;code&gt;8 + 3 = 11&lt;/code&gt; is too big so &lt;code&gt;right&lt;/code&gt; moves to index 1, then &lt;code&gt;8 + 1 = 9&lt;/code&gt; is still too big so &lt;code&gt;right&lt;/code&gt; moves to index 0, and the loop ends — even though &lt;code&gt;1 + 3&lt;/code&gt; is exactly 4. No exception, just a wrong answer. Sort first, or use a set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;left &amp;lt;= right&lt;/code&gt;.&lt;/strong&gt; The loop then compares an element with itself. For &lt;code&gt;pair_sum([2, 5, 7], 10)&lt;/code&gt; that would report indices 1 and 1 as a valid pair for the target 10.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Moving both pointers on a mismatch.&lt;/strong&gt; It feels symmetric and it is wrong. On &lt;code&gt;[2, 3, 4, 5]&lt;/code&gt; with target 8, the correct walk goes &lt;code&gt;2 + 5 = 7&lt;/code&gt; (too small, move &lt;code&gt;left&lt;/code&gt;) then &lt;code&gt;3 + 5 = 8&lt;/code&gt; and finds it. Moving both jumps straight to &lt;code&gt;3 + 4 = 7&lt;/code&gt;, then the pointers cross and the answer is missed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting a branch that moves nothing.&lt;/strong&gt; Every path through the loop body must advance a pointer or return. A condition that leaves both indices untouched is an infinite loop, and it is the failure mode you will hit first when adapting the pattern to a new problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating the in-place result as a shortened list.&lt;/strong&gt; &lt;code&gt;remove_duplicates&lt;/code&gt; returns a count, and the entries past that count are stale. Always slice with the returned length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping duplicate handling in 3-sum.&lt;/strong&gt; Without the &lt;code&gt;continue&lt;/code&gt; for a repeated fixed value, &lt;code&gt;[-1, -1, 0, 1, 1]&lt;/code&gt; yields the triple &lt;code&gt;(-1, 0, 1)&lt;/code&gt; twice — once for each &lt;code&gt;-1&lt;/code&gt; in the input. The skips are not an optimisation, they are part of the specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Given a sorted list and a target, count how many index pairs add to that target, including when the same value appears more than once.&lt;/li&gt;
&lt;li&gt;Move every zero in a list to the end while keeping the order of the non-zero values, in one pass with O(1) extra space.&lt;/li&gt;
&lt;li&gt;Merge two already-sorted lists into one sorted list using one pointer into each, without calling &lt;code&gt;sorted()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Given a list sorted in ascending order that may contain negatives, return the squares of its values in sorted order in O(n) — the trick is to fill the output from the back.&lt;/li&gt;
&lt;li&gt;Given a list of bar heights, compute the total rainwater trapped between them using two converging pointers and a running maximum from each side.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Two pointers turns a quadratic search over pairs into a linear sweep by making each step retire an entire row or column of the pairs you never have to check. The loop bound is easy — neither index backtracks, so the body runs at most n times — and the real work is the safety proof that lets you move a pointer at all. Learn the staircase argument on the sorted pair sum, then notice the same shape in palindromes, in-place compaction, cycle detection and the container problem.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (sorted input)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — the gap between the pointers shrinks by one per turn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (unsorted input)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n log n) — the sort dominates the O(n) sweep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time (3-sum)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n²) — n fixed values, each costing one linear sweep&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) — two indices and a running value; O(n) as soon as you sort or copy first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;In place&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes, for the compaction and reversal variants&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precondition&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sorted input, or some other monotonic property to move on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;List / array with O(1) indexing; linked list for fast and slow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The answer is a pair of positions and memory must stay constant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data is unsorted and a set is affordable, or the answer is a contiguous run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Merge joins, LSM compaction, &lt;code&gt;str.strip()&lt;/code&gt;, mark-compact garbage collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;values.reverse()&lt;/code&gt;, &lt;code&gt;heapq.merge()&lt;/code&gt;, &lt;code&gt;bisect&lt;/code&gt; for the sorted-search half&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/sliding-window-technique" rel="noopener noreferrer"&gt;The Sliding Window Technique&lt;/a&gt; — the other two-index pattern, for when the answer is a contiguous run rather than a pair.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/binary-search" rel="noopener noreferrer"&gt;Binary Search&lt;/a&gt; — the other way to exploit sorted input, and what pairs with two pointers when you need a partner rather than a pair.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/merge-sort" rel="noopener noreferrer"&gt;Merge Sort&lt;/a&gt; — its merge step is two pointers over two sorted lists, and it is where the O(n log n) sorting cost comes from.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/linked-lists" rel="noopener noreferrer"&gt;Linked Lists&lt;/a&gt; — build the structure that fast and slow pointers traverse, including cycles.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting arguments used throughout this post, from first principles.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Fast Exponentiation in Python: Computing Huge Powers in log n Steps</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:38:19 +0000</pubDate>
      <link>https://dev.to/bimal-py/fast-exponentiation-in-python-computing-huge-powers-in-log-n-steps-57nh</link>
      <guid>https://dev.to/bimal-py/fast-exponentiation-in-python-computing-huge-powers-in-log-n-steps-57nh</guid>
      <description>&lt;p&gt;Ask Python for &lt;code&gt;2 ** 10**18 % 1000000007&lt;/code&gt; and it will never answer. Not because the modulus is hard, but because &lt;code&gt;**&lt;/code&gt; is evaluated first, and &lt;code&gt;2 ** 10**18&lt;/code&gt; is a number with 10^18 binary digits. Writing it down needs about 125 petabytes of memory. The machine will thrash and die long before the &lt;code&gt;%&lt;/code&gt; runs.&lt;/p&gt;

&lt;p&gt;Ask Python for &lt;code&gt;pow(2, 10**18, 1000000007)&lt;/code&gt; and it answers instantly. Same value, 83 multiplications, and every value it carries along the way stays below the ten-digit modulus. The gap between those two lines is one of the largest practical speedups in all of elementary computing, and it comes from a single identity you already know: &lt;code&gt;a^n&lt;/code&gt; for even &lt;code&gt;n&lt;/code&gt; equals &lt;code&gt;(a^(n/2))^2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Halve the exponent, square the answer. Repeat. An exponent of 10^18 reaches zero in 60 halvings, not 10^18 decrements. That is fast exponentiation — also called binary exponentiation, exponentiation by squaring, or square-and-multiply — and it is the arithmetic that every RSA handshake and every Diffie-Hellman key exchange on the internet is built from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Start with the obvious way to compute &lt;code&gt;a^n&lt;/code&gt;: multiply by &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;n&lt;/code&gt; times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;naive_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Multiply base by itself, one multiplication per unit of the exponent.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1594323
1024
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That costs &lt;code&gt;n&lt;/code&gt; multiplications. The cost is proportional to the &lt;em&gt;value&lt;/em&gt; of the exponent, which is exponential in the number of digits you typed. A 19-digit exponent means a quintillion multiplications.&lt;/p&gt;

&lt;p&gt;Now the identity. For any &lt;code&gt;a&lt;/code&gt; and any even &lt;code&gt;n&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a^n = (a^(n/2))^2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Read it right to left and it is a bargain. To get &lt;code&gt;a^n&lt;/code&gt;, you do not need &lt;code&gt;a^n&lt;/code&gt;. You need &lt;code&gt;a^(n/2)&lt;/code&gt;, and then one squaring. Half the problem, plus one multiplication.&lt;/p&gt;

&lt;p&gt;Odd exponents cannot be halved cleanly, so peel one factor off first:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a^n = a × a^(n-1)&lt;/code&gt; when &lt;code&gt;n&lt;/code&gt; is odd&lt;/p&gt;

&lt;p&gt;Subtracting 1 from an odd number always leaves an even number, so an odd step is immediately followed by a halving. The two rules together, plus the base case &lt;code&gt;a^0 = 1&lt;/code&gt;, define the whole algorithm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; is 0: the answer is 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; is even: compute &lt;code&gt;a^(n/2)&lt;/code&gt;, square it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; is odd: compute &lt;code&gt;a^(n-1)&lt;/code&gt;, multiply by &lt;code&gt;a&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every two steps at worst, the exponent halves. Halving 10^18 takes 60 steps. That is the entire performance argument.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-halving.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-halving.svg" alt="The chain of recursive calls for 3 to the power 13, halving on even exponents and peeling one factor off odd ones" width="1000" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take &lt;code&gt;3^13&lt;/code&gt;. The naive loop would do 12 multiplications. Here is what the halving rules do instead, working downwards from the top:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;3^13&lt;/code&gt; — 13 is odd, so &lt;code&gt;3^13 = 3 × 3^12&lt;/code&gt;. Need &lt;code&gt;3^12&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^12&lt;/code&gt; — even, so &lt;code&gt;3^12 = (3^6)^2&lt;/code&gt;. Need &lt;code&gt;3^6&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^6&lt;/code&gt; — even, so &lt;code&gt;3^6 = (3^3)^2&lt;/code&gt;. Need &lt;code&gt;3^3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^3&lt;/code&gt; — odd, so &lt;code&gt;3^3 = 3 × 3^2&lt;/code&gt;. Need &lt;code&gt;3^2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^2&lt;/code&gt; — even, so &lt;code&gt;3^2 = (3^1)^2&lt;/code&gt;. Need &lt;code&gt;3^1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^1&lt;/code&gt; — odd, so &lt;code&gt;3^1 = 3 × 3^0&lt;/code&gt;. Need &lt;code&gt;3^0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;3^0&lt;/code&gt; — the base case, 1.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now unwind, filling in real numbers on the way back up: &lt;code&gt;3^0 = 1&lt;/code&gt;, &lt;code&gt;3^1 = 3&lt;/code&gt;, &lt;code&gt;3^2 = 9&lt;/code&gt;, &lt;code&gt;3^3 = 27&lt;/code&gt;, &lt;code&gt;3^6 = 729&lt;/code&gt;, &lt;code&gt;3^12 = 531441&lt;/code&gt;, &lt;code&gt;3^13 = 1594323&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Six lines, and only five of them are genuine multiplications — &lt;code&gt;3 × 3^0&lt;/code&gt; is a multiplication by 1, which costs nothing in principle. Twelve multiplications became five.&lt;/p&gt;

&lt;h3&gt;
  
  
  The same thing, seen in binary
&lt;/h3&gt;

&lt;p&gt;There is a second way to look at this that explains the name &lt;em&gt;square-and-multiply&lt;/em&gt;, and it is the one the iterative code implements.&lt;/p&gt;

&lt;p&gt;Write the exponent in binary. 13 is &lt;code&gt;1101&lt;/code&gt;, which says &lt;code&gt;13 = 8 + 4 + 1&lt;/code&gt;. Exponents add when powers multiply, so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;3^13 = 3^8 × 3^4 × 3^1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can build &lt;code&gt;3^1&lt;/code&gt;, &lt;code&gt;3^2&lt;/code&gt;, &lt;code&gt;3^4&lt;/code&gt;, &lt;code&gt;3^8&lt;/code&gt; by starting at &lt;code&gt;3&lt;/code&gt; and squaring repeatedly — each squaring doubles the exponent, so four squarings reach &lt;code&gt;3^16&lt;/code&gt;. Then keep only the ones whose bit is set and multiply them together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-binary-view.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-binary-view.svg" alt="The binary decomposition of the exponent 13, keeping the repeated squares whose bit is set" width="1000" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;6561 × 81 × 3 = 1594323&lt;/code&gt;. Two multiplications to combine them, three squarings to build them: five, matching the recursion exactly. That is not a coincidence — the recursion's even steps &lt;em&gt;are&lt;/em&gt; the squarings and its odd steps &lt;em&gt;are&lt;/em&gt; the kept bits, read in the opposite order.&lt;/p&gt;

&lt;p&gt;The number of squarings is the number of bits in &lt;code&gt;n&lt;/code&gt; minus one. The number of combining multiplications is the number of 1 bits minus one. Both are at most log2(n), which is where the bound comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The recursive version reads exactly like the three rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;base ** exponent, computed with the halving identity.

    An even exponent squares the half power. An odd exponent peels off one
    factor of base, which always leaves an even exponent behind.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1594323
1024
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a print and you can watch the walkthrough happen for real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;traced_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same recursion, printing each power as the calls unwind.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;^0 = 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;traced_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)^2 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;half&lt;/span&gt;
    &lt;span class="n"&gt;smaller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;traced_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; * &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;^&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;smaller&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;smaller&lt;/span&gt;


&lt;span class="nf"&gt;traced_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;3^0 = 1
3^1 = 3 * 3^0 = 3
3^2 = (3^1)^2 = 9
3^3 = 3 * 3^2 = 27
3^6 = (3^3)^2 = 729
3^12 = (3^6)^2 = 531441
3^13 = 3 * 3^12 = 1594323
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The iterative version walks the exponent's bits from the bottom up. It keeps two values: &lt;code&gt;result&lt;/code&gt;, the answer accumulated so far, and &lt;code&gt;current&lt;/code&gt;, which holds &lt;code&gt;base&lt;/code&gt; raised to the next power of two.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_power_iterative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Square-and-multiply, driven by the bits of the exponent from the bottom up.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;       &lt;span class="c1"&gt;# after step k this holds base ** (2 ** k)
&lt;/span&gt;    &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;remaining=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bit=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  current=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; result=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# The last squaring would never be used, so skip it.
&lt;/span&gt;            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power_iterative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;fast_power_iterative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1594323
remaining=13  bit=1  current=3     result=3
remaining=6   bit=0  current=9     result=3
remaining=3   bit=1  current=81    result=243
remaining=1   bit=1  current=6561  result=1594323
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-square-multiply.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-square-multiply.svg" alt="The four iterations of the square-and-multiply loop for 3 to the power 13, showing the remaining exponent, its low bit, and both accumulators" width="1000" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;remaining % 2&lt;/code&gt; is the current low bit of the exponent&lt;/strong&gt;, and &lt;code&gt;remaining //= 2&lt;/code&gt; is the halving — a right shift by one place. So the loop runs once per bit of &lt;code&gt;n&lt;/code&gt;, which for 10^18 is 60 times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;current&lt;/code&gt; is the repeated-squaring chain.&lt;/strong&gt; It starts at &lt;code&gt;base&lt;/code&gt; and squares every iteration, so it passes through &lt;code&gt;base^1&lt;/code&gt;, &lt;code&gt;base^2&lt;/code&gt;, &lt;code&gt;base^4&lt;/code&gt;, &lt;code&gt;base^8&lt;/code&gt; and so on. Iteration &lt;code&gt;k&lt;/code&gt; always has &lt;code&gt;current&lt;/code&gt; equal to &lt;code&gt;base ** (2 ** k)&lt;/code&gt;, which is exactly the value the binary decomposition wants when bit &lt;code&gt;k&lt;/code&gt; is set.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;result *= current&lt;/code&gt; is the "keep this one" decision.&lt;/strong&gt; It fires only when the bit is 1, which is why the loop's multiplication count depends on how many 1 bits &lt;code&gt;n&lt;/code&gt; has.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The order inside the loop matters.&lt;/strong&gt; You must fold &lt;code&gt;current&lt;/code&gt; into &lt;code&gt;result&lt;/code&gt; &lt;em&gt;before&lt;/em&gt; squaring &lt;code&gt;current&lt;/code&gt;, because the bit you just examined refers to the current value, not the next one. Swapping those two lines silently produces &lt;code&gt;base ** (2 * n)&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The final squaring is skipped&lt;/strong&gt; by the &lt;code&gt;if remaining &amp;gt; 0&lt;/code&gt; guard. Nothing reads &lt;code&gt;current&lt;/code&gt; after the last iteration, and that skipped squaring is the most expensive one, on the biggest numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The recursion's depth is logarithmic, not linear.&lt;/strong&gt; Each odd step is followed by an even step, so &lt;code&gt;fast_power&lt;/code&gt; recurses at most &lt;code&gt;2 × log2(n)&lt;/code&gt; deep — about 120 frames for a 10^18 exponent, nowhere near Python's default limit of 1000.&lt;/p&gt;

&lt;p&gt;Counting the multiplications directly makes the growth impossible to argue with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;multiplication_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Multiplications square-and-multiply performs, without performing them.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;          &lt;span class="c1"&gt;# fold base ** (2 ** k) into the result
&lt;/span&gt;        &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;          &lt;span class="c1"&gt;# square to reach the next power of two
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bits in n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;naive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fast&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bit_length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;multiplication_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  n  bits in n                naive  fast
                 13          4                   12     6
                100          7                   99     9
               1000         10                  999    15
            1000000         20               999999    26
1000000000000000000         60   999999999999999999    83
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quintillion multiplications become 83, and &lt;code&gt;fast&lt;/code&gt; never exceeds twice &lt;code&gt;bits in n&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modular exponentiation
&lt;/h2&gt;

&lt;p&gt;Everything above still produces enormous numbers: &lt;code&gt;7^1000&lt;/code&gt; looks harmless and has 846 digits. In cryptography you never want &lt;code&gt;a^n&lt;/code&gt; itself — you want &lt;code&gt;a^n mod m&lt;/code&gt;, and there the numbers can be kept small at every step.&lt;/p&gt;

&lt;p&gt;The rule that makes this work is that modular arithmetic survives multiplication:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;(x × y) mod m = ((x mod m) × (y mod m)) mod m&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So you can reduce after every single multiplication instead of at the end. No intermediate value ever exceeds &lt;code&gt;m^2&lt;/code&gt;, which for a 2048-bit modulus means numbers of at most 4096 bits rather than numbers of &lt;code&gt;n × 2048&lt;/code&gt; bits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-modular-reduction.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-modular-reduction.svg" alt="Reducing modulo m after every multiplication so intermediate values never grow past m squared" width="1000" height="240"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_power_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;base ** exponent % modulus, reducing after every multiplication.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;                      &lt;span class="c1"&gt;# everything is congruent to 0 mod 1
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
    &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
        &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fast_power_mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_007&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_007&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;9 9
846
719476260 719476260
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three details earn their place. &lt;code&gt;base % modulus&lt;/code&gt; up front handles a base larger than the modulus. The &lt;code&gt;modulus == 1&lt;/code&gt; guard covers the one input the loop gets wrong: every integer is congruent to 0 modulo 1, and an exponent of 0 never enters the loop, so the initial &lt;code&gt;result = 1&lt;/code&gt; would come straight back out. And the reduction happens on both the accumulator and the squaring chain — miss either and the numbers grow without limit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use the built-in
&lt;/h3&gt;

&lt;p&gt;Python has this in the language. &lt;code&gt;pow(base, exponent)&lt;/code&gt; is the same as &lt;code&gt;base ** exponent&lt;/code&gt;, and the three-argument &lt;code&gt;pow(base, exponent, modulus)&lt;/code&gt; is modular exponentiation implemented in C, using a windowed variant of the same square-and-multiply algorithm. Since Python 3.8, a negative exponent with a modulus gives the modular inverse: &lt;code&gt;pow(a, -1, m)&lt;/code&gt; is the value &lt;code&gt;x&lt;/code&gt; with &lt;code&gt;a × x ≡ 1 (mod m)&lt;/code&gt;, and it raises &lt;code&gt;ValueError&lt;/code&gt; when no inverse exists, which happens exactly when &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt; share a factor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;private_exponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;private_exponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;private_exponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;3120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ValueError: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1594323
23
2753 1
ValueError: base is not invertible for the given modulus
2 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;2753&lt;/code&gt; is not a random number: it is the RSA private exponent from the textbook key with primes 61 and 53, where the modulus is 3233 and &lt;code&gt;e&lt;/code&gt; is 17. The last line shows the other route to an inverse — by Fermat's little theorem, &lt;code&gt;a^(p-2) ≡ a^(-1) (mod p)&lt;/code&gt; for prime &lt;code&gt;p&lt;/code&gt;, so &lt;code&gt;pow(7, 11, 13)&lt;/code&gt; and &lt;code&gt;pow(7, -1, 13)&lt;/code&gt; agree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Multiplications: O(log n).&lt;/strong&gt; The loop runs once per bit of &lt;code&gt;n&lt;/code&gt;, and the number of bits is &lt;code&gt;floor(log2(n)) + 1&lt;/code&gt;. Each iteration does at most two multiplications: one squaring, plus one fold into the result when the bit is set. So the total is at most &lt;code&gt;2 × log2(n) + 1&lt;/code&gt;, hit when every bit is set, and never below &lt;code&gt;log2(n)&lt;/code&gt;, the floor reached when &lt;code&gt;n&lt;/code&gt; is a power of two and only one bit is set. The exact count is &lt;code&gt;(bits - 1) + popcount(n)&lt;/code&gt;, and the table above shows it: 10^18 has 60 bits and 24 of them set, giving 59 + 24 = 83.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest caveat: a multiplication is not O(1).&lt;/strong&gt; Big O on multiplication &lt;em&gt;counts&lt;/em&gt; is the right measure only when each multiplication has fixed cost. For &lt;code&gt;pow(a, n, m)&lt;/code&gt; that holds — every operand stays below &lt;code&gt;m&lt;/code&gt;, so each multiplication costs the same, and the total is &lt;code&gt;O(log n)&lt;/code&gt; machine-word multiplications for fixed &lt;code&gt;m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Without a modulus it does not hold at all. &lt;code&gt;a^n&lt;/code&gt; has about &lt;code&gt;n × log2(a)&lt;/code&gt; bits, so the final squaring alone operates on numbers half that size, and CPython multiplies large integers with Karatsuba's algorithm at roughly &lt;code&gt;O(d^1.585)&lt;/code&gt; for &lt;code&gt;d&lt;/code&gt;-digit operands. The cost is then dominated by the last one or two multiplications, and it is governed by the size of the &lt;em&gt;answer&lt;/em&gt; — which no algorithm can beat, because you have to write the answer down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(1) for the iterative version&lt;/strong&gt; — three integers, regardless of &lt;code&gt;n&lt;/code&gt;. The recursive version adds &lt;code&gt;O(log n)&lt;/code&gt; stack frames. With a modulus, every one of those integers stays below &lt;code&gt;m&lt;/code&gt;, so the space is genuinely constant in &lt;code&gt;n&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Ffast-exponentiation-growth.svg" alt="Logarithmic growth against linear growth, the gap that turns a quintillion steps into 83" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Matrix exponentiation, and Fibonacci in O(log n)
&lt;/h2&gt;

&lt;p&gt;Nothing in the algorithm cares that the values are numbers. It needs multiplication, associativity and an identity element — nothing else. Matrices have all three.&lt;/p&gt;

&lt;p&gt;Linear recurrences can be written as matrix powers. The Fibonacci step &lt;code&gt;F(n+1) = F(n) + F(n-1)&lt;/code&gt; is exactly what the matrix &lt;code&gt;[[1, 1], [1, 0]]&lt;/code&gt; does to the vector &lt;code&gt;(F(n), F(n-1))&lt;/code&gt;. Raise that matrix to the &lt;code&gt;n&lt;/code&gt;th power and its top-right entry is &lt;code&gt;F(n)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Multiply two 2x2 integer matrices.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;matrix_power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Matrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same square-and-multiply loop, with matrices instead of numbers.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;        &lt;span class="c1"&gt;# the identity matrix plays the role of 1
&lt;/span&gt;    &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matrix&lt;/span&gt;
    &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exponent&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;matrix_multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;F(index), using O(log index) matrix multiplications.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;matrix_power&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fibonacci&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
2880067194370816120
208988
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop body is identical to &lt;code&gt;fast_power_iterative&lt;/code&gt; with &lt;code&gt;*&lt;/code&gt; replaced by &lt;code&gt;matrix_multiply&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt; replaced by the identity matrix. The last line computes the millionth Fibonacci number — 208,988 digits long — with 26 matrix multiplications, the count the table above gives for an exponent of 10^6. The usual iterative Fibonacci would need a million additions on numbers of that size.&lt;/p&gt;

&lt;p&gt;The same construction handles any recurrence of the form &lt;code&gt;T(n) = c1×T(n-1) + ... + ck×T(n-k)&lt;/code&gt;: build the &lt;code&gt;k&lt;/code&gt;-by-&lt;code&gt;k&lt;/code&gt; companion matrix and raise it to the &lt;code&gt;n&lt;/code&gt;th power in &lt;code&gt;O(k^3 log n)&lt;/code&gt; operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it whenever the exponent is large and the modulus is present.&lt;/strong&gt; That is the entire domain: cryptography, hashing, counting problems modulo a prime like 10^9 + 7, and linear recurrences via matrices. Under a modulus there is no reason ever to write the naive loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not write it yourself in Python.&lt;/strong&gt; &lt;code&gt;pow(base, exponent, modulus)&lt;/code&gt; does the same thing in C, and beats a Python-level loop by a wide margin. The value in the code above is knowing what &lt;code&gt;pow&lt;/code&gt; costs and why, not shipping it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for cryptographic code you deploy.&lt;/strong&gt; The textbook loop branches on the bits of the exponent, so its running time and power draw leak the secret exponent — this is the basis of Paul Kocher's 1996 timing attacks on Diffie-Hellman and RSA. Real implementations use constant-time variants such as the Montgomery ladder; OpenSSL's is &lt;code&gt;BN_mod_exp_mont_consttime&lt;/code&gt;. Use a vetted library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not bother for small fixed exponents.&lt;/strong&gt; &lt;code&gt;x * x * x&lt;/code&gt; beats any general routine for a cube.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it to build an exact huge power you then throw away.&lt;/strong&gt; If you only need the last few digits or the size of the answer, use a modulus or &lt;code&gt;math.log&lt;/code&gt; instead. &lt;code&gt;2 ** 10**9&lt;/code&gt; is a valid Python expression and a bad idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RSA.&lt;/strong&gt; Both encryption and decryption are single modular exponentiations: the ciphertext is &lt;code&gt;m^e mod n&lt;/code&gt; and the plaintext is &lt;code&gt;c^d mod n&lt;/code&gt;. The common public exponent 65537 keeps the encrypting side cheap: it is &lt;code&gt;2^16 + 1&lt;/code&gt; — binary &lt;code&gt;10000000000000001&lt;/code&gt;, two bits set — so encryption costs 16 squarings and one multiply. The private exponent is a full-length 2048-bit number, which is why signing is far slower than verifying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diffie-Hellman and its elliptic-curve variant.&lt;/strong&gt; Finite-field Diffie-Hellman computes &lt;code&gt;g^a mod p&lt;/code&gt; with a secret exponent. The elliptic-curve version, which is what a modern TLS handshake normally negotiates, runs the identical algorithm with point addition in place of multiplication, where it is called double-and-add.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primality testing.&lt;/strong&gt; The Miller-Rabin and Fermat tests are built on computing &lt;code&gt;a^(n-1) mod n&lt;/code&gt; for candidate &lt;code&gt;n&lt;/code&gt;. Every large prime used in cryptography was found by running modular exponentiation a few hundred times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rolling hashes.&lt;/strong&gt; &lt;a href="https://bimalkhatri.com.np/blogs/rabin-karp-string-matching" rel="noopener noreferrer"&gt;Rabin-Karp&lt;/a&gt; and Rabin fingerprints need &lt;code&gt;base^(window - 1) mod m&lt;/code&gt; to drop the outgoing character. With a window of 10,000 that is 21 multiplications instead of nearly 10,000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modular inverses.&lt;/strong&gt; RSA key generation derives the private exponent &lt;code&gt;d&lt;/code&gt; as the inverse of &lt;code&gt;e&lt;/code&gt; modulo &lt;code&gt;(p-1)(q-1)&lt;/code&gt;, which is one &lt;code&gt;pow(e, -1, phi)&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linear recurrences.&lt;/strong&gt; Matrix exponentiation is the standard way to answer "the &lt;code&gt;n&lt;/code&gt;th term of this recurrence, modulo &lt;code&gt;p&lt;/code&gt;, for &lt;code&gt;n&lt;/code&gt; up to 10^18".&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Computing the power first, then the modulus.&lt;/strong&gt; &lt;code&gt;(a ** b) % m&lt;/code&gt; is the single most common bug here. It is mathematically correct and computationally hopeless — Python builds the entire &lt;code&gt;a^b&lt;/code&gt; before reducing. Always &lt;code&gt;pow(a, b, m)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to reduce the squaring chain.&lt;/strong&gt; Reducing &lt;code&gt;result&lt;/code&gt; but not &lt;code&gt;current&lt;/code&gt; keeps the answer correct and lets &lt;code&gt;current&lt;/code&gt; grow to the full unreduced size, which throws away the whole point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Squaring before multiplying into the result.&lt;/strong&gt; Inside the loop, &lt;code&gt;result *= current&lt;/code&gt; must come before &lt;code&gt;current *= current&lt;/code&gt;. Reversed, every set bit contributes one power too many.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Returning 1 for a modulus of 1.&lt;/strong&gt; &lt;code&gt;pow(x, y, 1)&lt;/code&gt; is 0, because every integer is congruent to 0 modulo 1. A hand-written version that starts &lt;code&gt;result = 1&lt;/code&gt; and never multiplies returns 1 instead. Guard it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negative exponents.&lt;/strong&gt; &lt;code&gt;fast_power(2, -3)&lt;/code&gt; never reaches &lt;code&gt;exponent == 0&lt;/code&gt; with the halving rules as written, because &lt;code&gt;-3&lt;/code&gt; is odd and &lt;code&gt;-4 // 2&lt;/code&gt; is &lt;code&gt;-2&lt;/code&gt;, then &lt;code&gt;-1&lt;/code&gt;, then &lt;code&gt;-2&lt;/code&gt; again — it recurses until Python raises &lt;code&gt;RecursionError&lt;/code&gt;. The iterative version fails more quietly: &lt;code&gt;while remaining &amp;gt; 0&lt;/code&gt; never runs, so it returns 1. Either raise for negative exponents or convert them to &lt;code&gt;1 / fast_power(base, -exponent)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;math.pow&lt;/code&gt;.&lt;/strong&gt; It returns a float and loses exactness above &lt;code&gt;2^53&lt;/code&gt;. &lt;code&gt;math.pow(3, 40)&lt;/code&gt; is not an integer answer. Use &lt;code&gt;**&lt;/code&gt; or &lt;code&gt;pow&lt;/code&gt; for integers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Rewrite &lt;code&gt;fast_power&lt;/code&gt; so it raises a &lt;code&gt;ValueError&lt;/code&gt; on a negative exponent, and confirm the error fires.&lt;/li&gt;
&lt;li&gt;Write the top-down version that walks &lt;code&gt;bin(exponent)&lt;/code&gt; from the leading bit, squaring the result each step and multiplying by the base when the bit is 1, then check it agrees with &lt;code&gt;fast_power_iterative&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Compute the last ten digits of &lt;code&gt;2^(10^18)&lt;/code&gt; using a modulus of &lt;code&gt;10^10&lt;/code&gt;, and explain why the answer is even.&lt;/li&gt;
&lt;li&gt;Implement a modular inverse using Fermat's little theorem for a prime modulus, and compare it against &lt;code&gt;pow(a, -1, m)&lt;/code&gt; for every &lt;code&gt;a&lt;/code&gt; from 1 to 100 with modulus 101.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;matrix_power&lt;/code&gt; to compute the &lt;code&gt;n&lt;/code&gt;th term of &lt;code&gt;T(n) = 2×T(n-1) + 3×T(n-2)&lt;/code&gt; with &lt;code&gt;T(0) = 0&lt;/code&gt; and &lt;code&gt;T(1) = 1&lt;/code&gt;, modulo &lt;code&gt;10^9 + 7&lt;/code&gt;, for &lt;code&gt;n&lt;/code&gt; equal to one million.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Fast exponentiation is one identity applied repeatedly: halve the exponent and square, peel a factor off when the exponent is odd. That turns &lt;code&gt;n&lt;/code&gt; multiplications into &lt;code&gt;(bits - 1) + popcount(n)&lt;/code&gt;, which is never more than &lt;code&gt;2 × log2(n) + 1&lt;/code&gt;. Reduce modulo &lt;code&gt;m&lt;/code&gt; after every multiplication and the numbers stay small too, which is what makes public-key cryptography possible at all. In Python you write &lt;code&gt;pow(base, exponent, modulus)&lt;/code&gt; — but knowing what those three arguments cost is the difference between code that returns in a microsecond and code that never returns.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(log n) — n a power of two: log2(n) squarings and one fold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(log n) multiplications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worst case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(log n) multiplications — at most &lt;code&gt;2 × log2(n) + 1&lt;/code&gt;, when every bit is set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) iterative — three integers; O(log n) stack frames if recursive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Exact cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(bits in n − 1)&lt;/code&gt; squarings plus &lt;code&gt;popcount(n)&lt;/code&gt; folds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multiplication cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Constant only under a modulus; without one, big-int multiplication dominates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Integers, or anything with associative multiplication and an identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Generalises to&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Matrices (linear recurrences), polynomials, elliptic-curve points&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The exponent is large and you want it modulo something&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The exponent is small and fixed, or the exact unreduced power is astronomically large&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RSA, Diffie-Hellman, Miller-Rabin primality, rolling hashes, modular inverses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pow(base, exponent, modulus)&lt;/code&gt;, and &lt;code&gt;pow(a, -1, m)&lt;/code&gt; for inverses since 3.8&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/euclidean-algorithm-gcd" rel="noopener noreferrer"&gt;The Euclidean Algorithm&lt;/a&gt; — the other halving-style number-theory algorithm, and the extended version that also computes modular inverses.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/binary-search" rel="noopener noreferrer"&gt;Binary Search&lt;/a&gt; — the same "halve the problem" move applied to a sorted list instead of an exponent.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/sieve-of-eratosthenes" rel="noopener noreferrer"&gt;Sieve of Eratosthenes&lt;/a&gt; — where the primes used as moduli come from.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/rabin-karp-string-matching" rel="noopener noreferrer"&gt;Rabin-Karp String Matching&lt;/a&gt; — rolling hashes, which need a modular power of the base.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — why log n and n are separated by more than most people expect.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Sieve of Eratosthenes in Python: Every Prime Under a Million, Fast</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:38:13 +0000</pubDate>
      <link>https://dev.to/bimal-py/sieve-of-eratosthenes-in-python-every-prime-under-a-million-fast-1931</link>
      <guid>https://dev.to/bimal-py/sieve-of-eratosthenes-in-python-every-prime-under-a-million-fast-1931</guid>
      <description>&lt;p&gt;There are 78,498 prime numbers below one million, and a laptop can produce every one of them in a few milliseconds. It can only do that if you stop asking the obvious question.&lt;/p&gt;

&lt;p&gt;The obvious question is "is this number prime?", asked once per number. Answer it by trial division and listing the primes below a million costs about 68 million modulo operations. The sieve of Eratosthenes asks a different question — "which numbers can I rule out?" — and answers it with 2.1 million writes and not a single division. Same list of primes, thirty-two times less work, and the gap widens with every extra digit.&lt;/p&gt;

&lt;p&gt;Eratosthenes ran the library at Alexandria in the third century BC and is better known for measuring the circumference of the Earth. His method for primes is 2,200 years old and is still what production code uses when it needs every prime up to a bound. What follows proves the two optimisations that most implementations write without justifying, derives the O(n log log n) running time from the sum of the reciprocals of the primes rather than asserting it, and covers the two variants you will actually reach for: a segmented sieve for ranges too large to hold in memory, and a smallest-prime-factor table that turns factorisation into a lookup.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Write out every whole number from 1 to n. Cross out 1 straight away — a prime has exactly two distinct divisors and 1 has one, so it does not qualify.&lt;/p&gt;

&lt;p&gt;Now repeat two steps until you run out of work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the smallest number still standing. It is prime.&lt;/li&gt;
&lt;li&gt;Cross out every multiple of it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 1 is the part that deserves an argument, and the argument is three lines. Suppose &lt;code&gt;k&lt;/code&gt; is the smallest number above 1 that is still standing. Every prime below &lt;code&gt;k&lt;/code&gt; was picked in an earlier round, and each of those rounds crossed out all of its own multiples. So &lt;code&gt;k&lt;/code&gt; is not a multiple of any prime smaller than itself. A composite number always has a prime factor smaller than itself. Therefore &lt;code&gt;k&lt;/code&gt; is not composite.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-multiples-of-two.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-multiples-of-two.svg" alt="The numbers 1 to 30 in a six-column grid with every even number above 2 crossed out" width="1000" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice what step 2 does &lt;em&gt;not&lt;/em&gt; involve. Crossing out the multiples of 7 means visiting 14, 21, 28, 35 — repeated addition with a stride of 7. There is no division anywhere in the algorithm and no test of any individual number. Trial division asks n separate questions, each costing up to the square root of n in divisions; the sieve replaces all of them with a handful of straight walks through an array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take the numbers 1 to 30. Cross out 1, then start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Round 1 — the smallest survivor is 2, so 2 is prime.&lt;/strong&gt; Cross out 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30. That is 14 cells written.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Round 2 — the smallest survivor is now 3.&lt;/strong&gt; Its first multiple worth visiting is 3 × 3 = 9, because 6 already went with the twos. Cross out 9, 12, 15, 18, 21, 24, 27, 30 — 8 cells, and half of them (12, 18, 24, 30) were already crossed out by 2. The sieve does not check first. Reading a cell to find out whether it is already marked costs at least as much as writing it, and writing "already crossed out" again is harmless.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-multiples-of-three.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-multiples-of-three.svg" alt="The same grid after the multiples of 3 are crossed out, starting from 9" width="1000" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Round 3 — the smallest survivor is 5.&lt;/strong&gt; Cross out 25 and 30. Only 25 is new; 10, 15 and 20 went in earlier rounds and 30 went twice already.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Round 4 — the smallest survivor is 7, and this is where you stop.&lt;/strong&gt; 7 × 7 = 49 is already past 30, and every multiple of 7 beyond 7 itself that is 30 or less is 7 times something smaller than 7, so it carries a factor below 7 and went long ago. The same holds for 11, 13 and every later survivor: nothing is left to mark, so everything still standing is prime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-primes-remaining.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-primes-remaining.svg" alt="The finished grid with 25 struck out and the ten primes up to 30 highlighted" width="1000" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ten primes — 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 — for 24 writes and zero divisions.&lt;/p&gt;

&lt;p&gt;The six-column layout is not decorative: every prime past 3 lands in the first or the fifth column, because the other four columns are exactly the numbers divisible by 2 or by 3. Past 5, only 8 of every 30 consecutive numbers can be prime at all — the observation that wheel factorisation uses to shrink a sieve's memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Start with the baseline, so the improvement is measurable rather than asserted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_prime_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;True if number is prime, decided by dividing by every candidate up to its square root.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="c1"&gt;# If number = a * b with a &amp;lt;= b, then a * a &amp;lt;= number, so any divisor above
&lt;/span&gt;    &lt;span class="c1"&gt;# the square root can only appear paired with one below it.
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;primes_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every prime below limit, decided one number at a time.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_prime_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;primes_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;primes_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
168
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is correct and it is the right tool when you have exactly one number to test. As a way to produce a whole list it throws away everything it learns: the moment it discovers that 4 is even, it forgets, and asks the same question again at 6.&lt;/p&gt;

&lt;p&gt;Here is the sieve. &lt;code&gt;limit&lt;/code&gt; is exclusive, matching &lt;code&gt;range&lt;/code&gt;, so &lt;code&gt;sieve_of_eratosthenes(30)&lt;/code&gt; returns the primes below 30.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return every prime strictly below limit, in ascending order.

    Rather than testing numbers, this marks composites: each prime it finds
    strikes off that prime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s own multiples in a single sweep of additions.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;is_prime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="c1"&gt;# Every composite below limit has a prime factor no larger than its own
&lt;/span&gt;    &lt;span class="c1"&gt;# square root, so once candidate squared reaches limit nothing is left to mark.
&lt;/span&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="c1"&gt;# Multiples of candidate below candidate squared already carry a
&lt;/span&gt;            &lt;span class="c1"&gt;# smaller prime factor and were struck off on an earlier sweep.
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
        &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;primes_by_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
[] [2] []
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twenty-two lines, and the last one checks it against the baseline on every number below ten thousand.&lt;/p&gt;

&lt;p&gt;In CPython the version above spends nearly all its time in the interpreter, one bytecode dispatch per write. Because the marking pattern is an arithmetic progression, you can hand the entire inner loop to C with a single slice assignment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sieve_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same sieve with the marking loop handed to C by slice assignment.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x00\x00&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;
            &lt;span class="c1"&gt;# bytearray(k) is k zero bytes, so one assignment clears the whole
&lt;/span&gt;            &lt;span class="c1"&gt;# arithmetic progression without a Python-level loop.
&lt;/span&gt;            &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;


&lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sieve_flags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;999_983&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;999_984&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;78498 1 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical algorithm, identical number of writes, roughly forty times faster on a million-element sieve, and one byte per number instead of the eight a list of pointers costs. Converting the flags back into a list of primes then costs more than the sieve itself — if the question is "is 786,433 prime?", keep the flags and index into them.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The array is the sheet of paper, and the index is the number.&lt;/strong&gt; &lt;code&gt;is_prime[17]&lt;/code&gt; answers a question about 17: no hashing, no search, no comparison, just an offset into a contiguous block of memory. That is the decision the whole algorithm rests on, and it is also where the O(n) space cost comes from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;is_prime[0] = is_prime[1] = False&lt;/code&gt;&lt;/strong&gt; handles the two numbers the loop never visits. Neither is prime and nothing crosses them out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The outer loop stops at the square root.&lt;/strong&gt; Any composite &lt;code&gt;m&lt;/code&gt; can be written as &lt;code&gt;a * b&lt;/code&gt; with &lt;code&gt;a &amp;lt;= b&lt;/code&gt;, which forces &lt;code&gt;a * a &amp;lt;= m&lt;/code&gt;, so &lt;code&gt;m&lt;/code&gt; has a prime factor no larger than the square root of &lt;code&gt;m&lt;/code&gt; — and therefore smaller than the square root of &lt;code&gt;limit&lt;/code&gt;. Some earlier round has already crossed &lt;code&gt;m&lt;/code&gt; out. Past that point every remaining round would sweep an empty range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;if is_prime[candidate]&lt;/code&gt;&lt;/strong&gt; skips composites, and loses nothing by doing so. A composite candidate has a prime factor &lt;code&gt;q&lt;/code&gt; smaller than itself, and every multiple of candidate is also a multiple of &lt;code&gt;q&lt;/code&gt;, so &lt;code&gt;q&lt;/code&gt; already dealt with all of them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The inner loop starts at &lt;code&gt;candidate * candidate&lt;/code&gt;, not &lt;code&gt;2 * candidate&lt;/code&gt;.&lt;/strong&gt; Take any earlier multiple &lt;code&gt;k * candidate&lt;/code&gt; with &lt;code&gt;2 &amp;lt;= k &amp;lt; candidate&lt;/code&gt;. Then &lt;code&gt;k&lt;/code&gt; has a prime factor &lt;code&gt;q&lt;/code&gt; with &lt;code&gt;q &amp;lt;= k &amp;lt; candidate&lt;/code&gt;, so &lt;code&gt;q&lt;/code&gt; ran in an earlier round, marking multiples of &lt;code&gt;q&lt;/code&gt; from &lt;code&gt;q * q&lt;/code&gt; upwards. And &lt;code&gt;k * candidate&lt;/code&gt; is at least &lt;code&gt;q * candidate&lt;/code&gt;, which exceeds &lt;code&gt;q * q&lt;/code&gt; because candidate exceeds &lt;code&gt;q&lt;/code&gt;. So it was already struck off: skipping it is correct, not merely convenient.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-start-at-square.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-start-at-square.svg" alt="The multiples of 5 up to 30, showing that 10, 15 and 20 were already crossed out before the round for 5 begins" width="1000" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The inner loop step is &lt;code&gt;candidate&lt;/code&gt;,&lt;/strong&gt; which keeps the walk sequential in memory and free of arithmetic beyond one addition per cell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases&lt;/strong&gt; fall out of the single guard at the top. Limits of 0, 1 and 2 return an empty list before any array is allocated; a limit of 3 clears the first two flags, never enters the outer loop because 2 × 2 is not below 3, and answers &lt;code&gt;[2]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now count what the two approaches actually do, rather than trusting the description.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Modulo tests trial division performs to list every prime below limit.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;square_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stop_at_root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Cells the sieve writes to, with either optimisation switchable off.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;is_prime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;outer_bound&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stop_at_root&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;outer_bound&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;square_start&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;is_prime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
                &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;writes&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;limit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;modulo tests&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sieve writes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ratio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_trial_division&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;writes&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;6.1&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    limit    modulo tests    sieve writes   ratio
    1,000           5,287           1,409     3.8
   10,000         117,526          16,979     6.9
  100,000       2,745,693         193,076    14.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ratio roughly doubles for every tenfold increase in n, which is what you expect when one side grows like the square root of n and the other barely grows at all. Extend the same measurement to a million and trial division needs 67,740,403 modulo tests against the sieve's 2,122,046 writes — and a modulo is a much more expensive machine instruction than a byte store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Time: O(n log log n), best, average and worst alike.&lt;/strong&gt; There is no input to be lucky or unlucky with: the only parameter is the bound, so a given n always writes to exactly the same cells. The three cases collapse into one count, and here is where that count comes from.&lt;/p&gt;

&lt;p&gt;For each prime &lt;code&gt;p&lt;/code&gt; with &lt;code&gt;p * p &amp;lt; n&lt;/code&gt;, the inner loop writes to the multiples of &lt;code&gt;p&lt;/code&gt; from &lt;code&gt;p * p&lt;/code&gt; up to n. That is &lt;code&gt;(n - p * p) / p + 1&lt;/code&gt; writes, which is a little under &lt;code&gt;n / p&lt;/code&gt;. Add it up over every prime the outer loop reaches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;total writes  ≈  n × (1/2 + 1/3 + 1/5 + 1/7 + ... + 1/p)   for primes p ≤ √n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the entire running time hangs on one quantity: the sum of the reciprocals of the primes up to a bound. That sum diverges, but astonishingly slowly. Mertens proved in 1874 that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1/2 + 1/3 + 1/5 + ... + 1/p  =  ln ln x + M + o(1)     for primes p ≤ x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where M ≈ 0.2615 is the Meissel-Mertens constant. The double logarithm is not a coincidence, and you can see where it comes from without the full proof. The prime number theorem says there are about &lt;code&gt;x / ln x&lt;/code&gt; primes below x, which makes the k-th prime roughly &lt;code&gt;k ln k&lt;/code&gt; in size. Summing reciprocals of the primes is then summing &lt;code&gt;1 / (k ln k)&lt;/code&gt; over k, and that sum behaves like the integral of &lt;code&gt;1 / (k ln k)&lt;/code&gt;, which is &lt;code&gt;ln ln k&lt;/code&gt;. One logarithm comes from the thinning-out of the primes; the other comes from integrating a reciprocal.&lt;/p&gt;

&lt;p&gt;Substituting &lt;code&gt;x = √n&lt;/code&gt; gives &lt;code&gt;ln ln √n = ln(ln n / 2) = ln ln n - ln 2&lt;/code&gt;, so the count of writes is about &lt;code&gt;n × (ln ln n - ln 2 + M)&lt;/code&gt;, and dropping the constants leaves &lt;strong&gt;O(n log log n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is a claim you can check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MEISSEL_MERTENS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2614972128&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;predicted_writes_per_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;(plain n log log n estimate, same estimate corrected for the square start).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;base_primes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Mertens: the sum of 1/p over primes p &amp;lt;= x is ln ln x + M.
&lt;/span&gt;    &lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;MEISSEL_MERTENS&lt;/span&gt;
    &lt;span class="c1"&gt;# The square start skips the first p - 1 multiples of every base prime p.
&lt;/span&gt;    &lt;span class="n"&gt;corrected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_primes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_primes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrected&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;measured&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ln ln n - ln 2 + M&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;corrected&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;measured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;
    &lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;corrected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;predicted_writes_per_number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;measured&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;8.3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;19.3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;corrected&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;9.3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        n  measured   ln ln n - ln 2 + M  corrected
   10,000     1.698                1.789      1.685
  100,000     1.931                2.012      1.920
1,000,000     2.122                2.194      2.118
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last column matches the measurement to within a fifth of a percent at a million. The formula is not an analogy; it is the count.&lt;/p&gt;

&lt;p&gt;It is also worth seeing how small &lt;code&gt;log log n&lt;/code&gt; is. At n = 1,000,000 the sieve does 2.12 writes per number. At n = 1,000,000,000,000 it does about 2.9. For any n you will ever sieve, &lt;code&gt;log log n&lt;/code&gt; sits between 1 and 4 — which is why people describe the sieve as "basically linear" and are not being sloppy when they do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-growth.svg" alt="Growth curves for n, n log n and n squared, with the straight n line highlighted as the one the sieve tracks" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The baseline it beats: O(n √n), or better in practice.&lt;/strong&gt; Trial division tests each of n numbers with up to &lt;code&gt;√n&lt;/code&gt; divisions, giving the plain O(n √n) bound. The measured cost is lower, because composites exit at their first divisor and only the primes pay the full square root — about &lt;code&gt;n / ln n&lt;/code&gt; of them, for a true cost near &lt;code&gt;n^1.5 / ln n&lt;/code&gt;. Either way it is a power of n, and the sieve is a logarithm of a logarithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Neither of the two famous optimisations changes the complexity class.&lt;/strong&gt; They are constant-factor wins, and it is worth knowing how large.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;writes at n = 1,000,000, by which optimisations are switched on&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  both:                 &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  no square start:      &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;square_start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  neither:              &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;writes at n = 1,000,000, by which optimisations are switched on
  both:                 2,122,046
  no square start:      2,197,837
  neither:              2,775,208
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Starting at &lt;code&gt;p * p&lt;/code&gt; instead of &lt;code&gt;2 * p&lt;/code&gt; saves 3.6% of the writes — real, but far less than its fame suggests, and the share shrinks as n grows. Running the outer loop all the way to n while starting at &lt;code&gt;2 * p&lt;/code&gt; costs 31% more, because you are then summing &lt;code&gt;1/p&lt;/code&gt; over every prime below n rather than below &lt;code&gt;√n&lt;/code&gt;, and the difference between those two sums tends to &lt;code&gt;ln 2&lt;/code&gt;. If you keep the square start, stopping at the square root saves no writes at all — the ranges past that point are empty — it saves the loop overhead of visiting the other n cells.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(n).&lt;/strong&gt; One flag per number, whether or not you ever look at it. With a &lt;code&gt;bytearray&lt;/code&gt; that is one byte per number: 1 MB for a million, 1 GB for a billion. A bit array cuts it eightfold and a wheel that skips multiples of 2, 3 and 5 cuts it by another factor of 30/8, but the shape of the cost does not change. This is the sieve's real limitation, and it is the reason the next section exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two variants worth the extra code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sieving a range that will not fit in memory
&lt;/h3&gt;

&lt;p&gt;Suppose you want the primes just above one trillion. A flat sieve would need a trillion cells. But to sieve any block of numbers up to &lt;code&gt;high&lt;/code&gt;, all you need are the primes up to &lt;code&gt;√high&lt;/code&gt; — because that is the largest prime factor a composite in that range can be forced to have. For a trillion that is the 78,498 primes below a million, which fit comfortably in memory.&lt;/p&gt;

&lt;p&gt;So: sieve the small primes once, then sweep the target range one block at a time, marking each block with those same primes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-segmented-blocks.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fsieve-of-eratosthenes-segmented-blocks.svg" alt="The four stages of a segmented sieve, from base primes to the surviving primes in one block" width="1000" height="240"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;segmented_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every prime in the half-open range [low, high), one block at a time.

    Memory holds the primes up to the square root of high plus a single block,
    so high may be far beyond anything a flat sieve could store.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;base_primes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isqrt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;block_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;block_start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;block_stop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;block_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x01&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_stop&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;block_start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;base_primes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# First multiple of prime at or above block_start, but never below
&lt;/span&gt;            &lt;span class="c1"&gt;# prime squared, or a base prime would strike itself out.
&lt;/span&gt;            &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prime&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;block_start&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;block_stop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;block_start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;block_start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;segmented_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;sieve_of_eratosthenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;segmented_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;segmented_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;True
[1000003, 1000033, 1000037, 1000039, 1000081, 1000099]
335
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are 335 primes in the ten thousand numbers starting at a trillion, found without ever allocating more than 65,536 flags plus the base primes. Working memory is O(√high + block size), plus whatever share of the output you keep. The &lt;code&gt;max(prime * prime, ...)&lt;/code&gt; term is the same square-start rule as before, and it is what stops the block containing 2 from crossing 2 out.&lt;/p&gt;

&lt;p&gt;Two costs to be aware of. Every block pays a pass over all 78,498 base primes even when most of them have no multiple inside it, so very small blocks are wasteful — 32 KB to 256 KB is the usual choice, sized to fit the CPU cache, which is also where the speed comes from. And you cannot ask for primes near 10^30 this way: &lt;code&gt;√high&lt;/code&gt; grows too, and the base sieve becomes the bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sieving the smallest prime factor
&lt;/h3&gt;

&lt;p&gt;Change one thing: instead of storing a flag, store the smallest prime that divides each index. The loop is the same shape, plus one condition so that the first prime to reach a cell keeps it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;smallest_prime_factor_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;spf[number] is the smallest prime dividing number, for every number below limit.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# nothing smaller divides it, so it is prime
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# not yet claimed by a smaller prime
&lt;/span&gt;                    &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;
        &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Prime factors of number with multiplicity. Needs 1 &amp;lt;= number &amp;lt; len(spf).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;factors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;prime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;factors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;//=&lt;/span&gt; &lt;span class="n"&gt;prime&lt;/span&gt;  &lt;span class="c1"&gt;# peel off one copy of that prime and continue
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;factors&lt;/span&gt;


&lt;span class="n"&gt;spf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;smallest_prime_factor_sieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999_999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999_983&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;factorise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spf&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[3, 3, 3, 7, 11, 13, 37]
[999983]
[2, 2, 2, 3, 3, 5] []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A cell keeps the first prime that reaches it, and rounds run in increasing order of prime, so that first arrival is the smallest prime factor. For any number above 1, &lt;code&gt;spf[number] == number&lt;/code&gt; is then exactly the test for primality; &lt;code&gt;spf[0]&lt;/code&gt; and &lt;code&gt;spf[1]&lt;/code&gt; are the two cells no round ever claims, so exclude them. That is why the table also tells you 999,983 is prime.&lt;/p&gt;

&lt;p&gt;Factorising afterwards is O(log n): every step divides the number by a prime of at least 2, so it cannot take more than &lt;code&gt;log2(n)&lt;/code&gt; steps — at most 20 for a number below a million. Trial-division factorisation of the same number takes up to &lt;code&gt;√n&lt;/code&gt; divisions, so about a thousand. If you need to factorise many numbers below a fixed bound, this table is the answer, and the same sweep can build Euler's totient, the Möbius function or a divisor count at no extra asymptotic cost.&lt;/p&gt;

&lt;p&gt;The price is memory: a Python list of a million distinct integers is roughly 40 MB, against 1 MB for the &lt;code&gt;bytearray&lt;/code&gt; of flags. &lt;code&gt;array("i", range(limit))&lt;/code&gt; from the standard library brings it down to 4 MB.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it when you need every prime below a fixed bound&lt;/strong&gt;, or a per-number table derived from factorisation, and that bound fits in memory. This is precomputation: pay O(n log log n) once at startup, then answer any number of "is it prime" or "factorise it" questions in constant or logarithmic time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it to test a single number&lt;/strong&gt;, especially a large one. Asking whether one 100-digit number is prime by sieving is not slow, it is impossible — the array would need more cells than there are atoms in the observable universe. The right tool is the Miller-Rabin test, which is a handful of modular exponentiations (see &lt;a href="https://bimalkhatri.com.np/blogs/fast-exponentiation" rel="noopener noreferrer"&gt;fast exponentiation&lt;/a&gt; for the log-time squaring trick it depends on) and is deterministic for every 64-bit input with a fixed set of seven bases. For one modest number, trial division by 2, 3 and then 6k ± 1 is fine and needs no memory at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use a flat sieve for a narrow window far out.&lt;/strong&gt; Primes between 10^12 and 10^12 + 10^4 want the segmented sieve above, which costs O(√high) memory instead of O(high).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be careful with the "linear sieve".&lt;/strong&gt; There is a well-known O(n) variant that marks each composite exactly once by pairing it with its smallest prime factor. The bound is genuinely better, but in practice it is often &lt;em&gt;slower&lt;/em&gt; than the classic sieve, because it accesses memory out of order and loses the sequential cache behaviour that makes the plain version fast. Measure before you adopt it.&lt;/p&gt;

&lt;p&gt;Python's standard library has no prime function at all. &lt;code&gt;math.isqrt&lt;/code&gt; and &lt;code&gt;math.gcd&lt;/code&gt; are there; primality and factorisation are not. If you would rather not maintain this yourself, SymPy provides &lt;code&gt;sympy.sieve&lt;/code&gt;, &lt;code&gt;sympy.primerange&lt;/code&gt;, &lt;code&gt;sympy.isprime&lt;/code&gt; and &lt;code&gt;sympy.factorint&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RSA and Diffie-Hellman key generation.&lt;/strong&gt; Producing a 2048-bit RSA key means finding two 1024-bit primes, and the practical way to find one is to test random odd candidates until one passes. A Miller-Rabin round on a 1024-bit number is a full modular exponentiation — thousands of times more expensive than a small-division check. So implementations sieve first. OpenSSL ships a static table of the first 2,048 primes (the largest is 17,863), and its candidate generator computes the candidate's remainder against every one of them, then walks forward through candidates by updating those remainders — a segmented sieve over the candidate window. By Mertens' third theorem, that table alone eliminates about 88% of odd candidates before a single expensive test runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Competitive programming and Project Euler.&lt;/strong&gt; "Find the sum of all the primes below two million" is Project Euler problem 10, and it is a two-line answer with a sieve, against about a minute of waiting with the trial division above. Contest solutions routinely sieve to 10^6 or 10^7 during setup and then answer thousands of queries by lookup; the smallest-prime-factor table is the standard way to factorise a hundred thousand inputs inside a time limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hash table sizing.&lt;/strong&gt; The GNU C++ standard library picks the bucket count for &lt;code&gt;unordered_map&lt;/code&gt; from a hard-coded list of primes, growing to the next entry when the load factor is exceeded. A prime bucket count keeps a poorly distributed hash function from collapsing onto a few buckets. The list is a compile-time constant rather than something computed at start-up — the kind of one-off table a sieve exists to produce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prime counting and number theory software.&lt;/strong&gt; Kim Walisch's &lt;code&gt;primesieve&lt;/code&gt; — a heavily optimised segmented, wheel-factorised sieve — is the standard tool for generating or counting primes anywhere in the 64-bit range, and is what research code reaches for instead of writing its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Starting the inner loop at &lt;code&gt;2 * candidate&lt;/code&gt; and calling it a bug.&lt;/strong&gt; It is not a bug, it is 3.6% more writes. The actual bug is starting at &lt;code&gt;2 * candidate&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; running the outer loop to n, which costs 31% more and is what most first attempts do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing before writing.&lt;/strong&gt; &lt;code&gt;if is_prime[multiple]: is_prime[multiple] = False&lt;/code&gt; looks like it avoids work. It adds a read to every write and avoids nothing, because the write is idempotent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using a set of composites instead of a flat array.&lt;/strong&gt; A &lt;code&gt;set&lt;/code&gt; turns each mark into a hash computation and a probe into scattered memory, and costs an order of magnitude more space. The sieve is fast precisely because the index is the number and the writes are sequential.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Off-by-one at the limit.&lt;/strong&gt; Decide whether your &lt;code&gt;limit&lt;/code&gt; is inclusive or exclusive and put it in the docstring. &lt;code&gt;range&lt;/code&gt;-style exclusive is the convention above; &lt;code&gt;sieve_of_eratosthenes(30)&lt;/code&gt; therefore does not consider 30 itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting 0 and 1.&lt;/strong&gt; &lt;code&gt;[True] * limit&lt;/code&gt; claims both are prime. Clear them explicitly; nothing in the loop ever visits them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sieving inside a loop.&lt;/strong&gt; Building a fresh sieve every time you need a primality check turns an O(n log log n) precomputation into a per-query cost. Sieve once, keep the array, index into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sum every prime below two million, and check that the total is what you would expect from a table of prime sums.&lt;/li&gt;
&lt;li&gt;Count the twin primes below one million — pairs &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;p + 2&lt;/code&gt; that are both prime.&lt;/li&gt;
&lt;li&gt;Extend the sieve so that it also records, for every number below the limit, how many &lt;em&gt;distinct&lt;/em&gt; prime factors it has.&lt;/li&gt;
&lt;li&gt;Write &lt;code&gt;nth_prime(k)&lt;/code&gt; by sieving up to the bound &lt;code&gt;k * (ln k + ln ln k)&lt;/code&gt;, which is known to exceed the k-th prime for k of at least 6, then indexing the result.&lt;/li&gt;
&lt;li&gt;Use the segmented sieve to find the first gap of at least 100 between consecutive primes, and report where it starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The sieve of Eratosthenes wins by inverting the question. Testing numbers costs a square root per number; ruling out multiples costs one write per composite discovery, and the total number of those writes is n times the sum of the reciprocals of the primes up to &lt;code&gt;√n&lt;/code&gt; — which Mertens tells us is &lt;code&gt;ln ln n - ln 2 + M&lt;/code&gt;, a number between 1 and 4 for every input you will ever use. That is the whole argument, and the measured writes agree with it to a fraction of a percent.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n log log n) in every case — n times the sum of 1/p over primes p up to &lt;code&gt;√n&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — one flag per number; 1 MB per million with a &lt;code&gt;bytearray&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Baseline it replaces&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n √n) trial division — 32 times more work at n = 1,000,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Divisions performed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None — the inner loop only adds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flat array of flags, indexed by the number itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Range variant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Segmented sieve — O(√high) memory, any window up to 64 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Factorisation variant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Smallest-prime-factor table — O(log n) factorisation afterwards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need every prime below a fixed n, or a per-number factor table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Testing one large number — use Miller-Rabin instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Small-prime rejection in RSA key generation, contest precomputation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nothing in the standard library; &lt;code&gt;sympy.sieve&lt;/code&gt; / &lt;code&gt;sympy.primerange&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Write it once, keep the array, and stop asking numbers whether they are prime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/euclidean-algorithm-gcd" rel="noopener noreferrer"&gt;The Euclidean Algorithm&lt;/a&gt; — the other 2,000-year-old algorithm still in daily use, and the one behind RSA key setup.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/fast-exponentiation" rel="noopener noreferrer"&gt;Fast Exponentiation&lt;/a&gt; — modular exponentiation in log n steps, which is what Miller-Rabin runs after the sieve has done the cheap rejections.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/prefix-sums" rel="noopener noreferrer"&gt;Prefix Sums&lt;/a&gt; — the same trade in a different shape: precompute once, answer every query in constant time.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — how to build the counting arguments used above from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/arrays-and-dynamic-arrays" rel="noopener noreferrer"&gt;Arrays and Dynamic Arrays&lt;/a&gt; — why indexing a contiguous block is the fastest lookup there is, which is the sieve's whole advantage.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Euclidean Algorithm in Python: The Oldest Algorithm Still in Daily Use</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:37:41 +0000</pubDate>
      <link>https://dev.to/bimal-py/the-euclidean-algorithm-in-python-the-oldest-algorithm-still-in-daily-use-3p3b</link>
      <guid>https://dev.to/bimal-py/the-euclidean-algorithm-in-python-the-oldest-algorithm-still-in-daily-use-3p3b</guid>
      <description>&lt;p&gt;Euclid wrote this one down around 300 BC, in Book VII of the &lt;em&gt;Elements&lt;/em&gt;. It is still the code that runs when Python reduces a fraction, and still the code that runs when a machine generates an RSA key. No other algorithm in this series is that old and that current at the same time.&lt;/p&gt;

&lt;p&gt;The problem it solves is small: given two whole numbers, find the largest number that divides both of them exactly. The greatest common divisor of 48 and 18 is 6. The obvious approach is to try every candidate from the smaller number downwards until one divides both, which for two eighteen-digit numbers means up to a quintillion tests. Euclid's method answers the same question in at most 90 divisions, and usually far fewer.&lt;/p&gt;

&lt;p&gt;That gap — from a quintillion to under a hundred — comes from a single observation about divisors that takes two lines to prove. Once you have it, least common multiples, modular inverses and RSA private keys all fall out of the same loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;common divisor&lt;/strong&gt; of &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; is a number that divides both with no remainder. The common divisors of 48 and 18 are 1, 2, 3 and 6, so the &lt;em&gt;greatest&lt;/em&gt; common divisor — the gcd — is 6.&lt;/p&gt;

&lt;p&gt;Euclid's insight is that you can shrink the problem without changing the answer.&lt;/p&gt;

&lt;p&gt;Take two positive numbers with &lt;code&gt;a&lt;/code&gt; larger than &lt;code&gt;b&lt;/code&gt;, and suppose some number &lt;code&gt;d&lt;/code&gt; divides both. Then &lt;code&gt;d&lt;/code&gt; divides their difference &lt;code&gt;a - b&lt;/code&gt; too — a whole number of &lt;code&gt;d&lt;/code&gt;s minus a whole number of &lt;code&gt;d&lt;/code&gt;s is still a whole number of &lt;code&gt;d&lt;/code&gt;s. The argument runs backwards just as easily: anything dividing &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;a - b&lt;/code&gt; divides their sum, which is &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the pair &lt;code&gt;(a, b)&lt;/code&gt; and the pair &lt;code&gt;(a - b, b)&lt;/code&gt; have &lt;strong&gt;exactly the same set of common divisors&lt;/strong&gt;. Not a similar set — the same one. Identical sets have identical largest members:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcd(a, b) = gcd(a - b, b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is the whole algorithm. Keep replacing the larger number with the difference. The numbers shrink, the answer never moves, and when the two finally become equal you are looking at the gcd, because the greatest divisor a number shares with itself is the number.&lt;/p&gt;

&lt;h3&gt;
  
  
  From subtracting to dividing
&lt;/h3&gt;

&lt;p&gt;Repeated subtraction is correct but wasteful: the gcd of 1,000,000 and 2 costs half a million identical subtractions.&lt;/p&gt;

&lt;p&gt;Look at what those subtractions achieve. They strip out copies of &lt;code&gt;b&lt;/code&gt; until what is left is smaller than &lt;code&gt;b&lt;/code&gt; — which is division with remainder, spelled slowly. Doing it in one step gives Euclid's real rule:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gcd(a, b) = gcd(b, a mod b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where &lt;code&gt;a mod b&lt;/code&gt; is the remainder after dividing &lt;code&gt;a&lt;/code&gt; by &lt;code&gt;b&lt;/code&gt;. When the remainder hits zero, &lt;code&gt;b&lt;/code&gt; divides &lt;code&gt;a&lt;/code&gt; exactly, and the gcd of the pair is &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-idea-loop.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-idea-loop.svg" alt="The Euclidean loop: replace a and b with b and the remainder, and stop when the remainder is zero" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two properties make this a real algorithm rather than a nice identity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It is correct at every step.&lt;/strong&gt; Write &lt;code&gt;a = q * b + r&lt;/code&gt;. The same divisor argument still applies, with &lt;code&gt;q&lt;/code&gt; copies of &lt;code&gt;b&lt;/code&gt; removed instead of one: &lt;code&gt;r = a - q * b&lt;/code&gt; is a difference of multiples of any common divisor &lt;code&gt;d&lt;/code&gt;, and &lt;code&gt;q * b + r = a&lt;/code&gt; is a sum of them. Same set of common divisors, same greatest member.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It always terminates.&lt;/strong&gt; The remainder is never negative and is always strictly smaller than &lt;code&gt;b&lt;/code&gt;, so the second number of the pair strictly decreases every step and is bounded below by zero. A strictly decreasing sequence of non-negative integers has to reach 0. It cannot take longer than &lt;code&gt;b&lt;/code&gt; steps; it will turn out to take dramatically fewer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Start with the subtraction form on 48 and 18, since it is the version you can follow without doing any arithmetic in your head.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;48&lt;/code&gt; and &lt;code&gt;18&lt;/code&gt; — 48 is larger, so replace it: &lt;code&gt;48 - 18 = 30&lt;/code&gt;. Now &lt;code&gt;(30, 18)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;30&lt;/code&gt; and &lt;code&gt;18&lt;/code&gt; — replace again: &lt;code&gt;30 - 18 = 12&lt;/code&gt;. Now &lt;code&gt;(12, 18)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;12&lt;/code&gt; and &lt;code&gt;18&lt;/code&gt; — now 18 is the larger one: &lt;code&gt;18 - 12 = 6&lt;/code&gt;. Now &lt;code&gt;(12, 6)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;12&lt;/code&gt; and &lt;code&gt;6&lt;/code&gt; — &lt;code&gt;12 - 6 = 6&lt;/code&gt;. Now &lt;code&gt;(6, 6)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The two are equal. The answer is &lt;code&gt;6&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-subtraction-trace.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-subtraction-trace.svg" alt="Four subtraction steps taking the pair 48 and 18 down to 6 and 6" width="1000" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Four subtractions. Every intermediate pair — &lt;code&gt;(30, 18)&lt;/code&gt;, &lt;code&gt;(12, 18)&lt;/code&gt;, &lt;code&gt;(12, 6)&lt;/code&gt;, &lt;code&gt;(6, 6)&lt;/code&gt; — has the same common divisors as the original: 1, 2, 3 and 6.&lt;/p&gt;

&lt;p&gt;Now the division form on a harder pair, 1071 and 462. By subtraction this takes 11 steps. By division it takes three:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;1071 = 2 * 462 + 147&lt;/code&gt;, so the remainder is &lt;code&gt;147&lt;/code&gt;. The pair becomes &lt;code&gt;(462, 147)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;462 = 3 * 147 + 21&lt;/code&gt;, remainder &lt;code&gt;21&lt;/code&gt;. The pair becomes &lt;code&gt;(147, 21)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;147 = 7 * 21 + 0&lt;/code&gt;, remainder &lt;code&gt;0&lt;/code&gt;. Done — the gcd is &lt;code&gt;21&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-division-trace.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-division-trace.svg" alt="A table of the three division steps reducing 1071 and 462 to a remainder of zero" width="1000" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the shift that happens between rows: the old &lt;code&gt;b&lt;/code&gt; becomes the new &lt;code&gt;a&lt;/code&gt;, and the remainder becomes the new &lt;code&gt;b&lt;/code&gt;. That single move is the entire loop body. And notice the last non-zero remainder, 21 — that is always the answer. Check it: 1071 = 21 × 51 and 462 = 21 × 22, and 51 and 22 share nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Here is the subtraction version first, because it maps one-to-one onto the argument above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gcd_subtraction&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Greatest common divisor by repeated subtraction.

    Both inputs must be positive integers. Replacing the larger number with
    the difference leaves the set of common divisors untouched, so the answer
    never changes; the two numbers just get smaller until they meet.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&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="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd_subtraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd_subtraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6
21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Correct, but it is only ever a teaching version — feed it 1,000,000 and 2 and it performs 499,999 subtractions, and feed it a zero and it never returns at all.&lt;/p&gt;

&lt;p&gt;The division form fixes both problems and is shorter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gcd&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Greatest common divisor by Euclid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s division rule.

    Repeatedly replaces the pair (a, b) with (b, a mod b). The second value
    strictly shrinks every step, so it reaches 0, and the first value is the
    answer at that point. gcd(0, 0) is 0 by convention, matching math.gcd.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&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="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;b&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gcd_recursive&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same rule written as its own definition: gcd(a, b) = gcd(b, a mod b).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;abs&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;gcd_recursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;gcd_recursive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;270&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;192&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;21 21
6 6
5 5 0
6 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both versions are five lines of real work. The recursive one is the identity &lt;code&gt;gcd(a, b) = gcd(b, a mod b)&lt;/code&gt; typed out verbatim, which makes it the better one to read; the iterative one is the better one to ship, since it uses no stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;while b:&lt;/code&gt;&lt;/strong&gt; is the termination test. In Python an integer is falsy only when it is 0, so this loop runs until the remainder reaches zero — exactly the stopping condition from the proof. When it exits, &lt;code&gt;b&lt;/code&gt; is 0 and the answer sits in &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;a, b = b, a % b&lt;/code&gt;&lt;/strong&gt; is the whole algorithm in one line. The right-hand side is fully evaluated before either assignment happens, so &lt;code&gt;a % b&lt;/code&gt; still uses the old &lt;code&gt;a&lt;/code&gt;. Split it into two statements and you destroy &lt;code&gt;a&lt;/code&gt; before computing the remainder — that is the most common way this function gets broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The order of the arguments does not matter.&lt;/strong&gt; If you call &lt;code&gt;gcd(18, 48)&lt;/code&gt;, the first iteration computes &lt;code&gt;18 mod 48&lt;/code&gt;, which is 18, so the pair becomes &lt;code&gt;(48, 18)&lt;/code&gt; and the algorithm carries on normally. Passing them the wrong way round costs exactly one extra division, never a wrong answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;abs&lt;/code&gt; on the way in&lt;/strong&gt; handles negative inputs. Divisors do not care about sign — 6 divides both 48 and −48 — so the gcd is conventionally non-negative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero needs no special case.&lt;/strong&gt; &lt;code&gt;gcd(5, 0)&lt;/code&gt; skips the loop and returns 5, which is right: every number divides 0, so the greatest divisor shared by 5 and 0 is 5. &lt;code&gt;gcd(0, 5)&lt;/code&gt; takes one iteration to swap into the same state. &lt;code&gt;gcd(0, 0)&lt;/code&gt; returns 0, the convention &lt;code&gt;math.gcd&lt;/code&gt; also uses.&lt;/p&gt;

&lt;p&gt;Now count the work instead of trusting the claim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_subtraction_steps&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;How many subtractions the naive form performs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&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="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_division_steps&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;How many divisions the modulo form performs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;b&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="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
        &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pair&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;subtractions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;divisions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcd(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="nf"&gt;count_subtraction_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count_division_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pair                  subtractions  divisions
gcd(48, 18)                      4          3
gcd(1071, 462)                  11          3
gcd(1000000, 2)             499999          1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last row is the argument for division in one line: 499,999 steps against one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Time: O(log min(a, b)).&lt;/strong&gt; Here is where the logarithm comes from.&lt;/p&gt;

&lt;p&gt;Claim: when &lt;code&gt;a&lt;/code&gt; is at least &lt;code&gt;b&lt;/code&gt;, the remainder &lt;code&gt;a mod b&lt;/code&gt; is smaller than &lt;code&gt;a / 2&lt;/code&gt;. Two cases, and they cover everything. If &lt;code&gt;b&lt;/code&gt; is at most &lt;code&gt;a / 2&lt;/code&gt;, then the remainder is smaller than &lt;code&gt;b&lt;/code&gt;, which is at most &lt;code&gt;a / 2&lt;/code&gt;. If &lt;code&gt;b&lt;/code&gt; is bigger than &lt;code&gt;a / 2&lt;/code&gt;, then &lt;code&gt;b&lt;/code&gt; fits into &lt;code&gt;a&lt;/code&gt; exactly once, so the remainder is &lt;code&gt;a - b&lt;/code&gt;, which is again smaller than &lt;code&gt;a / 2&lt;/code&gt;. Either way the remainder loses at least half. (The loop guarantees the precondition: from the first division onwards, the new first number is the old second one, which is the larger of the pair.)&lt;/p&gt;

&lt;p&gt;Now follow the pair through two steps. Starting from &lt;code&gt;(a, b)&lt;/code&gt; you get &lt;code&gt;(b, a mod b)&lt;/code&gt;, then &lt;code&gt;(a mod b, ...)&lt;/code&gt;. So after two steps the first slot holds a value smaller than half of what it held before. A quantity that halves every two steps reaches 1 after about &lt;code&gt;2 * log2(a)&lt;/code&gt; steps, and the loop stops there. Within a division or two both numbers are at most the smaller input — straight away if you passed the larger one first, after one extra swap if you did not — so the bound is &lt;strong&gt;O(log min(a, b))&lt;/strong&gt; divisions.&lt;/p&gt;

&lt;p&gt;Concretely: an eighteen-digit number is about 60 bits, so this argument caps the run at roughly 120 divisions — against 10¹⁸ trial divisions for the naive search. The true bound is sharper still, and it has a name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The worst case is consecutive Fibonacci numbers&lt;/strong&gt;, and this is not a curiosity — it is exactly the input that makes every quotient as small as possible. Each Fibonacci number is the sum of the two before it, so dividing one by its predecessor gives a quotient of 1 and a remainder equal to the one before &lt;em&gt;that&lt;/em&gt;. Quotient 1 is the least progress a division step can make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pair&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;divisions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
    &lt;span class="n"&gt;pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcd(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="nf"&gt;count_division_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;worst_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worst_pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;larger&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;smaller&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;larger&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_division_steps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;larger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smaller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;worst_steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;worst_steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worst_pair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;larger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;smaller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;worst pair below 1000: gcd&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;worst_pair&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; takes &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;worst_steps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; divisions&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pair             divisions
gcd(2, 1)                1
gcd(3, 2)                2
gcd(5, 3)                3
gcd(8, 5)                4
gcd(13, 8)               5
gcd(21, 13)              6
gcd(34, 21)              7
gcd(55, 34)              8
worst pair below 1000: gcd(987, 610) takes 14 divisions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each rung of the Fibonacci ladder costs exactly one more division than the last, and the brute-force search over every pair below 1,000 lands on 987 and 610 — Fibonacci numbers again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-fibonacci-worst-case.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-fibonacci-worst-case.svg" alt="Two remainder chains: 12 and 8 collapse in two divisions, 13 and 8 crawl down in five" width="1000" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what &lt;strong&gt;Lamé's theorem&lt;/strong&gt; (1844) formalises: if the algorithm needs &lt;code&gt;n&lt;/code&gt; division steps on a pair whose smaller member is &lt;code&gt;b&lt;/code&gt;, then &lt;code&gt;b&lt;/code&gt; is at least the (n+1)-th Fibonacci number. Turned around, &lt;code&gt;n&lt;/code&gt; is never more than five times the number of decimal digits of &lt;code&gt;b&lt;/code&gt; — so an eighteen-digit pair takes at most 90 divisions, tightening the 120 the halving argument gave. The 14-step run above therefore needed a &lt;code&gt;b&lt;/code&gt; of at least 610, which is exactly what the search found. Lamé's proof is generally counted as the first complexity analysis of an algorithm in history, written a century before there was a computer to run one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-growth.svg" alt="Logarithmic growth against linear growth as the inputs get larger" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best case is O(1).&lt;/strong&gt; When &lt;code&gt;b&lt;/code&gt; already divides &lt;code&gt;a&lt;/code&gt;, the first division leaves a remainder of 0 and the loop stops: &lt;code&gt;gcd(48, 24)&lt;/code&gt; costs one division, and &lt;code&gt;gcd(48, 0)&lt;/code&gt; costs none at all. Nothing can be cheaper than that, and it is a reachable case, not a theoretical one: any pair where one number is a multiple of the other lands on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The average case is logarithmic too, with a much smaller constant than the worst case.&lt;/strong&gt; Count the divisions over random pairs and it works out at roughly two per decimal digit of the smaller number — about 35 divisions for an eighteen-digit pair, against the 90 Lamé permits. The halving argument still caps every single input, so no choice of inputs can push the average above &lt;strong&gt;O(log min(a, b))&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(1)&lt;/strong&gt; for the iterative version — two integers and a temporary. The recursive version uses one stack frame per division, so &lt;strong&gt;O(log min(a, b))&lt;/strong&gt; frames; for any pair of 64-bit integers that is under 100 frames, comfortably inside Python's default recursion limit of 1,000.&lt;/p&gt;

&lt;p&gt;One honest caveat: counting divisions assumes a division is one operation, which holds only while the numbers fit in a machine word. RSA keys are 2048 bits and up. Counting bit operations instead, the whole run costs about O(k²) for k-bit inputs with schoolbook division — practical, but the reason production libraries use the refinements below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things you get for free
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Least common multiple
&lt;/h3&gt;

&lt;p&gt;The lowest common multiple of 4 and 6 is 12. There is no separate algorithm for it, because gcd and lcm are two halves of one fact:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a * b = gcd(a, b) * lcm(a, b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The reason is visible in the prime factorisations. For each prime, the gcd takes the smaller exponent and the lcm takes the larger. The smaller plus the larger is the sum, and the sum is what the product &lt;code&gt;a * b&lt;/code&gt; has. So the lcm is the product divided by the gcd.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lcm&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Lowest common multiple, derived from the gcd.

    Divides before multiplying so the intermediate value never exceeds the
    answer, which matters in every language with fixed-width integers.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nf"&gt;gcd&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="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lcm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;lcm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;lcm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lcm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;12 42 23562
0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write it as &lt;code&gt;a // gcd(a, b) * b&lt;/code&gt;, not &lt;code&gt;a * b // gcd(a, b)&lt;/code&gt;. The division is exact either way, but dividing first keeps the intermediate value no larger than the answer. In Python that is a habit; in C or Java it is the difference between a correct result and a wrapped one. The zero guard matters too — without it, &lt;code&gt;lcm(0, 0)&lt;/code&gt; divides by a gcd of zero.&lt;/p&gt;

&lt;h3&gt;
  
  
  The extended Euclidean algorithm
&lt;/h3&gt;

&lt;p&gt;Bézout's identity says something stronger than "the gcd exists": for any &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; there are integers &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; with&lt;/p&gt;

&lt;p&gt;&lt;code&gt;a * x + b * y = gcd(a, b)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Those coefficients are already hiding in the division trace — you just have to read it backwards. From the run on 1071 and 462:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;21 = 462 - 3 * 147&lt;/code&gt; (the second division, rearranged)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;147 = 1071 - 2 * 462&lt;/code&gt; (the first division, rearranged)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Substitute the second into the first: &lt;code&gt;21 = 462 - 3 * (1071 - 2 * 462) = 7 * 462 - 3 * 1071&lt;/code&gt;. So &lt;code&gt;x = -3&lt;/code&gt; and &lt;code&gt;y = 7&lt;/code&gt;. Multiply it out if you like: −3,213 + 3,234 = 21.&lt;/p&gt;

&lt;p&gt;The recursive implementation does that substitution automatically. Each call gets its child's coefficients and rewrites them in terms of its own two numbers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;extended_gcd&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="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return (g, x, y) such that a * x + b * y == g == gcd(a, b).

    Both inputs must be non-negative. There is no abs() on the way in here,
    unlike gcd above, because x and y have to match the numbers you passed;
    a negative input comes back with a negative g.

    The base case is free: gcd(a, 0) is a, and a * 1 + 0 * 0 == a. Every
    other level rewrites its child&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s answer in terms of its own two numbers.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extended_gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# The child solved b * x + (a mod b) * y == g. Substituting
&lt;/span&gt;    &lt;span class="c1"&gt;# a mod b == a - (a // b) * b and collecting terms gives the line below.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&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="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;


&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extended_gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcd = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, x = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, y = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1071 * (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) + 462 * &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&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="mi"&gt;462&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcd = 21, x = -3, y = 7
1071 * (-3) + 462 * 7 = 21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-extended-calls.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Feuclidean-algorithm-gcd-extended-calls.svg" alt="The recursion for 1071 and 462 descending to the base case and returning coefficients back up" width="1000" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The recursion descends exactly as far as the plain version does — same divisions, same bound, three extra arithmetic operations per level: the floor division, one multiplication and one subtraction. Extended Euclid is O(log min(a, b)) too, and because it is recursive it also carries the O(log min(a, b)) stack frames.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modular inverses, and why RSA needs them
&lt;/h3&gt;

&lt;p&gt;This is the payoff. In ordinary arithmetic the inverse of 17 is 1/17. Modular arithmetic has no fractions, so the inverse of &lt;code&gt;a&lt;/code&gt; modulo &lt;code&gt;m&lt;/code&gt; is the whole number &lt;code&gt;x&lt;/code&gt; with &lt;code&gt;a * x&lt;/code&gt; leaving a remainder of 1 when divided by &lt;code&gt;m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Such an &lt;code&gt;x&lt;/code&gt; exists if and only if &lt;code&gt;gcd(a, m) = 1&lt;/code&gt;, and the reason is Bézout. If the gcd is 1, then &lt;code&gt;a * x + m * y = 1&lt;/code&gt; for some &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;m * y&lt;/code&gt; vanishes modulo &lt;code&gt;m&lt;/code&gt; — so &lt;code&gt;a * x&lt;/code&gt; leaves remainder 1. If the gcd is bigger than 1, no multiple of &lt;code&gt;a&lt;/code&gt; can ever land on 1, because everything in sight is divisible by that common factor.&lt;/p&gt;

&lt;p&gt;So the extended algorithm &lt;em&gt;is&lt;/em&gt; the modular inverse algorithm. Take &lt;code&gt;x&lt;/code&gt;, reduce it into range, done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;modular_inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The number that multiplies `value` back to 1, working modulo `modulus`.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;extended_gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has no inverse modulo &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&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;modulus&lt;/span&gt;


&lt;span class="n"&gt;public_exponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;
&lt;span class="n"&gt;totient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3120&lt;/span&gt;  &lt;span class="c1"&gt;# (61 - 1) * (53 - 1) for the textbook RSA key with n = 3233
&lt;/span&gt;&lt;span class="n"&gt;private_exponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;modular_inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_exponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;totient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;private exponent d = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;private_exponent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;17 * &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;private_exponent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; mod 3120 = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;public_exponent&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;private_exponent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;totient&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;modular_inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;ValueError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private exponent d = 2753
17 * 2753 mod 3120 = 1
6 has no inverse modulo 9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those numbers are the textbook RSA key: primes 61 and 53, modulus 3,233, public exponent 17. The private exponent 2,753 is not chosen or searched for — it is the modular inverse of 17, produced by extended Euclid in five divisions. Generating an RSA key pair means picking a public exponent coprime to the totient (a gcd test) and then inverting it (extended Euclid). Both halves are this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it whenever you need a gcd, an lcm, or a modular inverse&lt;/strong&gt; — in practice, fractions, ratios, cycle lengths and any modular arithmetic. It is one of the few algorithms with no real competition in its weight class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But in Python, call the standard library.&lt;/strong&gt; &lt;code&gt;math.gcd&lt;/code&gt; is written in C, accepts any number of arguments since 3.9, and for large integers CPython switches to Lehmer's algorithm — a refinement that handles several quotients at a time from the leading digits alone, cutting the number of full-width divisions sharply. &lt;code&gt;math.lcm&lt;/code&gt; arrived in 3.9, and since 3.8 &lt;code&gt;pow(a, -1, m)&lt;/code&gt; gives you a modular inverse directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fractions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Fraction&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lcm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Fraction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1071&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;462&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3120&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;1920&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1080&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2560&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1366&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gcd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;divisor&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;21 60 6
51/22
2753
1920x1080 -&amp;gt; 16:9
2560x1440 -&amp;gt; 16:9
1366x768 -&amp;gt; 683:384
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Write it yourself&lt;/strong&gt; when you need the Bézout coefficients — &lt;code&gt;pow(a, -1, m)&lt;/code&gt; hands back the inverse but throws away &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;, which you need for solving equations of the form &lt;code&gt;a * x + b * y = c&lt;/code&gt;. Or in a language with no gcd in its standard library. Or when you are learning, which is the best reason of all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use the subtraction form.&lt;/strong&gt; Its only defence is hardware where division is unavailable or ruinously expensive, and even there the right answer is binary GCD (Stein's algorithm), which replaces division with subtraction, halving and parity tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not expect it to factorise anything.&lt;/strong&gt; The gcd tells you what two numbers share, not what either one is made of. Finding the prime factors of a single number is a vastly harder problem — the one RSA's security rests on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Python's &lt;code&gt;fractions.Fraction&lt;/code&gt;&lt;/strong&gt; normalises every fraction on construction by dividing numerator and denominator by their gcd. That is why &lt;code&gt;Fraction(1071, 462)&lt;/code&gt; printed &lt;code&gt;51/22&lt;/code&gt; above. CPython's &lt;code&gt;statistics&lt;/code&gt; module adds its partial sums as &lt;code&gt;Fraction&lt;/code&gt; objects to stay exact, so gcds run on the way to a plain &lt;code&gt;mean&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RSA key generation.&lt;/strong&gt; Choosing a public exponent means checking that it is coprime to &lt;code&gt;(p - 1) * (q - 1)&lt;/code&gt;, which is a gcd test, and the private exponent is that public exponent's modular inverse, which is extended Euclid. Both halves of generating a key pair are this algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;pow(a, -1, m)&lt;/code&gt; in CPython&lt;/strong&gt; is an extended-Euclid implementation. The same algorithm produces the inverse of &lt;code&gt;q&lt;/code&gt; modulo &lt;code&gt;p&lt;/code&gt; that a PKCS#1 RSA private key stores next to the two primes — the coefficient behind the Chinese remainder theorem shortcut that makes RSA decryption several times faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aspect ratios.&lt;/strong&gt; Dividing a screen's width and height by their gcd is how 1920×1080 becomes 16:9. It also exposes the odd ones: 1366×768 reduces no further than 683:384, because 683 is prime. A true 16:9 panel 768 pixels tall would be 1365.33 pixels wide, so that laptop resolution is famously &lt;em&gt;almost&lt;/em&gt; 16:9 and never exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anything with repeating cycles.&lt;/strong&gt; Two signals with periods 12 and 18 realign after lcm(12, 18) = 36 units. Gear ratios, polyrhythms in music, and traffic-light cycle planning are the same computation with different units.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Passing zero to the subtraction version.&lt;/strong&gt; With &lt;code&gt;a = 0&lt;/code&gt; and &lt;code&gt;b = 5&lt;/code&gt;, the two are never equal, and &lt;code&gt;b -= a&lt;/code&gt; subtracts nothing forever. The loop hangs. The division form has no such failure mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Returning the wrong variable.&lt;/strong&gt; After &lt;code&gt;while b:&lt;/code&gt; ends, &lt;code&gt;b&lt;/code&gt; is 0 by definition. The answer is &lt;code&gt;a&lt;/code&gt;. Returning &lt;code&gt;b&lt;/code&gt; gives you a very confident zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Splitting the swap.&lt;/strong&gt; Writing &lt;code&gt;a = b&lt;/code&gt; followed by &lt;code&gt;b = a % b&lt;/code&gt; computes the remainder of &lt;code&gt;b&lt;/code&gt; divided by &lt;code&gt;b&lt;/code&gt;, which is 0, so the function returns after one step with the wrong answer. Keep it as a single tuple assignment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Porting the modulo naively.&lt;/strong&gt; Python's &lt;code&gt;%&lt;/code&gt; returns a non-negative result when the divisor is positive; C, Java, Go and JavaScript take the sign of the dividend instead, so &lt;code&gt;-48 % 18&lt;/code&gt; is −12 in those languages and 6 in Python. A direct port can return a negative gcd, and the subtraction form can loop forever. Take absolute values on entry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to reduce the Bézout coefficient.&lt;/strong&gt; The &lt;code&gt;x&lt;/code&gt; that comes back is often negative — for 17 modulo 3120 it is −367. A modular inverse must be reported in range, so return &lt;code&gt;x % modulus&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming an inverse always exists.&lt;/strong&gt; It exists only when the gcd is 1. Check the returned &lt;code&gt;g&lt;/code&gt; before using &lt;code&gt;x&lt;/code&gt;, as &lt;code&gt;modular_inverse&lt;/code&gt; does above; skipping the check gives you a number that quietly fails to invert anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write &lt;code&gt;gcd_of_list&lt;/code&gt; that returns the gcd of any number of integers, using &lt;code&gt;functools.reduce&lt;/code&gt; over the two-argument version.&lt;/li&gt;
&lt;li&gt;Reduce a fraction to lowest terms without using &lt;code&gt;fractions&lt;/code&gt;, making sure a negative denominator moves its sign to the numerator.&lt;/li&gt;
&lt;li&gt;Count the divisions for every pair below 10,000 and confirm that the worst pair is again two consecutive Fibonacci numbers.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;extended_gcd&lt;/code&gt; to solve &lt;code&gt;a * x + b * y = c&lt;/code&gt; for given &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;c&lt;/code&gt;, reporting that no solution exists when the gcd does not divide &lt;code&gt;c&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Implement binary GCD (Stein's algorithm) using only subtraction, halving and even/odd tests, and compare its step count against the division form on a few large pairs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The Euclidean algorithm is the best return on five lines of code in all of computing. One observation — that &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; share their divisors with &lt;code&gt;b&lt;/code&gt; and &lt;code&gt;a mod b&lt;/code&gt; — turns a search over a quintillion candidates into fewer than a hundred divisions, and the extended version throws in Bézout coefficients and modular inverses at no extra asymptotic cost. Twenty-three centuries later, nothing has replaced it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) — &lt;code&gt;b&lt;/code&gt; already divides &lt;code&gt;a&lt;/code&gt;, so one division ends it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(log min(a, b)) — the remainder at least halves every two steps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worst case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(log min(a, b)) — consecutive Fibonacci numbers, every quotient 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) iterative; O(log min(a, b)) stack frames if recursive&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Two integers, nothing else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Handles negatives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes, with &lt;code&gt;abs&lt;/code&gt; on entry; the gcd is taken as non-negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extended version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same bound, returns &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; with &lt;code&gt;a * x + b * y = gcd(a, b)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need a gcd, an lcm, a reduced fraction or a modular inverse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python has it already — and never use the subtraction form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;RSA key generation, &lt;code&gt;Fraction&lt;/code&gt; normalisation, aspect ratios, cycle lengths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;math.gcd(a, b)&lt;/code&gt;, &lt;code&gt;math.lcm(a, b)&lt;/code&gt;, &lt;code&gt;pow(a, -1, m)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/fast-exponentiation" rel="noopener noreferrer"&gt;Fast Exponentiation&lt;/a&gt; — the other half of modular arithmetic, and how RSA actually encrypts once it has these keys.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/sieve-of-eratosthenes" rel="noopener noreferrer"&gt;Sieve of Eratosthenes&lt;/a&gt; — the other ancient algorithm still in daily use, and the fastest way to list every prime below a limit.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/binary-search" rel="noopener noreferrer"&gt;Binary Search&lt;/a&gt; — the same logarithmic payoff from the same trick of discarding half the problem each step.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the full treatment of the counting arguments used above.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/recursion-and-backtracking" rel="noopener noreferrer"&gt;Recursion and Backtracking&lt;/a&gt; — why the recursive form is safe here and dangerous elsewhere.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Rabin-Karp in Python: Finding Substrings With Rolling Hashes</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:37:34 +0000</pubDate>
      <link>https://dev.to/bimal-py/rabin-karp-in-python-finding-substrings-with-rolling-hashes-174m</link>
      <guid>https://dev.to/bimal-py/rabin-karp-in-python-finding-substrings-with-rolling-hashes-174m</guid>
      <description>&lt;p&gt;Substring search done the obvious way re-reads the pattern at every position in the text. Rabin-Karp does something stranger: it turns every window of the text into a single number and compares numbers instead of strings. Comparing two integers costs the same whether the pattern is 3 characters or 300.&lt;/p&gt;

&lt;p&gt;That trade only pays off because of one arithmetic trick. Once you know the number for the window starting at position &lt;code&gt;i&lt;/code&gt;, you can get the number for the window starting at &lt;code&gt;i + 1&lt;/code&gt; in constant time — subtract the leading character's contribution, shift, add the new trailing character. That is the rolling hash, and it is the entire algorithm.&lt;/p&gt;

&lt;p&gt;Be clear about where it is genuinely useful, though. For finding one string inside another in Python, &lt;code&gt;text.find(pattern)&lt;/code&gt; beats anything you write here, and &lt;a href="https://bimalkhatri.com.np/blogs/kmp-string-matching" rel="noopener noreferrer"&gt;KMP&lt;/a&gt; has the better worst case. Rabin-Karp earns its place elsewhere: hunting ten thousand patterns at once, or wanting a cheap hash of every window for its own sake — deduplication, backup chunking, plagiarism detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Give every string of length &lt;code&gt;m&lt;/code&gt; a number, by reading it as a number written in base &lt;code&gt;B&lt;/code&gt; — exactly as &lt;code&gt;407&lt;/code&gt; is &lt;code&gt;4 × 10² + 0 × 10 + 7&lt;/code&gt; in base 10. Map the characters to digits (&lt;code&gt;a&lt;/code&gt; is 0, &lt;code&gt;b&lt;/code&gt; is 1, up to &lt;code&gt;z&lt;/code&gt; as 25) and take the base to be the alphabet size, 26. Then &lt;code&gt;"ban"&lt;/code&gt; is &lt;code&gt;1 × 26² + 0 × 26 + 13 = 689&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That mapping is exact: distinct strings of the same length get distinct numbers, because base-26 digits are unique. But you cannot afford the numbers — a 40-character window in base 256 is a 320-bit integer, and arithmetic on it is no longer constant time. So take it modulo a prime &lt;code&gt;P&lt;/code&gt; that fits in a machine word. The hash is now small and fast, and equal hashes only mean the strings are &lt;em&gt;probably&lt;/em&gt; equal. That word "probably" separates a correct implementation from a broken one.&lt;/p&gt;

&lt;p&gt;Here is the baseline you are trying to beat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return every start index where pattern occurs in text, by direct comparison.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# The slice copies m characters before the comparison even starts.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1, 3]
[0, 1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are &lt;code&gt;n - m + 1&lt;/code&gt; starting positions and each comparison can cost up to &lt;code&gt;m&lt;/code&gt; characters, so the worst case is O(n·m). A character-by-character version behaves far better on English text, because most comparisons die on the first character — but "usually fine" is not a bound, and the pathological input (&lt;code&gt;"aaaa…a"&lt;/code&gt; searched for &lt;code&gt;"aaa…ab"&lt;/code&gt;) really does cost the full product. The slice above never gets even the good case: &lt;code&gt;text[start:start + m]&lt;/code&gt; copies &lt;code&gt;m&lt;/code&gt; characters before the comparison begins, so it pays the full product on every input.&lt;/p&gt;

&lt;p&gt;Rabin-Karp replaces that inner comparison with an integer comparison:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-hash-windows.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-hash-windows.svg" alt="The text banana split into four overlapping three-character windows, each labelled with its base-26 hash modulo 101" width="1000" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The rolling update
&lt;/h3&gt;

&lt;p&gt;Computing each of those hashes from scratch costs O(m), and there are &lt;code&gt;n - m + 1&lt;/code&gt; of them, so you are back at O(n·m) and have gained nothing. The win comes from how much consecutive windows share.&lt;/p&gt;

&lt;p&gt;Window &lt;code&gt;i&lt;/code&gt; covers &lt;code&gt;text[i]&lt;/code&gt; through &lt;code&gt;text[i + m - 1]&lt;/code&gt;; window &lt;code&gt;i + 1&lt;/code&gt; covers &lt;code&gt;text[i + 1]&lt;/code&gt; through &lt;code&gt;text[i + m]&lt;/code&gt;. They share &lt;code&gt;m - 1&lt;/code&gt; characters, so going from one to the next is three digit operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remove the leading digit.&lt;/strong&gt; &lt;code&gt;text[i]&lt;/code&gt; is the most significant digit of window &lt;code&gt;i&lt;/code&gt;, contributing &lt;code&gt;value(text[i]) · B^(m-1)&lt;/code&gt;. Subtract exactly that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shift left one place.&lt;/strong&gt; Multiply by &lt;code&gt;B&lt;/code&gt;, moving every remaining character up one power. That is what sliding means.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Append the new digit.&lt;/strong&gt; Add &lt;code&gt;value(text[i + m])&lt;/code&gt; in the now-empty units place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As one line, everything taken modulo &lt;code&gt;P&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hash(i + 1) = ((hash(i) - value(text[i]) * high_power) * B + value(text[i + m])) % P&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;where &lt;code&gt;high_power&lt;/code&gt; is &lt;code&gt;B^(m-1) % P&lt;/code&gt;, computed once before the loop. Two multiplications, a subtraction, an addition and one modulo — and not one of them depends on &lt;code&gt;m&lt;/code&gt;. That is the O(1) step that makes the whole thing worth doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take the text &lt;code&gt;"banana"&lt;/code&gt; and the pattern &lt;code&gt;"ana"&lt;/code&gt;, with base 26 and modulus 101. The letter values are &lt;code&gt;a = 0&lt;/code&gt;, &lt;code&gt;b = 1&lt;/code&gt;, &lt;code&gt;n = 13&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The pattern first: &lt;code&gt;"ana"&lt;/code&gt; is &lt;code&gt;0 × 676 + 13 × 26 + 0 = 338&lt;/code&gt;, and &lt;code&gt;338 mod 101 = 35&lt;/code&gt;. That number is computed once and never again. The first window, &lt;code&gt;"ban"&lt;/code&gt;, is &lt;code&gt;1 × 676 + 0 × 26 + 13 = 689&lt;/code&gt;, and &lt;code&gt;689 mod 101 = 83&lt;/code&gt;. Not 35, so no match at position 0. Every window after that comes from the rolling update, with &lt;code&gt;high_power = 26² mod 101 = 676 mod 101 = 70&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roll from &lt;code&gt;"ban"&lt;/code&gt; to &lt;code&gt;"ana"&lt;/code&gt;.&lt;/strong&gt; The character leaving is &lt;code&gt;b&lt;/code&gt;, worth 1, so subtract &lt;code&gt;1 × 70 = 70&lt;/code&gt;: &lt;code&gt;83 - 70 = 13&lt;/code&gt;. Shift: &lt;code&gt;13 × 26 = 338&lt;/code&gt;. The character entering is &lt;code&gt;a&lt;/code&gt;, worth 0, so add nothing. &lt;code&gt;338 mod 101 = 35&lt;/code&gt;, which equals the pattern hash — so verify the characters, &lt;code&gt;text[1:4]&lt;/code&gt; really is &lt;code&gt;"ana"&lt;/code&gt;, and report a match at index 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roll from &lt;code&gt;"ana"&lt;/code&gt; to &lt;code&gt;"nan"&lt;/code&gt;.&lt;/strong&gt; Leaving is &lt;code&gt;a&lt;/code&gt;, worth 0, so subtract nothing: still 35. Shift: &lt;code&gt;35 × 26 = 910&lt;/code&gt;. Entering is &lt;code&gt;n&lt;/code&gt;, worth 13: &lt;code&gt;910 + 13 = 923&lt;/code&gt;, and &lt;code&gt;923 mod 101 = 14&lt;/code&gt; since &lt;code&gt;101 × 9 = 909&lt;/code&gt;. Not 35, no match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roll from &lt;code&gt;"nan"&lt;/code&gt; to &lt;code&gt;"ana"&lt;/code&gt;.&lt;/strong&gt; Leaving is &lt;code&gt;n&lt;/code&gt;, worth 13: &lt;code&gt;14 - 13 × 70 = 14 - 910 = -896&lt;/code&gt;. This is where hand-written implementations break, because the intermediate went negative. Python's &lt;code&gt;%&lt;/code&gt; always returns a non-negative result for a positive modulus, so &lt;code&gt;-896 % 101&lt;/code&gt; is 13 and the arithmetic carries on. Shift and append: &lt;code&gt;13 × 26 = 338&lt;/code&gt;, plus 0, &lt;code&gt;mod 101 = 35&lt;/code&gt;. Another hit, verified, match at index 3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-rolling-update.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-rolling-update.svg" alt="The rolling update from the window ban to the window ana, shown as four arithmetic steps" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are those hashes computed the slow way, one full pass per window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
&lt;span class="n"&gt;MOD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Map &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;..&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;z&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; onto the digits 0..25 of a base-26 number.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;slow_window_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Read a string as a base-26 number, kept small by a modulus. Costs O(len(window)).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;


&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pattern&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;slow_window_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;slow_window_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pattern ana -&amp;gt; 35
0 ban -&amp;gt; 83
1 ana -&amp;gt; 35
2 nan -&amp;gt; 14
3 ana -&amp;gt; 35
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the same sequence of numbers produced by rolling, at a fixed cost per window instead of a fresh pass over each one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;high_power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 26 ** 2 mod 101: the weight of the leading character
&lt;/span&gt;&lt;span class="n"&gt;rolling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;slow_window_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high_power&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high_power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;rolling&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;entering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;rolling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;rolling&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;high_power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entering&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;MOD&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;rolling&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;high_power 70
ban 83
ana 35
nan 14
ana 35
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Identical hashes, one constant-time step each. Note &lt;code&gt;pow(BASE, m - 1, MOD)&lt;/code&gt;: Python's three-argument &lt;code&gt;pow&lt;/code&gt; does modular exponentiation in O(log m) multiplications rather than &lt;code&gt;m&lt;/code&gt; of them, the technique in &lt;a href="https://bimalkhatri.com.np/blogs/fast-exponentiation" rel="noopener noreferrer"&gt;fast exponentiation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spurious hits
&lt;/h3&gt;

&lt;p&gt;Now break it deliberately. Keep base 26 but drop the modulus to 31, still prime but far too small. &lt;code&gt;"ana"&lt;/code&gt; is 338 and &lt;code&gt;338 mod 31 = 28&lt;/code&gt;; &lt;code&gt;"nan"&lt;/code&gt; is &lt;code&gt;13 × 676 + 0 + 13 = 8801&lt;/code&gt; and &lt;code&gt;8801 mod 31 = 28&lt;/code&gt; as well. Two windows sharing no arrangement of characters now hash identically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-spurious-hit.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-spurious-hit.svg" alt="The four windows of banana under modulus 31, with the window nan flagged as a spurious hit" width="1000" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is a &lt;strong&gt;spurious hit&lt;/strong&gt;: equal hashes, unequal strings. You cannot eliminate it, because you are mapping every possible &lt;code&gt;m&lt;/code&gt;-character string into &lt;code&gt;P&lt;/code&gt; buckets and there are vastly more strings than buckets. The only defence is the one the algorithm builds in: &lt;strong&gt;when the hashes match, compare the actual characters before reporting anything.&lt;/strong&gt; Skip that check to save time and you have written a search that returns wrong answers on inputs you will never think to test.&lt;/p&gt;

&lt;p&gt;A large prime does not remove collisions, it makes them rare — which is the whole point of the modulus choice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prime matters.&lt;/strong&gt; A modulus sharing factors with the base throws information away. The extreme case is a power of the base, &lt;code&gt;B = 256&lt;/code&gt; with &lt;code&gt;P = 2^24&lt;/code&gt;: that keeps only the window's last three bytes and discards every earlier character, so &lt;code&gt;"...abc"&lt;/code&gt; and &lt;code&gt;"...xyz abc"&lt;/code&gt; collide by construction. A prime shares no factors with a sensible base, so all &lt;code&gt;P&lt;/code&gt; residues stay reachable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large matters.&lt;/strong&gt; For a hash spread evenly over &lt;code&gt;P&lt;/code&gt; values, two different windows collide with probability about &lt;code&gt;1/P&lt;/code&gt;. With &lt;code&gt;P&lt;/code&gt; near &lt;code&gt;10^9&lt;/code&gt;, scanning a million-character text you expect roughly &lt;code&gt;10^6 / 10^9 = 0.001&lt;/code&gt; spurious hits. With &lt;code&gt;P = 31&lt;/code&gt; you expect one every 31 windows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The base should be at least the alphabet size&lt;/strong&gt;, so distinct characters are distinct digits, and coprime to the modulus. 256 for bytes; 31 and 257 are common for text.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;The real implementation, with a byte-sized base and a prime near a billion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                      &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_007&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return every start index where pattern occurs in text.

    Each window&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s hash is derived from the previous window&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s in constant time,
    and every hash match is verified against the real characters before it is
    reported, because two different windows can share a hash.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;high_power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# The slice runs only when the hashes agree, so it is rare on real text.
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;entering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;leaving&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;high_power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entering&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abracadabra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;longer pattern&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1, 3]
[0, 7]
[0, 1, 2]
[]
[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The setup loop builds both hashes at once&lt;/strong&gt;, walking the pattern and the first window together with Horner's method: multiply the running value by the base, add the next digit, reduce. That loop is the O(m) preprocessing cost, and the only place a hash is built from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;high_power&lt;/code&gt; is &lt;code&gt;B^(m-1) mod P&lt;/code&gt;&lt;/strong&gt; — the weight of the leading character. It never changes, so it is computed once. Getting it wrong by one power (using &lt;code&gt;B^m&lt;/code&gt;) is the most common bug here. The first window is still hashed from scratch, so a match at index 0 survives; every rolled hash after it is wrong, so the search quietly returns a short list rather than raising.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ord(character)&lt;/code&gt; is the digit mapping.&lt;/strong&gt; Base 256 with &lt;code&gt;ord&lt;/code&gt; handles ASCII directly. Non-ASCII survives too — &lt;code&gt;ord&lt;/code&gt; goes well past 255, so digits can exceed the base, but the hash stays a deterministic function of the characters. For byte-level work, encode to UTF-8 first and hash the bytes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The verification is the &lt;code&gt;and&lt;/code&gt; in the condition.&lt;/strong&gt; Python short-circuits, so the slice comparison only runs when the hashes already agree. On text with few matches it almost never executes, which is exactly why the average case is linear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The guarded roll.&lt;/strong&gt; The update sits behind &lt;code&gt;if start &amp;lt; n - m&lt;/code&gt; because the last window has no &lt;code&gt;text[start + m]&lt;/code&gt; to read. Without the guard, every search ends in an &lt;code&gt;IndexError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases.&lt;/strong&gt; An empty pattern returns every position from 0 to &lt;code&gt;n&lt;/code&gt;, matching the convention that an empty string occurs everywhere; it needs a special case because &lt;code&gt;high_power&lt;/code&gt; is meaningless for &lt;code&gt;m = 0&lt;/code&gt;. A pattern longer than the text returns before any arithmetic. Overlapping matches fall out free — the window advances one character, not &lt;code&gt;m&lt;/code&gt;, which is why &lt;code&gt;"aaaa"&lt;/code&gt; searched for &lt;code&gt;"aa"&lt;/code&gt; reports 0, 1 and 2.&lt;/p&gt;

&lt;p&gt;Spurious hits are measurable. This counts how often the hashes agree against how often the strings really match, on a 1,200-character text built by repeating &lt;code&gt;"banana"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_hash_hits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return (hash hits, verified matches) for the base-26 lowercase hash.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;high_power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern_hash&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;verified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;verified&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;high_power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;BASE&lt;/span&gt;
                      &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;letter_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verified&lt;/span&gt;


&lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;  &lt;span class="c1"&gt;# 1,200 characters holding 400 real occurrences of "ana"
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_007&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_hash_hits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;modulus &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; hash hits, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;verified&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; verified matches&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modulus         31: 600 hash hits, 400 verified matches
modulus        101: 400 hash hits, 400 verified matches
modulus 1000000007: 400 hash hits, 400 verified matches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modulus 31 produces 200 wasted verifications — one for every &lt;code&gt;"nan"&lt;/code&gt; in the text. Modulus 101 already separates those two windows. The billion-sized prime is what you would actually ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Preprocessing: O(m).&lt;/strong&gt; One pass over the pattern and one over the first window, plus &lt;code&gt;pow(base, m - 1, modulus)&lt;/code&gt;, which is O(log m) multiplications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The scan: O(n) hash work.&lt;/strong&gt; There are &lt;code&gt;n - m + 1&lt;/code&gt; windows, and each rolling update is a fixed two multiplications, one subtraction, one addition and one modulo. No part of it depends on &lt;code&gt;m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification: O(m) per hash hit.&lt;/strong&gt; Two kinds of hit exist. Real matches: if the pattern occurs &lt;code&gt;k&lt;/code&gt; times you compare &lt;code&gt;k · m&lt;/code&gt; characters. That cost is Rabin-Karp's own, not a law of string search — &lt;a href="https://bimalkhatri.com.np/blogs/kmp-string-matching" rel="noopener noreferrer"&gt;KMP&lt;/a&gt; reports the same &lt;code&gt;k&lt;/code&gt; occurrences in O(n + m) total however large &lt;code&gt;k&lt;/code&gt; is, because it never re-reads a character it has already matched. Spurious hits: with the hash spread evenly over &lt;code&gt;P&lt;/code&gt; residues each window collides with probability about &lt;code&gt;1/P&lt;/code&gt;, so the expected count is &lt;code&gt;n / P&lt;/code&gt; and the expected wasted work is &lt;code&gt;n · m / P&lt;/code&gt; characters. On a million-character text with a 100-character pattern and &lt;code&gt;P = 10^9&lt;/code&gt;, that is &lt;code&gt;10^6 × 100 / 10^9&lt;/code&gt; — a tenth of one character comparison.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best case: O(n + m).&lt;/strong&gt; There is no early exit, so the loop always performs all &lt;code&gt;n - m + 1&lt;/code&gt; rolling updates. The floor is therefore the O(m) preprocessing plus one constant-time step per window, and it is reached exactly when no window's hash ever equals the pattern's — no verification runs at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Average case: O(n + m).&lt;/strong&gt; Add the three: &lt;code&gt;O(m)&lt;/code&gt; preprocessing, &lt;code&gt;O(n)&lt;/code&gt; rolling, &lt;code&gt;O(k · m + n · m / P)&lt;/code&gt; verification. With few occurrences and a large prime the last term rounds away, leaving a bound linear in the total input size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worst case: O(n·m).&lt;/strong&gt; This happens whenever every window produces a hit. The easy trigger is a text and pattern of one repeated character, where every window is a genuine match and each verification costs the full &lt;code&gt;m&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;worst_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;
&lt;span class="n"&gt;worst_pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rabin_karp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worst_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worst_pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;windows matched;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;worst_pattern&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;characters compared&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1901 windows matched; 190100 characters compared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;KMP finds those same 1,901 matches in about &lt;code&gt;n + m&lt;/code&gt; = 2,100 steps, which is the concrete version of "KMP has the better worst case".&lt;/p&gt;

&lt;p&gt;The dangerous trigger is adversarial. Base and modulus are usually constants in the source, so anyone who can read the code can build a text whose every window collides with the pattern hash, turning a linear search quadratic on demand. If the input comes from strangers, pick the modulus or base randomly at process start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(1) working memory.&lt;/strong&gt; A handful of integers, however long the text is. Two things sit outside that count: the returned list of positions, which is output rather than working space, and the &lt;code&gt;text[start:start + m]&lt;/code&gt; slice, which Python materialises as a throwaway &lt;code&gt;m&lt;/code&gt; characters wide each time a hash agrees. The multi-pattern version below needs O(k) for a dictionary of &lt;code&gt;k&lt;/code&gt; pattern hashes.&lt;/p&gt;

&lt;p&gt;One caveat about "constant time" arithmetic: it holds while the numbers fit in a machine word. Keep the modulus below &lt;code&gt;2^31&lt;/code&gt; if intermediate products must fit in a signed 64-bit integer, since &lt;code&gt;(window - leaving * high_power) * base&lt;/code&gt; can reach roughly &lt;code&gt;P × B²&lt;/code&gt; — about 2^47 when &lt;code&gt;P&lt;/code&gt; is near 2^31 and &lt;code&gt;B&lt;/code&gt; is 256. Python integers are arbitrary precision so nothing overflows, but arithmetic on numbers far larger than a word stops being constant time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-growth.svg" alt="Linear growth against quadratic growth, marking where Rabin-Karp sits in each case" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching for many patterns at once
&lt;/h2&gt;

&lt;p&gt;This is where Rabin-Karp stops being an academic exercise. A window's hash does not know what it is being compared against, so instead of comparing it to one number, look it up in a dictionary of many.&lt;/p&gt;

&lt;p&gt;Put every pattern's hash in a dict keyed by hash and run the same single pass, checking each window's hash against the dict. That lookup is O(1) whether you are searching for 4 patterns or 40,000. KMP cannot do this — its skip table is built from one specific pattern, so &lt;code&gt;k&lt;/code&gt; patterns means &lt;code&gt;k&lt;/code&gt; passes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-multi-pattern.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frabin-karp-string-matching-multi-pattern.svg" alt="A dictionary of pattern hashes, with the current window hash landing in one bucket" width="1000" height="384"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rabin_karp_multi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                     &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1_000_000_007&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Find every occurrence of any of the patterns, which must share one length.

    The rolling hash is unchanged; only the comparison changes, from one number
    to a dictionary lookup. That lookup costs the same whether there is one
    pattern or a hundred thousand.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;every pattern must have the same length&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;by_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;
        &lt;span class="n"&gt;by_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;high_power&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Two different patterns can collide too, so still verify each candidate.
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;by_hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;high_power&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;
                      &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;modulus&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;


&lt;span class="n"&gt;sentence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the quick brown fox jumps over the lazy dog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_multi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quick&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;brown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jumps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zebra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

&lt;span class="n"&gt;needles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;03&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;  &lt;span class="c1"&gt;# 1,000 patterns, all length 5
&lt;/span&gt;&lt;span class="n"&gt;log_line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user id042 opened id777 and closed id042 again&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rabin_karp_multi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;log_line&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needles&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[(4, 'quick'), (10, 'brown'), (20, 'jumps')]
[(5, 'id042'), (18, 'id777'), (35, 'id042')]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second search checks a thousand patterns against 46 characters in one pass, at the same scanning cost as searching for one — only the dictionary build is proportional to the number of patterns. The dict values are lists rather than single strings because two &lt;em&gt;patterns&lt;/em&gt; can collide with each other, which needs the same verification treatment.&lt;/p&gt;

&lt;p&gt;The restriction is real, though: every pattern must be the same length, because one rolling hash tracks one window size. Patterns of &lt;code&gt;d&lt;/code&gt; distinct lengths need &lt;code&gt;d&lt;/code&gt; passes. If &lt;code&gt;d&lt;/code&gt; is large, use Aho-Corasick instead — it handles mixed lengths in one pass with an automaton built from a &lt;a href="https://bimalkhatri.com.np/blogs/tries-prefix-trees" rel="noopener noreferrer"&gt;trie&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it for many patterns of one length.&lt;/strong&gt; Blocklists of fixed-length tokens, a set of known hashes, a dictionary of 5-grams: one pass, one dict lookup per position.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it when the rolling hash is the product, not the search.&lt;/strong&gt; Content-defined chunking, fingerprinting every substring of a document, finding duplicate blocks between two files — these want a hash of every window and never do exact matching at all. The rolling update is the only reason they are affordable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it when you want a short, obviously-correct implementation.&lt;/strong&gt; It is about 20 lines you can re-derive from the base-&lt;code&gt;B&lt;/code&gt; idea. KMP's failure function is easier to get subtly wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for a single pattern in Python.&lt;/strong&gt; The built-ins are written in C and are strictly better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finditer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?=ana)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
[1, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;str.find&lt;/code&gt; and the &lt;code&gt;in&lt;/code&gt; operator use CPython's &lt;code&gt;fastsearch&lt;/code&gt;: a Boyer-Moore-Horspool variant with a bitmask skip table, plus the two-way algorithm for long needles since Python 3.10 to avoid quadratic blowups. The lookahead in &lt;code&gt;re.finditer("(?=ana)", …)&lt;/code&gt; is how you get overlapping matches, which a &lt;code&gt;find&lt;/code&gt; loop misses unless it advances by one character rather than by the pattern length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when the worst case must be bounded.&lt;/strong&gt; Rabin-Karp is O(n·m) in the worst case, and an attacker can construct that case. &lt;a href="https://bimalkhatri.com.np/blogs/kmp-string-matching" rel="noopener noreferrer"&gt;KMP&lt;/a&gt; is O(n + m) always, with no probabilistic argument anywhere in it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for approximate matching.&lt;/strong&gt; A rolling hash tells you two windows are equal, never how similar they are — change one character and the hash is unrelated. For "how different are these two strings", reach for &lt;a href="https://bimalkhatri.com.np/blogs/edit-distance-levenshtein" rel="noopener noreferrer"&gt;edit distance&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;rsync&lt;/strong&gt; is the clearest example of the shape, even though it does not use a polynomial hash. To work out which blocks of a file the far end already has, it rolls a weak checksum (a variant of Adler-32) over every byte offset, looks each value up in a hash table of the remote block checksums, and only then confirms a hit with a strong hash — MD4 originally, MD5 in current versions. Cheap rolling hash, expensive verification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content-defined chunking&lt;/strong&gt; is Rabin's fingerprint doing what it was designed for. The Low-Bandwidth Network File System (LBFS, 2001) introduced it: roll a hash over a small sliding window and cut a chunk boundary wherever the hash's low bits are all zero. Because boundaries depend on content rather than offset, inserting a byte at the front of a file shifts only the chunk containing it — every other chunk keeps its identity and does not need re-uploading. &lt;strong&gt;restic&lt;/strong&gt; does exactly this with a Rabin fingerprint in its &lt;code&gt;chunker&lt;/code&gt; package; &lt;strong&gt;borgbackup&lt;/strong&gt; does the same job with a different rolling hash (buzhash), which tells you the structure matters more than the specific function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plagiarism and near-duplicate detection.&lt;/strong&gt; The winnowing algorithm behind MOSS (Schleimer, Wilkerson and Aiken, 2003) hashes every k-gram of a document and keeps a sample as its fingerprint; Karp-Rabin hashing is what makes hashing &lt;em&gt;every&lt;/em&gt; k-gram affordable. Broder's shingling work on near-duplicate web pages at AltaVista used Rabin fingerprints of overlapping word sequences for the same reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interview and contest problems.&lt;/strong&gt; Rolling hashes solve a family of questions that look unrelated to string search: counting distinct substrings of a given length, finding the longest substring common to two strings by binary searching the length, and comparing two long substrings in O(1) after O(n) preprocessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Not verifying the characters.&lt;/strong&gt; The serious one. Equal hashes are evidence, not proof. An implementation that reports a match on hash equality alone is wrong, and wrong rarely enough that testing will not find it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recomputing each window hash from scratch.&lt;/strong&gt; If the inner loop walks &lt;code&gt;m&lt;/code&gt; characters you have written the naive algorithm with extra arithmetic. The update must be O(1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;B^m&lt;/code&gt; instead of &lt;code&gt;B^(m-1)&lt;/code&gt;.&lt;/strong&gt; The leading character sits in place &lt;code&gt;m - 1&lt;/code&gt;. Every rolled hash after the first window is then wrong, so the search loses every match except one that starts at index 0 — and loses them silently. Test on a text where the pattern occurs late, not only at the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting the intermediate go negative in a language without floor modulo.&lt;/strong&gt; &lt;code&gt;window - leaving * high_power&lt;/code&gt; is often negative. Python's &lt;code&gt;%&lt;/code&gt; returns a non-negative result, so this just works; in C, C++, Java, Go or Rust it does not, and the search silently misses matches unless you add &lt;code&gt;modulus&lt;/code&gt; back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A modulus that is too small, or shares factors with the base.&lt;/strong&gt; Modulus 31 gave a spurious hit every few windows above, and a power-of-two modulus with base 256 is worse than small — it discards everything but the last few characters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reaching for Python's built-in &lt;code&gt;hash()&lt;/code&gt;.&lt;/strong&gt; It cannot roll, and it is randomised per process by default, so results are not reproducible across runs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting overlapping matches.&lt;/strong&gt; After a match at index &lt;code&gt;i&lt;/code&gt; the next window starts at &lt;code&gt;i + 1&lt;/code&gt;, not &lt;code&gt;i + m&lt;/code&gt;. Skipping ahead by the pattern length misses the second &lt;code&gt;"ana"&lt;/code&gt; in &lt;code&gt;"banana"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add an early return to &lt;code&gt;rabin_karp_search&lt;/code&gt; so it stops at the first match and returns &lt;code&gt;-1&lt;/code&gt; when there is none, mirroring &lt;code&gt;str.find&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Count the distinct substrings of length &lt;code&gt;k&lt;/code&gt; in a string by rolling one hash across it and collecting the values in a set, then explain why the answer can be slightly too low.&lt;/li&gt;
&lt;li&gt;Extend &lt;code&gt;rabin_karp_multi&lt;/code&gt; to accept patterns of different lengths by grouping them by length and running one pass per group, and count how many passes a realistic blocklist needs.&lt;/li&gt;
&lt;li&gt;Implement double hashing: carry two rolling hashes with different primes and treat a hit as a hit only when both agree. Measure how many spurious hits survive on the &lt;code&gt;"banana" * 200&lt;/code&gt; text with moduli 31 and 37.&lt;/li&gt;
&lt;li&gt;Write a content-defined chunker: roll a hash over a 48-byte window and cut a boundary wherever &lt;code&gt;hash % 4096 == 0&lt;/code&gt;. Insert a character at the front of the input and show that all chunks after the first are unchanged.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Rabin-Karp is a good algorithm attached to a great primitive. The search itself is rarely the right tool for one pattern in one text — the standard library is faster and KMP has a stronger guarantee. The rolling hash underneath it is what matters: constant-time hashing of every window is the foundation of backup deduplication, rsync's block matching, and every plagiarism detector that fingerprints k-grams. Learn the update arithmetic, and remember that a hash match is a hint that must always be checked.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preprocessing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(m) — hash the pattern and the first window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n + m) — no window's hash ever collides&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n + m) — one O(1) roll per window, about n/P spurious hits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worst case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n·m) — every window hits, so every window is verified&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) working memory for one pattern, O(k) for k patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Finds overlapping matches&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes — the window advances one character at a time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes — any number of patterns of one length, in a single pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deterministic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes on output, probabilistic only on running time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;String or byte sequence, plus a dict of pattern hashes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Many same-length patterns, or you need a hash of every window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One pattern and a bounded worst case matters — use KMP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;rsync block matching, restic chunking, MOSS plagiarism fingerprints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;text.find(pattern)&lt;/code&gt; / &lt;code&gt;re.finditer&lt;/code&gt; — CPython's C &lt;code&gt;fastsearch&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/kmp-string-matching" rel="noopener noreferrer"&gt;The KMP Algorithm&lt;/a&gt; — the other classic string search, with a guaranteed linear worst case and no hashing at all.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash Tables&lt;/a&gt; — where hash functions, collisions and buckets are explained from first principles.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/sliding-window-technique" rel="noopener noreferrer"&gt;The Sliding Window Technique&lt;/a&gt; — the same add-one-remove-one idea applied to sums and counts instead of hashes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/fast-exponentiation" rel="noopener noreferrer"&gt;Fast Exponentiation&lt;/a&gt; — how &lt;code&gt;pow(base, m - 1, modulus)&lt;/code&gt; computes a huge modular power in O(log m) steps.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting arguments used to justify O(n + m) above.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The KMP Algorithm in Python: String Search Without Ever Going Backwards</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:37:03 +0000</pubDate>
      <link>https://dev.to/bimal-py/the-kmp-algorithm-in-python-string-search-without-ever-going-backwards-4pdj</link>
      <guid>https://dev.to/bimal-py/the-kmp-algorithm-in-python-string-search-without-ever-going-backwards-4pdj</guid>
      <description>&lt;p&gt;Searching a text for a substring looks like it should be free. Line the pattern up at the start, compare characters until one disagrees, slide one place right, try again. Python spells it &lt;code&gt;"needle" in haystack&lt;/code&gt; and you never think about it.&lt;/p&gt;

&lt;p&gt;The obvious implementation has a failure mode that is easy to trigger. Looking for &lt;code&gt;aaaaab&lt;/code&gt; inside ten &lt;code&gt;a&lt;/code&gt; characters followed by a &lt;code&gt;b&lt;/code&gt; — an eleven character text — costs 36 character comparisons. Scale both sides up and the cost is the product of the two lengths, not the sum.&lt;/p&gt;

&lt;p&gt;The waste has a precise shape. Every time an alignment fails, the naive search slides the pattern one place right and starts from scratch, even though it just read five characters that matched and is about to read four of them again. It learned something and threw it away. Knuth-Morris-Pratt, published in 1977 by Donald Knuth, James Morris and Vaughan Pratt, keeps it: one small table computed from the pattern alone, and with it the pointer into the text never moves backwards, not once, on any input. The search costs O(n + m), the text length plus the pattern length, with no bad case hiding behind the average.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The naive version, and where it wastes work
&lt;/h3&gt;

&lt;p&gt;Start with the version you would write without thinking: at every possible starting position, compare the pattern character by character and give up at the first disagreement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return every start index where pattern occurs in text, by brute force.

    For each possible alignment of the pattern against the text, compare
    characters left to right and abandon the alignment at the first mismatch.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABDABABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaaaaaaab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[7]
[5]
[1, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is correct, including the overlapping matches of &lt;code&gt;ana&lt;/code&gt; in &lt;code&gt;banana&lt;/code&gt; at 1 and 3. The cost only bites when the pattern nearly matches in many places, so add a counter and measure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;naive_search_counted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Brute force again, but also report how many characters it compared.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;comparisons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;comparisons&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
            &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comparisons&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sample_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_pattern&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABDABABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaaaaaaab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;naive_search_counted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample_pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sample_pattern&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sample_text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; comparisons&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ABABCABAB in ABABDABABABCABAB: [7], 26 comparisons
aaaaab in aaaaaaaaaab: [5], 36 comparisons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thirty-six comparisons to search eleven characters. There are &lt;code&gt;n - m + 1&lt;/code&gt; alignments and each can run the full length of the pattern before failing, so the worst case is &lt;code&gt;(n - m + 1) * m&lt;/code&gt; comparisons: &lt;strong&gt;O(n × m)&lt;/strong&gt;. On a 1 MB text with a 1,000 character pattern of that shape, a billion comparisons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-naive-restart.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-naive-restart.svg" alt="Three consecutive naive alignments of aaaaab against a text of ten a characters, each one re-reading five characters the previous alignment already matched" width="1000" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repetitive input is not exotic: DNA has a four-letter alphabet, log lines repeat the same prefixes, binary formats are full of zero bytes. And if an attacker picks the input, they can make this quadratic on purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a partial match already tells you
&lt;/h3&gt;

&lt;p&gt;Here is the observation the whole algorithm rests on. Suppose you aligned the pattern at text position &lt;code&gt;s&lt;/code&gt;, matched &lt;code&gt;j&lt;/code&gt; characters, then hit a mismatch. You now know something exact and free: &lt;code&gt;text[s : s + j]&lt;/code&gt; equals &lt;code&gt;pattern[: j]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The naive version deletes that. But you can work out in advance, without looking at the text at all, which shifts are worth trying. A match beginning at &lt;code&gt;s + k&lt;/code&gt;, for a shift &lt;code&gt;k&lt;/code&gt; between 1 and &lt;code&gt;j&lt;/code&gt;, needs its first &lt;code&gt;j - k&lt;/code&gt; characters to line up with &lt;code&gt;text[s + k : s + j]&lt;/code&gt; — which you already know, because those characters are &lt;code&gt;pattern[k : j]&lt;/code&gt;. So the shift is worth trying only if&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pattern[: j - k]&lt;/code&gt; equals &lt;code&gt;pattern[k : j]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;which says: some prefix of the pattern is also a suffix of &lt;code&gt;pattern[: j]&lt;/code&gt;. A string that is both a proper prefix and a suffix of another string is called a &lt;strong&gt;border&lt;/strong&gt;. "Proper" means not the whole string — otherwise everything borders itself and nothing ever shifts.&lt;/p&gt;

&lt;p&gt;To shift as little as possible, so you never jump over a match, you want the &lt;strong&gt;longest&lt;/strong&gt; border. The longest border of &lt;code&gt;ABABCABAB&lt;/code&gt; is &lt;code&gt;ABAB&lt;/code&gt;: those four characters open the pattern and also close it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-border.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-border.svg" alt="The pattern ABABCABAB with its first four characters and its last four characters both highlighted as the string ABAB" width="1000" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So after matching all nine characters, the next alignment to try is not one place right and not nine — it is five places right, because four characters are already verified. The longest border of every prefix of the pattern is the entire table KMP needs. It is called the &lt;strong&gt;failure function&lt;/strong&gt;, or the &lt;strong&gt;LPS array&lt;/strong&gt;, for &lt;em&gt;longest proper prefix which is also a suffix&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Building the table for ABABCABAB
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;lps[index]&lt;/code&gt; is the length of the longest border of &lt;code&gt;pattern[: index + 1]&lt;/code&gt;. Build it left to right carrying one variable, &lt;code&gt;length&lt;/code&gt;, the border length of the previous prefix.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index 0&lt;/strong&gt;, &lt;code&gt;A&lt;/code&gt;. A one-character string has no proper prefix, so &lt;code&gt;lps[0] = 0&lt;/code&gt;. Always.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 1&lt;/strong&gt;, &lt;code&gt;B&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt; is 0, so try to extend the empty border: is &lt;code&gt;pattern[1]&lt;/code&gt; equal to &lt;code&gt;pattern[0]&lt;/code&gt;? &lt;code&gt;B&lt;/code&gt; against &lt;code&gt;A&lt;/code&gt;, no. &lt;code&gt;lps[1] = 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 2&lt;/strong&gt;, &lt;code&gt;A&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt; is 0. Is &lt;code&gt;pattern[2]&lt;/code&gt; equal to &lt;code&gt;pattern[0]&lt;/code&gt;? &lt;code&gt;A&lt;/code&gt; against &lt;code&gt;A&lt;/code&gt;, yes. &lt;code&gt;length&lt;/code&gt; becomes 1, &lt;code&gt;lps[2] = 1&lt;/code&gt;. The border of &lt;code&gt;ABA&lt;/code&gt; is &lt;code&gt;A&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 3&lt;/strong&gt;, &lt;code&gt;B&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt; is 1. Is &lt;code&gt;pattern[3]&lt;/code&gt; equal to &lt;code&gt;pattern[1]&lt;/code&gt;? Yes. &lt;code&gt;length&lt;/code&gt; becomes 2, &lt;code&gt;lps[3] = 2&lt;/code&gt;. The border of &lt;code&gt;ABAB&lt;/code&gt; is &lt;code&gt;AB&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 4&lt;/strong&gt;, &lt;code&gt;C&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt; is 2. Is &lt;code&gt;pattern[4]&lt;/code&gt; equal to &lt;code&gt;pattern[2]&lt;/code&gt;? &lt;code&gt;C&lt;/code&gt; against &lt;code&gt;A&lt;/code&gt;, no — &lt;code&gt;AB&lt;/code&gt; cannot be extended. Fall back to the border of &lt;code&gt;AB&lt;/code&gt;, which is &lt;code&gt;lps[1] = 0&lt;/code&gt;, and try again: &lt;code&gt;C&lt;/code&gt; against &lt;code&gt;pattern[0]&lt;/code&gt;, still no. Nothing is left, so &lt;code&gt;lps[4] = 0&lt;/code&gt;. The &lt;code&gt;C&lt;/code&gt; occurs nowhere else in the pattern, so it destroys every border.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 5&lt;/strong&gt;, &lt;code&gt;A&lt;/code&gt;. &lt;code&gt;length&lt;/code&gt; is 0 and &lt;code&gt;A&lt;/code&gt; matches &lt;code&gt;pattern[0]&lt;/code&gt;, so &lt;code&gt;lps[5] = 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 6&lt;/strong&gt;, &lt;code&gt;B&lt;/code&gt;. Matches &lt;code&gt;pattern[1]&lt;/code&gt;. &lt;code&gt;lps[6] = 2&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 7&lt;/strong&gt;, &lt;code&gt;A&lt;/code&gt;. Matches &lt;code&gt;pattern[2]&lt;/code&gt;. &lt;code&gt;lps[7] = 3&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index 8&lt;/strong&gt;, &lt;code&gt;B&lt;/code&gt;. Matches &lt;code&gt;pattern[3]&lt;/code&gt;. &lt;code&gt;lps[8] = 4&lt;/code&gt; — the &lt;code&gt;ABAB&lt;/code&gt; from the diagram above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-lps-table.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-lps-table.svg" alt="The pattern ABABCABAB with its LPS values 0 0 1 2 0 1 2 3 4 written underneath each character" width="1000" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Index 4 is the step worth staring at. When a border cannot be extended, the next candidate is &lt;em&gt;the border of that border&lt;/em&gt;. That is why the table can be built out of itself, and it is the same move the search makes on a mismatch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Searching ABABDABABABCABAB
&lt;/h3&gt;

&lt;p&gt;Now search the text &lt;code&gt;ABABDABABABCABAB&lt;/code&gt; with &lt;code&gt;i&lt;/code&gt; as the text pointer and &lt;code&gt;j&lt;/code&gt; as the pattern pointer. &lt;code&gt;j&lt;/code&gt; doubles as "how many characters currently match", so the pattern sits at text position &lt;code&gt;i - j&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alignment at 0.&lt;/strong&gt; &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt; match, so &lt;code&gt;i&lt;/code&gt; and &lt;code&gt;j&lt;/code&gt; both reach 4. Then &lt;code&gt;text[4]&lt;/code&gt; is &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;pattern[4]&lt;/code&gt; is &lt;code&gt;C&lt;/code&gt;. Mismatch, four characters matched. Consult &lt;code&gt;lps[3] = 2&lt;/code&gt;: two of them survive. Set &lt;code&gt;j = 2&lt;/code&gt; and &lt;strong&gt;leave &lt;code&gt;i&lt;/code&gt; at 4&lt;/strong&gt;. The pattern has slid right by two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alignment at 2.&lt;/strong&gt; Compare &lt;code&gt;text[4]&lt;/code&gt;, still &lt;code&gt;D&lt;/code&gt;, against &lt;code&gt;pattern[2]&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;. Mismatch again with &lt;code&gt;j = 2&lt;/code&gt;, so consult &lt;code&gt;lps[1] = 0&lt;/code&gt;. Set &lt;code&gt;j = 0&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt; unchanged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alignment at 4.&lt;/strong&gt; With &lt;code&gt;j&lt;/code&gt; at 0 there is nothing left to fall back on. &lt;code&gt;D&lt;/code&gt; against &lt;code&gt;A&lt;/code&gt; fails, and only now does &lt;code&gt;i&lt;/code&gt; move, to 5.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alignment at 5.&lt;/strong&gt; &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt; match again, taking &lt;code&gt;i&lt;/code&gt; to 9 and &lt;code&gt;j&lt;/code&gt; to 4. &lt;code&gt;text[9]&lt;/code&gt; is &lt;code&gt;A&lt;/code&gt;, &lt;code&gt;pattern[4]&lt;/code&gt; is &lt;code&gt;C&lt;/code&gt;. Mismatch, &lt;code&gt;lps[3] = 2&lt;/code&gt; again, &lt;code&gt;j = 2&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt; stays at 9.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alignment at 7.&lt;/strong&gt; The payoff. Compare &lt;code&gt;text[9]&lt;/code&gt; against &lt;code&gt;pattern[2]&lt;/code&gt;: &lt;code&gt;A&lt;/code&gt; against &lt;code&gt;A&lt;/code&gt;, a match. The characters &lt;code&gt;text[7:9]&lt;/code&gt; were never re-read, and the search rolls straight on to a full match starting at index 7.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-search-trace.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-search-trace.svg" alt="The text ABABDABABABCABAB with the pattern drawn at five successive alignments, the text pointer staying at index 4 for three of them" width="1000" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Five alignments, 19 character comparisons, and the text pointer moved right 16 times and left zero times. Naive search needed 26 comparisons on the same input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Two functions. The table is the harder one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Longest proper prefix that is also a suffix, for every prefix of pattern.

    lps[index] is the length of the longest string that is both a proper
    prefix and a suffix of pattern[:index + 1]. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Proper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; means it is not the
    whole prefix, so the value is always at most index.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;lps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# length of the border of the prefix ending at index - 1
&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="c1"&gt;# The border cannot be extended by pattern[index], so try the next
&lt;/span&gt;        &lt;span class="c1"&gt;# shortest border. lps[length - 1] is exactly that border's length.
&lt;/span&gt;        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;


&lt;span class="n"&gt;demo_pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;demo_pattern&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;demo_pattern&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaaab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abcdef&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A B A B C A B A B
0 0 1 2 0 1 2 3 4
[0, 1, 2, 3, 4, 0]
[0, 0, 0, 0, 0, 0]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are the two extremes and one real case. A pattern of distinct characters has no borders, so every entry is 0 and KMP degenerates to naive search — which costs nothing, because naive search is already linear when nothing ever partially matches. A run of identical characters has the maximum possible borders, so the table counts up.&lt;/p&gt;

&lt;p&gt;The search loop is short once the table exists.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every start index of pattern in text, found in O(len(text) + len(pattern)).

    The text index never decreases: a mismatch rewinds the pattern index to a
    shorter border instead of re-reading characters of the text.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;lps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;# Overlapping matches count, so slide to the longest border
&lt;/span&gt;                &lt;span class="c1"&gt;# rather than starting the pattern from scratch.
&lt;/span&gt;                &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# text_index stays put
&lt;/span&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABDABABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;banana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aaaa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aa&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;xyz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[7]
[1, 3]
[0, 1, 2]
[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;lps[length - 1]&lt;/code&gt; is the fallback, in both functions.&lt;/strong&gt; If the current border has length &lt;code&gt;length&lt;/code&gt;, its own longest border has length &lt;code&gt;lps[length - 1]&lt;/code&gt;. Following that chain from a border to the border of the border enumerates &lt;em&gt;every&lt;/em&gt; border of the current prefix, longest first — which is the complete list of shifts worth trying, in the order that shifts least.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The off-by-one is &lt;code&gt;j - 1&lt;/code&gt;, not &lt;code&gt;j&lt;/code&gt;.&lt;/strong&gt; On a mismatch you have matched &lt;code&gt;pattern[: j]&lt;/code&gt;, a string of length &lt;code&gt;j&lt;/code&gt;, whose table entry lives at index &lt;code&gt;j - 1&lt;/code&gt;. Writing &lt;code&gt;lps[pattern_index]&lt;/code&gt; is the most common KMP bug: it consults the border of a prefix one character longer than the one you actually matched, so the pattern shifts by the wrong amount in whichever direction that entry happens to point. On &lt;code&gt;ABABCABAB&lt;/code&gt; it shifts too far and the match at index 7 disappears; on &lt;code&gt;aa&lt;/code&gt; the entry equals the current &lt;code&gt;j&lt;/code&gt;, so nothing shrinks and the loop spins forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The three branches cover every case.&lt;/strong&gt; Characters agree: advance both pointers. Characters disagree with something matched: rewind &lt;code&gt;j&lt;/code&gt;, hold &lt;code&gt;i&lt;/code&gt;. Characters disagree with nothing matched: there is no partial match to salvage, so advance &lt;code&gt;i&lt;/code&gt;. Note that &lt;code&gt;i&lt;/code&gt; increases in two branches and decreases in none — the whole "never goes backwards" claim is visible in three lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After a full match, &lt;code&gt;j&lt;/code&gt; becomes &lt;code&gt;lps[m - 1]&lt;/code&gt; rather than 0.&lt;/strong&gt; Resetting to 0 works for non-overlapping searches but misses &lt;code&gt;aa&lt;/code&gt; at index 1 of &lt;code&gt;aaaa&lt;/code&gt;. Using the border treats a completed match exactly like a mismatch one character past the end, which is what it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edge cases fall out of the arithmetic.&lt;/strong&gt; An empty text never enters the loop. A pattern longer than the text can never drive &lt;code&gt;j&lt;/code&gt; to &lt;code&gt;len(pattern)&lt;/code&gt;. A pattern that never matches returns an empty list with no special casing. The one case needing a guard is the empty pattern, since &lt;code&gt;pattern[0]&lt;/code&gt; raises &lt;code&gt;IndexError&lt;/code&gt;; the early return matches Python's convention, where &lt;code&gt;"abc".find("")&lt;/code&gt; is 0.&lt;/p&gt;

&lt;p&gt;Here is every comparison the search makes, printed by the algorithm itself, so you can check the hand trace above line by line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;kmp_trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Print one line per character comparison the search performs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;lps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;i_before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j_before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text_index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt;
        &lt;span class="n"&gt;text_char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern_char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text_char&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern_char&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MATCH at &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mismatch: j = lps[&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;j_before&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;] = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j_before&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j_before&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mismatch: j is 0, so i moves&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  i=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i_before&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; j=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;j_before&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text_char&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pattern_char&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;kmp_trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABDABABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ABABCABAB&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 1  i= 0 j=0  A vs A  match
 2  i= 1 j=1  B vs B  match
 3  i= 2 j=2  A vs A  match
 4  i= 3 j=3  B vs B  match
 5  i= 4 j=4  D vs C  mismatch: j = lps[3] = 2
 6  i= 4 j=2  D vs A  mismatch: j = lps[1] = 0
 7  i= 4 j=0  D vs A  mismatch: j is 0, so i moves
 8  i= 5 j=0  A vs A  match
 9  i= 6 j=1  B vs B  match
10  i= 7 j=2  A vs A  match
11  i= 8 j=3  B vs B  match
12  i= 9 j=4  A vs C  mismatch: j = lps[3] = 2
13  i= 9 j=2  A vs A  match
14  i=10 j=3  B vs B  match
15  i=11 j=4  C vs C  match
16  i=12 j=5  A vs A  match
17  i=13 j=6  B vs B  match
18  i=14 j=7  A vs A  match
19  i=15 j=8  B vs B  MATCH at 7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the &lt;code&gt;i&lt;/code&gt; column downwards: 0, 1, 2, 3, 4, 4, 4, 5, 6, … It repeats, but it never goes down.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-mismatch-rule.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-mismatch-rule.svg" alt="The three branches of the search loop: compare, advance both pointers on equality, rewind only the pattern pointer on a mismatch" width="1000" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;A single loop iteration is not bounded — one mismatch can trigger a whole chain of fallbacks. The bound comes from an &lt;strong&gt;amortised&lt;/strong&gt; argument: those fallbacks have to be paid for by earlier work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the table costs at most 2m comparisons.&lt;/strong&gt; Watch &lt;code&gt;length&lt;/code&gt;. It increases by 1 at most once per value of &lt;code&gt;index&lt;/code&gt;, so across the whole build it increases at most &lt;code&gt;m - 1&lt;/code&gt; times. Every iteration of the inner &lt;code&gt;while&lt;/code&gt; loop strictly decreases it, and it never drops below 0. A quantity that goes up at most &lt;code&gt;m - 1&lt;/code&gt; times in total can come down at most &lt;code&gt;m - 1&lt;/code&gt; times in total. So the inner loop runs at most &lt;code&gt;m - 1&lt;/code&gt; times &lt;em&gt;across the entire build&lt;/em&gt;, not per index, and the total comparison count stays under &lt;code&gt;2m&lt;/code&gt;. Preprocessing is &lt;strong&gt;O(m)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Searching costs at most 2n comparisons.&lt;/strong&gt; The same argument one level up. &lt;code&gt;pattern_index&lt;/code&gt; increases only in the branch where the characters agree, and that branch also increases &lt;code&gt;text_index&lt;/code&gt;, so it goes up at most &lt;code&gt;n&lt;/code&gt; times in total — and therefore comes down at most &lt;code&gt;n&lt;/code&gt; times in total. Every loop iteration performs exactly one comparison and then either increases &lt;code&gt;text_index&lt;/code&gt; or decreases &lt;code&gt;pattern_index&lt;/code&gt;, so there are at most &lt;code&gt;n + n&lt;/code&gt; iterations. The scan is &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Together: &lt;strong&gt;O(n + m)&lt;/strong&gt; for the best case, the average case and the worst case alike. There is no adversarial input. Measure it on the pathological shape from the opening, scaled up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;kmp_search_counted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;KMP that reports (matches, comparisons building lps, comparisons scanning).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;lps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;build_comparisons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;build_comparisons&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;build_comparisons&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;

    &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;scan_comparisons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;scan_comparisons&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;text_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;build_comparisons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan_comparisons&lt;/span&gt;


&lt;span class="n"&gt;haystack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;needle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;naive_matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;naive_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;naive_search_counted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kmp_matches&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;build_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scan_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;kmp_search_counted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; chars, pattern &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; chars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;naive: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naive_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; comparisons, matches &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;naive_matches&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kmp:   &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;build_count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;scan_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; comparisons &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
      &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;build_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to build the table, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;scan_count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to scan), matches &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;kmp_matches&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kmp bound 2n + 2m = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text 5001 chars, pattern 101 chars
naive: 495001 comparisons, matches [4900]
kmp:   10100 comparisons (199 to build the table, 9901 to scan), matches [4900]
kmp bound 2n + 2m = 10204
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Forty-nine times fewer comparisons, and the measured 10,100 sits just under the predicted ceiling of 10,204. Naive search pays the full 101 comparisons at every one of its 4,901 alignments; KMP pays just under two per text character, however repetitive the input is. Double the text and the pattern together and naive search quadruples, to 1,970,001; KMP only doubles, to 20,200.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fkmp-string-matching-growth.svg" alt="Linear growth against quadratic growth as the text length increases" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space is O(m)&lt;/strong&gt;: one integer per pattern character, plus the two indices. The text is read, never copied — no slicing, no buffer. The one thing that does grow with the text is the returned list of match positions, and that is output rather than working memory: yield each hit instead of collecting it and the scan itself holds O(m) state no matter how long the text is.&lt;/p&gt;

&lt;p&gt;Correctness deserves checking rather than trusting, so here is an exhaustive comparison against brute force and against Python's own &lt;code&gt;str.find&lt;/code&gt;, over every text of up to 8 characters and every pattern of up to 3 characters from a two-letter alphabet. Small alphabets maximise partial matches, which is precisely where wrong border logic shows up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;

&lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text_length&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;text_letters&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text_length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text_letters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern_length&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern_letters&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pattern_length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;needle_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern_letters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;naive_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needle_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;kmp_search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;needle_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
                &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
                &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;needle_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;
                &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; text/pattern pairs agree with brute force and with str.find&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;7154 text/pattern pairs agree with brute force and with str.find
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In application Python, reach for the built-in.&lt;/strong&gt; &lt;code&gt;pattern in text&lt;/code&gt;, &lt;code&gt;text.find(pattern)&lt;/code&gt;, &lt;code&gt;text.count(pattern)&lt;/code&gt; and &lt;code&gt;re.finditer&lt;/code&gt; are implemented in C and will beat a hand-written KMP loop by roughly two orders of magnitude, because the constant factor of a Python-level loop dwarfs any saving in comparisons. CPython already protects you from the quadratic blow-up too: its string search is a Boyer-Moore-Horspool style skip loop with a Bloom filter over the pattern's characters, and since Python 3.10 it switches to the Crochemore-Perrin "two-way" algorithm for long needles, which carries the same O(n + m) worst case guarantee. Not KMP, same protection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KMP is not the fastest algorithm in practice either.&lt;/strong&gt; It reads every character of the text at least once. Boyer-Moore-Horspool does not: it compares from the right of the pattern, and when the mismatching text character occurs nowhere in the pattern it jumps forward by the pattern's full length. On English prose with a 20 character pattern it touches roughly one character in ten. KMP cannot beat linear, so on ordinary text it loses. What KMP sells is the guarantee — Horspool's worst case is still O(n × m).&lt;/p&gt;

&lt;p&gt;Write KMP when one of these is true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You cannot rewind the input.&lt;/strong&gt; A socket, a pipe, or a multi-gigabyte file you refuse to buffer. Because the text pointer never goes backwards, KMP consumes each character exactly once on arrival, with O(m) state and no window at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The input is adversarial or genuinely repetitive.&lt;/strong&gt; Attacker-supplied patterns, DNA, run-length-ish binary formats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need the failure function for something else.&lt;/strong&gt; It generalises directly to Aho-Corasick for many patterns at once, and &lt;code&gt;m - lps[m - 1]&lt;/code&gt; gives the smallest period of a string for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your language's built-in search is naive.&lt;/strong&gt; Java's &lt;code&gt;String.indexOf&lt;/code&gt; is a straightforward O(n × m) scan.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The streaming case is where KMP is genuinely the right tool rather than a teaching exercise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StreamMatcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Report matches while consuming a stream one character at a time.

    Only the pattern, its LPS table and two integers are kept, so a 4 GB log
    is scanned with state proportional to the pattern and never to the stream.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pattern must not be empty&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_lps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return the start index of a match ending at this character, else None.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumed&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern_index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consumed&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="n"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StreamMatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bananana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1, 3, 5]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three overlapping matches in &lt;code&gt;bananana&lt;/code&gt;, found while holding nothing but the pattern, its three-entry table and two integers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use KMP for approximate matching.&lt;/strong&gt; The border logic assumes exact character equality; allow typos or wildcards and the argument collapses, so use &lt;a href="https://bimalkhatri.com.np/blogs/edit-distance-levenshtein" rel="noopener noreferrer"&gt;edit distance&lt;/a&gt;. For many patterns at once, build an Aho-Corasick automaton rather than running KMP once per pattern. If you search the &lt;em&gt;same&lt;/em&gt; text repeatedly, do not scan at all — index it once with a suffix array or an FM-index.&lt;/p&gt;

&lt;p&gt;One more honest number: on the 16 character example above, KMP costs 9 comparisons to build the table plus 19 to scan, against naive search's 26. Preprocessing is not free, and on short texts it does not pay for itself. KMP wins when &lt;code&gt;n&lt;/code&gt; is large relative to &lt;code&gt;m&lt;/code&gt;, or when one table is reused across many texts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Intrusion detection.&lt;/strong&gt; The failure function generalises from one pattern to a trie of thousands; the result is the Aho-Corasick automaton, and it is a multi-pattern matching engine in both the Snort and Suricata intrusion detection systems — Suricata will pick Intel's Hyperscan instead when its build includes it. They test each packet against tens of thousands of attack signatures in a single pass over the payload, which is only possible because the automaton never re-reads a byte.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small-alphabet sequence scanning.&lt;/strong&gt; DNA has four letters and is full of tandem repeats, so partial matches are constant and long — the input shape that pushes naive search towards its quadratic case, and the one KMP is immune to. It is a reasonable tool for scanning reads for a fixed motif such as a restriction site or a sequencing adapter. Note the limit: the large read aligners such as BWA and Bowtie do &lt;em&gt;not&lt;/em&gt; use KMP, because they answer millions of queries against one fixed reference genome, and it pays to build a Burrows-Wheeler/FM index of that genome once instead of streaming it per query.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Period detection.&lt;/strong&gt; For a pattern of length &lt;code&gt;m&lt;/code&gt;, the value &lt;code&gt;m - lps[m - 1]&lt;/code&gt; is its shortest period, and the string is that period repeated exactly when the period divides &lt;code&gt;m&lt;/code&gt;. &lt;code&gt;abcabcabc&lt;/code&gt; has a final LPS entry of 6, and 9 − 6 = 3 divides 9, so it is &lt;code&gt;abc&lt;/code&gt; three times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mostly, though, it is a building block.&lt;/strong&gt; Aho-Corasick, the Z-algorithm and the general theory of string periodicity all descend from the failure function. Learning KMP is how you get access to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;lps[j]&lt;/code&gt; instead of &lt;code&gt;lps[j - 1]&lt;/code&gt;.&lt;/strong&gt; The table is indexed by last-character position, so the entry for a matched prefix of length &lt;code&gt;j&lt;/code&gt; lives at &lt;code&gt;j - 1&lt;/code&gt;. Getting it wrong shifts by the wrong amount, and the symptom depends on the pattern: searching &lt;code&gt;ABABCABAB&lt;/code&gt; silently loses its match, searching &lt;code&gt;aba&lt;/code&gt; reports a phantom hit at index 1 of &lt;code&gt;abba&lt;/code&gt;, and searching &lt;code&gt;aa&lt;/code&gt; never terminates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advancing the text pointer on a mismatch when &lt;code&gt;j&lt;/code&gt; is above 0.&lt;/strong&gt; The mismatched text character has not been consumed yet — the whole point is to re-test it against the shorter alignment. Advancing past it skips matches starting inside the region you already read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resetting &lt;code&gt;j&lt;/code&gt; to 0 after a full match.&lt;/strong&gt; It looks harmless and breaks overlapping searches: &lt;code&gt;aa&lt;/code&gt; in &lt;code&gt;aaaa&lt;/code&gt; returns two matches instead of three. Use &lt;code&gt;lps[m - 1]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting the whole prefix count as its own border.&lt;/strong&gt; If &lt;code&gt;lps[index]&lt;/code&gt; could equal &lt;code&gt;index + 1&lt;/code&gt;, the fallback &lt;code&gt;length = lps[length - 1]&lt;/code&gt; would never shrink and the build loop would spin forever. "Proper" is doing real work in the definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebuilding the table inside the loop.&lt;/strong&gt; Scanning 10,000 files for one pattern should call &lt;code&gt;build_lps&lt;/code&gt; once, outside the file loop. Calling &lt;code&gt;kmp_search&lt;/code&gt; per file is still correct and still linear, but it throws away the thing preprocessing buys you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting the empty pattern.&lt;/strong&gt; &lt;code&gt;pattern[0]&lt;/code&gt; raises &lt;code&gt;IndexError&lt;/code&gt; on an empty string. Decide what an empty pattern means, then handle it explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add a &lt;code&gt;first_only: bool = False&lt;/code&gt; parameter to &lt;code&gt;kmp_search&lt;/code&gt; that returns as soon as it finds one match, and confirm the comparison count drops on a text with many occurrences.&lt;/li&gt;
&lt;li&gt;Count the overlapping occurrences of &lt;code&gt;aa&lt;/code&gt; in a string of 1,000 &lt;code&gt;a&lt;/code&gt; characters, and explain from the LPS table why the answer is 999.&lt;/li&gt;
&lt;li&gt;Write &lt;code&gt;smallest_period(text)&lt;/code&gt; using &lt;code&gt;len(text) - lps[-1]&lt;/code&gt;, and use it to decide whether a string is some shorter string repeated a whole number of times.&lt;/li&gt;
&lt;li&gt;Decide whether one string is a rotation of another with a single KMP search: check the lengths agree, then search the first inside the second concatenated with itself.&lt;/li&gt;
&lt;li&gt;Extend the matcher so that &lt;code&gt;?&lt;/code&gt; in the pattern matches any character, then explain why the LPS table can no longer be built the same way.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;KMP replaces "slide by one and start over" with "slide by exactly as much as the pattern's own structure permits". The table that says how much is the longest border of every prefix, built in O(m) by the same fallback move the search uses. The result is a search whose text pointer only ever moves right, and a hard O(n + m) bound with no worst case to fear — but also an algorithm that reads every character, which is why skip-based searches beat it on ordinary prose and why Python's own &lt;code&gt;str.find&lt;/code&gt; is built from different parts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n + m) — the bound is the same in every case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n + m)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Worst case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n + m) — at most 2m comparisons building, 2n scanning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Preprocessing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(m) time, on the pattern only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(m) — one integer per pattern character&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text pointer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Never decreases, so it runs on a stream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amortised&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes — fallbacks are paid for by earlier matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;String plus the LPS array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input is repetitive or adversarial, cannot be rewound, or feeds Aho-Corasick&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A C-level &lt;code&gt;str.find&lt;/code&gt; will do, or the text is ordinary prose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aho-Corasick in Snort and Suricata; motif scanning; period detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pattern in text&lt;/code&gt;, &lt;code&gt;text.find(pattern)&lt;/code&gt;, &lt;code&gt;re.finditer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/rabin-karp-string-matching" rel="noopener noreferrer"&gt;Rabin-Karp&lt;/a&gt; — the other linear-average substring search, built from rolling hashes instead of borders.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/tries-prefix-trees" rel="noopener noreferrer"&gt;Tries&lt;/a&gt; — the structure Aho-Corasick hangs its failure links on, and the natural next step from here.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the full treatment of the amortised argument used to prove the 2n bound.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/two-pointers-technique" rel="noopener noreferrer"&gt;The Two Pointers Technique&lt;/a&gt; — the same "each pointer only moves one way" discipline, applied to arrays.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/edit-distance-levenshtein" rel="noopener noreferrer"&gt;Edit Distance&lt;/a&gt; — what to reach for when the match does not have to be exact.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The N-Queens Problem in Python: Backtracking at Its Clearest</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:36:56 +0000</pubDate>
      <link>https://dev.to/bimal-py/the-n-queens-problem-in-python-backtracking-at-its-clearest-5dde</link>
      <guid>https://dev.to/bimal-py/the-n-queens-problem-in-python-backtracking-at-its-clearest-5dde</guid>
      <description>&lt;p&gt;Put eight queens on a chessboard so that none of them can capture another. That is the entire problem, posed by the chess composer Max Bezzel in 1848, and it has outlived almost every other puzzle of its era for one reason: it is the cleanest demonstration of backtracking anyone has found. The general version swaps eight for n — place n queens on an n by n board with no two sharing a row, a column or a diagonal.&lt;/p&gt;

&lt;p&gt;The reason it is worth your time is the gap between the obvious approach and the good one. Choosing 8 squares out of 64 gives 4,426,165,368 boards to test. The good version examines 2,057 partial boards and finds all 92 solutions. Nothing clever was added to get there — the entire improvement comes from two things: describing the board in a way that makes illegal positions unrepresentable, and abandoning a branch the instant it becomes hopeless.&lt;/p&gt;

&lt;p&gt;Be clear about what this is, though. N-queens is a benchmark and a teaching problem, not a production algorithm. If you only need &lt;em&gt;one&lt;/em&gt; solution for large n there are closed-form constructions that place the queens directly in O(n) time with no search at all. Backtracking earns its place when you need every solution, an exact count, or the freedom to bolt extra constraints onto the search.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;A queen attacks along four lines at once: her whole row, her whole column, and both diagonals running through her square. Every square on any of those lines is off limits to every other queen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-attack-lines.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-attack-lines.svg" alt="A 4 by 4 board with a queen on row 1, column 1, showing that she attacks her whole row, her whole column and both diagonals, leaving only four free squares" width="1000" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at how much one queen destroys. On a 4 by 4 board a queen placed near the middle attacks 11 of the other 15 squares, leaving 4. That destructiveness is why the naive search space is such a lie: almost none of it is reachable.&lt;/p&gt;

&lt;p&gt;Start counting anyway, because the numbers are what justify everything that follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;factorial&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;squares&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;comb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;squares&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  boards with n queens dropped anywhere&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;        &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  boards with exactly one queen per row&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;        &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  boards with one per row and one per column&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n = 4:          1,820  boards with n queens dropped anywhere
                  256  boards with exactly one queen per row
                   24  boards with one per row and one per column
n = 6:      1,947,792  boards with n queens dropped anywhere
               46,656  boards with exactly one queen per row
                  720  boards with one per row and one per column
n = 8:  4,426,165,368  boards with n queens dropped anywhere
           16,777,216  boards with exactly one queen per row
               40,320  boards with one per row and one per column
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two observations collapse that first column into the third, and both are worth stating precisely because they do all the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exactly one queen goes in every row.&lt;/strong&gt; There are n queens and n rows, and two queens in the same row would attack each other, so no row can hold two. n queens spread over n rows with at most one each means every row holds exactly one. So a candidate board is fully described by n numbers: for each row, which column its queen sits in. That single change takes 4.4 billion boards down to 16.7 million for n = 8.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No column is used twice either&lt;/strong&gt;, by exactly the same argument. So those n numbers are a &lt;em&gt;permutation&lt;/em&gt; of 0 through n − 1. That takes 16.7 million down to 40,320.&lt;/p&gt;

&lt;p&gt;Everything left is diagonals. And rather than generate all 40,320 permutations and filter them, build the permutation one row at a time and reject as early as possible: place a queen in row 0, then row 1, then row 2, and the moment a row has no legal square, throw away the last queen and try the next column for it instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-backtrack-loop.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-backtrack-loop.svg" alt="The backtracking cycle: place a queen, recurse into the next row, hit a dead end, remove the queen, try the next column" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That removal step is what makes it backtracking rather than plain recursion. The board must be restored to exactly its previous state before the next column is tried, or later branches inherit queens that are no longer there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naming the diagonals
&lt;/h3&gt;

&lt;p&gt;The whole algorithm now rests on one question, asked once per candidate square: &lt;em&gt;is this square attacked by any queen already placed?&lt;/em&gt; If answering it costs a scan over the placed queens, that is O(n) per square and O(n²) per row. There is a much better way.&lt;/p&gt;

&lt;p&gt;Give every diagonal a name. Walk one step down and to the right, and both the row and the column go up by one, so &lt;code&gt;row - col&lt;/code&gt; does not change. That means every square on a &lt;code&gt;\&lt;/code&gt; diagonal shares one value of &lt;code&gt;row - col&lt;/code&gt;, and squares on different &lt;code&gt;\&lt;/code&gt; diagonals never do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-down-diagonals.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-down-diagonals.svg" alt="A 4 by 4 grid with row minus col written in every square, showing the value is constant along each diagonal running down and to the right" width="1000" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other direction works the same way with the other sign. Walk one step down and to the left and the row goes up by one while the column goes down by one, so &lt;code&gt;row + col&lt;/code&gt; does not change. Every &lt;code&gt;/&lt;/code&gt; diagonal has its own constant value of &lt;code&gt;row + col&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-up-diagonals.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-up-diagonals.svg" alt="A 4 by 4 grid with row plus col written in every square, showing the value is constant along each diagonal running down and to the left" width="1000" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On an n by n board &lt;code&gt;row - col&lt;/code&gt; ranges from −(n − 1) to n − 1 and &lt;code&gt;row + col&lt;/code&gt; ranges from 0 to 2n − 2 — 2n − 1 diagonals in each direction, 30 in total for a chessboard. So keep three sets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;used_columns&lt;/code&gt; — the columns already holding a queen.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;used_down_diagonals&lt;/code&gt; — the &lt;code&gt;row - col&lt;/code&gt; values already taken.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;used_up_diagonals&lt;/code&gt; — the &lt;code&gt;row + col&lt;/code&gt; values already taken.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A square at &lt;code&gt;(row, col)&lt;/code&gt; is safe exactly when &lt;code&gt;col&lt;/code&gt;, &lt;code&gt;row - col&lt;/code&gt; and &lt;code&gt;row + col&lt;/code&gt; are all absent from their respective sets. Three set lookups, each O(1) on average because Python hashes small integers to themselves. No scan, no matter how many queens are already down. That is the trick the whole implementation is built on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take n = 4 and follow the search exactly as the code will run it, always trying columns left to right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Row 0, column 0.&lt;/strong&gt; The sets become columns &lt;code&gt;{0}&lt;/code&gt;, down &lt;code&gt;{0}&lt;/code&gt;, up &lt;code&gt;{0}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Row 1.&lt;/strong&gt; Column 0 is a used column. Column 1 has &lt;code&gt;row - col = 0&lt;/code&gt;, already taken by the queen at (0, 0) — they are on the same &lt;code&gt;\&lt;/code&gt; diagonal. Column 2 is clear on all three counts, so the queen goes there. The sets are now columns &lt;code&gt;{0, 2}&lt;/code&gt;, down &lt;code&gt;{0, -1}&lt;/code&gt;, up &lt;code&gt;{0, 3}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Row 2.&lt;/strong&gt; Every square fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Column 0 — the column is used.&lt;/li&gt;
&lt;li&gt;Column 1 — &lt;code&gt;row + col = 3&lt;/code&gt;, which the queen at (1, 2) already owns.&lt;/li&gt;
&lt;li&gt;Column 2 — the column is used.&lt;/li&gt;
&lt;li&gt;Column 3 — &lt;code&gt;row - col = -1&lt;/code&gt;, which the queen at (1, 2) already owns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-first-backtrack.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-first-backtrack.svg" alt="A 4 by 4 board with queens on row 0 column 0 and row 1 column 2, and all four squares of row 2 blocked, each labelled with the constraint that blocks it" width="1000" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is the first backtrack. The search returns to row 1, removes the queen from column 2 — restoring the sets to columns &lt;code&gt;{0}&lt;/code&gt;, down &lt;code&gt;{0}&lt;/code&gt;, up &lt;code&gt;{0}&lt;/code&gt; — and tries column 3 instead.&lt;/p&gt;

&lt;p&gt;From there it gets one row further. Row 2 accepts column 1, then row 3 has nothing left: columns 0, 3 and 1 are used, and column 2 sits on &lt;code&gt;row - col = 1&lt;/code&gt;, which the queen at (2, 1) owns. Back up to row 2 — column 2 lies on the same &lt;code&gt;\&lt;/code&gt; diagonal as the queen at (0, 0) and column 3 is taken, so row 2 is finished too. Row 1 then runs out of columns. The entire subtree under "row 0, column 0" contains no solution, and the search finally moves the first queen to column 1.&lt;/p&gt;

&lt;p&gt;That branch works out immediately: row 1 takes column 3, row 2 takes column 0, row 3 takes column 2. Four queens, no conflicts, first solution found.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-search-tree.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fn-queens-problem-search-tree.svg" alt="The search tree for n equals 4, showing the dead ends under the first column and the path to the first solution under the second" width="1000" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The remaining two branches are mirror images of the first two: column 2 mirrors column 1 and yields the second solution, column 3 mirrors column 0 and yields nothing. Seventeen partial boards examined in total, against 24 permutations and 1,820 raw placements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Start with the version that follows straight from the counting above: generate every permutation of the columns, then check the diagonals. It is short, obviously correct, and a useful reference to test the fast version against.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;permutations&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_safe_arrangement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;True when no two queens in a one-per-row, one-per-column board share a diagonal.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lower&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="c1"&gt;# Two queens share a diagonal when the row gap equals the column gap.
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lower&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;upper&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_by_permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Count solutions by testing every column permutation. Correct, and wasteful.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_safe_arrangement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;count_by_permutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; solutions from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; permutations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n = 4:   2 solutions from     24 permutations
n = 5:  10 solutions from    120 permutations
n = 6:   4 solutions from    720 permutations
n = 7:  40 solutions from  5,040 permutations
n = 8:  92 solutions from 40,320 permutations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those are the right answers — 92 for the classic eight-queens board — and note that the counts are not monotonic: six queens have only 4 solutions where five queens have 10. The problem is spikier than it looks.&lt;/p&gt;

&lt;p&gt;This version dies quickly, though. &lt;code&gt;permutations&lt;/code&gt; is a generator, so it hands the boards over one at a time rather than materialising all of them — but it still hands over every one of the n!, and nothing is judged until all n queens are already placed. n = 12 means 479 million full boards, and n = 15 means over a trillion. Worse, it learns nothing from failure: for n = 8 a permutation starting &lt;code&gt;0, 1&lt;/code&gt; already has two queens on the same diagonal, but the loop still produces all 720 completions of that prefix and tests every one of them from scratch.&lt;/p&gt;

&lt;p&gt;Backtracking fixes precisely that. Build the permutation left to right and check each queen against the ones already placed, so a doomed prefix is abandoned once instead of re-derived thousands of times.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every placement of n queens on an n by n board with no two attacking.

    A solution is a list of n column indices: entry r holds the column of the
    queen in row r. One queen per row is baked into the shape of the answer,
    so the search never considers anything else.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;solutions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# row - col is constant along a "\" line
&lt;/span&gt;    &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;     &lt;span class="c1"&gt;# row + col is constant along a "/" line
&lt;/span&gt;    &lt;span class="n"&gt;placement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Every row is filled, so this partial board is a complete solution.
&lt;/span&gt;            &lt;span class="n"&gt;solutions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;placement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_columns&lt;/span&gt;
                    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;
                    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;placement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;# Undo before trying the next column, so the three sets always
&lt;/span&gt;            &lt;span class="c1"&gt;# describe exactly the queens still standing on the board.
&lt;/span&gt;            &lt;span class="n"&gt;placement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;solutions&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n = 1: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;   n = 2: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;   n = 3: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[1, 3, 0, 2], [2, 0, 3, 1]]
n = 1: [[0]]   n = 2: []   n = 3: []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The two solutions for n = 4 are the ones traced by hand above, in the order the search finds them. The small boards behave correctly with no special cases: one queen on a 1 by 1 board is trivially fine, and 2 and 3 genuinely have no solutions, which the code discovers by exhausting the search rather than by being told.&lt;/p&gt;

&lt;p&gt;A list of column indices is a compact answer but a poor picture, so draw it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Draw one solution: Q for a queen, . for an empty square.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;queen_col&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;queen_col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;solution&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;solution&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n = 4, solution &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: columns &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solution&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;first_eight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;solve_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n = 8, first solution: columns &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;first_eight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_eight&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;n = 4, solution 1: columns [1, 3, 0, 2]
. Q . .
. . . Q
Q . . .
. . Q .
n = 4, solution 2: columns [2, 0, 3, 1]
. . Q .
Q . . .
. . . Q
. Q . .
n = 8, first solution: columns [0, 4, 7, 5, 2, 6, 1, 3]
Q . . . . . . .
. . . . Q . . .
. . . . . . . Q
. . . . . Q . .
. . Q . . . . .
. . . . . . Q .
. Q . . . . . .
. . . Q . . . .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;row&lt;/code&gt; parameter is the recursion depth.&lt;/strong&gt; There is no loop over rows anywhere, because "one queen per row" is expressed by the structure of the recursion: each call handles exactly one row, and &lt;code&gt;place_queen_in(row + 1)&lt;/code&gt; moves to the next. Nothing in the code can produce a board with two queens in a row, so no check for it exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The base case is &lt;code&gt;row == n&lt;/code&gt;.&lt;/strong&gt; Reaching it means all n rows were filled without a conflict, so the current &lt;code&gt;placement&lt;/code&gt; is a solution. There is no validity test at the bottom — every queen was checked before it was placed, so a complete board is correct by construction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;placement.copy()&lt;/code&gt; is load-bearing.&lt;/strong&gt; &lt;code&gt;placement&lt;/code&gt; is one list that the search mutates for the whole run. Appending it directly would store one more reference to that same list every time a solution is found — 92 of them for n = 8 — and the list is emptied again on the way out, so every one of those references ends up pointing at &lt;code&gt;[]&lt;/code&gt;. Copy it, or the function returns a pile of aliases to nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The &lt;code&gt;continue&lt;/code&gt; is the pruning.&lt;/strong&gt; When a square fails the three-set test, no queen is placed and no recursion happens, so every board that would have grown out of that square is skipped. That is the entire performance story — the whole subtree disappears from one &lt;code&gt;if&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The three &lt;code&gt;remove&lt;/code&gt; calls are the backtrack.&lt;/strong&gt; They mirror the three &lt;code&gt;add&lt;/code&gt; calls, undoing them in reverse order. The order does not matter functionally — the three sets are independent — but mirroring makes it obvious that nothing was missed. Forget one and the sets slowly fill with ghosts, and the search silently reports too few solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The nested function closes over the sets&lt;/strong&gt;, so every recursive call shares one copy of each. Passing copies down would also be correct, and would cost O(n) per call for nothing. Depth is only n + 1 frames, so Python's 1,000-frame recursion limit never binds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;The honest headline: the upper bound is loose, the real cost is much smaller, and nobody can state the real cost exactly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The upper bound.&lt;/strong&gt; Because the search fixes one queen per row and never repeats a column, the nodes at depth k are at most n × (n − 1) × … × (n − k + 1), which is n! / (n − k)!. Summing over all depths gives a total node count of at most&lt;/p&gt;

&lt;p&gt;n! × (1/0! + 1/1! + … + 1/n!), which is less than e × n! ≈ 2.72 n!.&lt;/p&gt;

&lt;p&gt;Each node loops over n columns and does three O(1) set lookups per column, so the work per node is O(n). Multiply: &lt;strong&gt;O(n × n!)&lt;/strong&gt; in the worst case, and that is genuinely an upper bound rather than a description of what happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happens.&lt;/strong&gt; The pruning removes most of that tree, and there is no known closed form for how much. So measure it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return (number of solutions, number of partial boards the search examined).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;boards_examined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;boards_examined&lt;/span&gt;
        &lt;span class="n"&gt;boards_examined&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_columns&lt;/span&gt;
                    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;
                    &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;continue&lt;/span&gt;

            &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_up_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_down_diagonals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;used_columns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;place_queen_in&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;boards_examined&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;solutions&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;boards examined&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;solutions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;count_n_queens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;solutions&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;boards&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  n  solutions  boards examined             n!
  4          2               17             24
  5         10               54            120
  6          4              153            720
  7         40              552          5,040
  8         92            2,057         40,320
  9        352            8,394        362,880
 10        724           35,539      3,628,800
 11      2,680          166,926     39,916,800
 12     14,200          856,189    479,001,600
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At n = 12 the bound allows about 1.3 billion nodes and the search examines 856,189 — roughly 1,500 times fewer. But watch the ratios down that middle column: 2,057, then 8,394, then 35,539, then 166,926, then 856,189. Each step multiplies the work by more than the step before it: 4.1×, then 4.2×, then 4.7×, then 5.1×. Pruning changed the constant and the base, not the shape: the growth is still worse than exponential. On this code n = 14 already takes minutes and n = 15 tens of minutes, and n = 20 is out of reach no matter how long you wait.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(n).&lt;/strong&gt; The recursion is n + 1 frames deep and the three sets hold at most n entries each. &lt;code&gt;count_n_queens&lt;/code&gt; therefore runs in linear space no matter how astronomically many solutions it counts. &lt;code&gt;solve_n_queens&lt;/code&gt; is different — it stores every solution, so its memory is O(n × number of solutions), which for n = 12 is 14,200 lists of 12 integers. If you only want the count, count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One more honest note:&lt;/strong&gt; counting solutions is far harder than finding one. The exact totals are known only up to n = 27, and that value was produced in 2016 by a distributed FPGA project that ran for about a year. No formula for the sequence is known.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use backtracking for n-queens when&lt;/strong&gt; you want all solutions, an exact count, or a solution satisfying extra constraints you invented ("no queen on the main diagonal", "these three squares must be occupied"). It is also the right shape for the whole family of constraint puzzles — Sudoku, graph colouring, crosswords, the knight's tour — where the three-set pattern generalises directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it to find a single solution for large n.&lt;/strong&gt; Explicit constructions exist that place n non-attacking queens for every n ≥ 4 in O(n) time by writing the column indices from a formula, with no search whatsoever. If all you need is one valid board, searching for it is the wrong tool by an enormous margin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it for very large n even with the constraints relaxed.&lt;/strong&gt; Local search beats it badly here: the min-conflicts heuristic starts from a random full board, picks a queen that is currently under attack, and moves it within its own column to the square that conflicts with the fewest others. It solves the million-queens problem in around fifty moves on average. Backtracking cannot get past a few dozen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not hand-roll it when the constraints get complicated.&lt;/strong&gt; Once the rules stop being three simple sets, a real constraint solver — Google's OR-Tools CP-SAT, or a SAT solver — will out-prune anything you write by hand, because it learns from conflicts instead of just backing up one level.&lt;/p&gt;

&lt;p&gt;And if the search tree has &lt;em&gt;overlapping&lt;/em&gt; subproblems rather than merely infeasible ones, backtracking is the wrong family entirely; that is what &lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;dynamic programming&lt;/a&gt; is for. N-queens has a trace of overlap: on an 8 by 8 board the prefixes &lt;code&gt;1, 3, 0, 2&lt;/code&gt; and &lt;code&gt;2, 0, 3, 1&lt;/code&gt; block exactly the same columns and the same diagonals, so rows 4 through 7 face an identical subproblem. There is far too little of it to pay for a memo table, though. The key would have to be a set of columns plus two sets of diagonals, and that key repeats so rarely that the cache needs an entry for very nearly every node — for which it cuts n = 8 from 2,057 nodes to 1,999, and n = 12 from 856,189 to 821,299. Under five per cent of the work saved, for a table almost as large as the search itself. Plain backtracking stays the right family here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;Not as itself. Nobody ships software that places queens on chessboards.&lt;/p&gt;

&lt;p&gt;What it has instead is a real career as a proving ground. Niklaus Wirth built his 1971 paper &lt;em&gt;Program Development by Stepwise Refinement&lt;/em&gt; — one of the founding documents of structured programming — around the eight queens problem, and the modern shape of this algorithm is essentially his. Constraint toolkits including Google's OR-Tools and MiniZinc ship it as a standard example, because everyone already knows the answers, so a new solver can be checked against them. Russell and Norvig's &lt;em&gt;Artificial Intelligence: A Modern Approach&lt;/em&gt; uses it as the running example for local search, which is where the million-queens figure comes from.&lt;/p&gt;

&lt;p&gt;The transferable part is the pattern, and that does ship. A Sudoku solver is this algorithm with different bookkeeping: three collections of used digits, one per row, one per column and one per 3 by 3 box, with the same place-recurse-undo cycle around them. Once you can write n-queens without thinking, you can write that in twenty minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to undo.&lt;/strong&gt; Leave out one of the three &lt;code&gt;remove&lt;/code&gt; calls and the sets fill with queens that are no longer on the board. Nothing crashes; the search just reports too few solutions. Drop the down-diagonal &lt;code&gt;remove&lt;/code&gt; and n = 8 comes back as 0 rather than 92, because the ghosts block every branch the search tries after its first retreat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storing the live list.&lt;/strong&gt; &lt;code&gt;solutions.append(placement)&lt;/code&gt; instead of &lt;code&gt;solutions.append(placement.copy())&lt;/code&gt; stores the same object every time. You get one reference per solution — 92 of them at n = 8 — all pointing at the same list, and that list is empty by the time the search returns, so the answer prints as ninety-two copies of &lt;code&gt;[]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the same expression for both diagonals.&lt;/strong&gt; If &lt;code&gt;used_down&lt;/code&gt; is also fed &lt;code&gt;row + col&lt;/code&gt;, the code compiles, runs, and reports 2,113 solutions for n = 8 — it is now only checking columns and one diagonal direction. Always check n = 8 against 92; it is the cheapest regression test in the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sizing the diagonal arrays by n.&lt;/strong&gt; Swapping the sets for boolean lists is a reasonable optimisation, but there are 2n − 1 diagonals in each direction, not n. Allocating &lt;code&gt;[False] * n&lt;/code&gt; and indexing it by &lt;code&gt;row - col&lt;/code&gt; raises nothing, because Python reads negative indices from the end — diagonal v and diagonal v − n end up sharing a slot, so the search blocks squares it should not and under-counts. That does not fail loudly and it does not fail uniformly: n = 8 comes back as 0 instead of 92 and n = 9 as 116 instead of 352, but n = 5 comes back as 10, which is the correct answer. Test the bug on a small odd board and it passes. A list of length exactly &lt;code&gt;2 * n - 1&lt;/code&gt; does work, since the negative wrap covers exactly one period, but &lt;code&gt;used_down[row - col + n - 1]&lt;/code&gt; on the same list is the version you can reread in six months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scanning the placed queens for every candidate square.&lt;/strong&gt; Looping over &lt;code&gt;placement&lt;/code&gt; and testing &lt;code&gt;abs(row - other_row) == abs(col - other_col)&lt;/code&gt; is correct and is what the permutation version does. It also turns an O(1) check into an O(n) one, which multiplies the whole runtime by n for no benefit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checking rows.&lt;/strong&gt; Some implementations carry a &lt;code&gt;used_rows&lt;/code&gt; set. It can never fire — the recursion visits each row exactly once — so it is pure overhead, and its presence usually signals a misunderstanding of why the one-queen-per-row rule was free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add an early return so the search stops at the first solution instead of enumerating all of them, and confirm it returns &lt;code&gt;[1, 3, 0, 2]&lt;/code&gt; for n = 4.&lt;/li&gt;
&lt;li&gt;Replace the three sets with three boolean lists, remembering the &lt;code&gt;+ n - 1&lt;/code&gt; shift for the down diagonals, and check that every count from n = 4 to n = 10 is unchanged.&lt;/li&gt;
&lt;li&gt;Print how many of the 92 eight-queens solutions have a queen in the corner square, using the rendering function to spot-check a few by eye.&lt;/li&gt;
&lt;li&gt;For even n, restrict the queen in row 0 to the left half of the board and double the resulting count; verify it matches the full search for n = 8, 10 and 12, and work out why the trick needs care for odd n.&lt;/li&gt;
&lt;li&gt;Reuse the place-recurse-undo skeleton to solve a 9 by 9 Sudoku, tracking used digits per row, per column and per 3 by 3 box.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;N-queens is the problem to reach for when you want to understand backtracking, because every part of it is visible. The search space shrinks from 4.4 billion to 40,320 through two sentences of reasoning, the conflict test collapses to three O(1) lookups because &lt;code&gt;row - col&lt;/code&gt; and &lt;code&gt;row + col&lt;/code&gt; name the diagonals, and the pruning turns an intractable bound into 2,057 examined boards. Learn the three-set trick properly; you will use it again the next time a puzzle has "no two of these may share a line" in its rules.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time, upper bound&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n × n!) — fewer than e × n! partial boards, O(n) work at each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time, measured&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;2,057 boards at n = 8; 856,189 at n = 12, about 1,500× below the bound&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — n + 1 recursion frames plus three sets of at most n entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space to list all solutions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n × number of solutions) — 14,200 lists of 12 ints at n = 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conflict check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(1) — three set lookups, never a scan of placed queens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key insight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One queen per row; &lt;code&gt;row - col&lt;/code&gt; and &lt;code&gt;row + col&lt;/code&gt; identify the two diagonals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Three sets plus one list of column indices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need all solutions, an exact count, or custom extra constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need one solution for large n — use the O(n) construction or min-conflicts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A benchmark for constraint and SAT solvers; the pattern behind Sudoku solvers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None in the standard library; &lt;code&gt;itertools.permutations&lt;/code&gt; gives the brute force&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/recursion-and-backtracking" rel="noopener noreferrer"&gt;Recursion and Backtracking in Python&lt;/a&gt; — the mental model this post applies, built from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/depth-first-search" rel="noopener noreferrer"&gt;Depth-First Search&lt;/a&gt; — the same traversal, run over an explicit graph instead of an implicit search tree.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/hash-tables" rel="noopener noreferrer"&gt;Hash Tables in Python&lt;/a&gt; — why those three set lookups really are O(1), and when they are not.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;Dynamic Programming Explained&lt;/a&gt; — what to do instead when the branches of the search overlap.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting argument behind the n! bound above.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Recursion and Backtracking in Python: Building the Mental Model</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:36:25 +0000</pubDate>
      <link>https://dev.to/bimal-py/recursion-and-backtracking-in-python-building-the-mental-model-1gid</link>
      <guid>https://dev.to/bimal-py/recursion-and-backtracking-in-python-building-the-mental-model-1gid</guid>
      <description>&lt;p&gt;A recursive function is one that calls itself. That sentence is the whole definition, and it is also why recursion confuses people: it sounds circular, like a dictionary that defines "recursion" as "see recursion". It is not circular, because every call works on a smaller problem than the one that made it, and because there is always a smallest problem the function answers outright instead of asking again.&lt;/p&gt;

&lt;p&gt;Once that clicks, a large part of this series stops being a list of tricks. Merge sort, quick sort, tree traversal, depth-first search, dynamic programming, divide and conquer, and every puzzle solver ever written are the same shape: solve the trivial case directly, break everything else into smaller versions of itself, combine the answers.&lt;/p&gt;

&lt;p&gt;Two halves follow. First plain recursion: the base case, the call stack drawn frame by frame, why a missing base case crashes instead of hanging, and where Python's limits sit. Then backtracking, which is recursion plus one move — undo the choice you just made and try the next one. That single addition is how you enumerate every subset, every permutation, every legal Sudoku grid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;A recursive function has exactly two parts, and if either one is missing the function is broken.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The base case.&lt;/strong&gt; A version of the problem so small that you answer it directly, with no further calls. &lt;code&gt;factorial(1)&lt;/code&gt; is 1. An empty list has length 0. A leaf node has no children to visit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The recursive case.&lt;/strong&gt; Everything else. You express the answer in terms of the same function applied to something strictly smaller, then do the small amount of work that turns that answer into yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Factorial is the standard first example because it is defined recursively in mathematics before anyone thinks about code. &lt;code&gt;4!&lt;/code&gt; means 4 × 3 × 2 × 1, which is the same as 4 × &lt;code&gt;3!&lt;/code&gt;. In general &lt;code&gt;n! = n × (n - 1)!&lt;/code&gt;, and &lt;code&gt;1! = 1&lt;/code&gt; stops it.&lt;/p&gt;

&lt;p&gt;The part beginners find genuinely hard is not writing that down. It is believing that the machine can keep track of four half-finished multiplications at once. It can, because of the &lt;strong&gt;call stack&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every time a Python function is called, the interpreter allocates a &lt;em&gt;frame&lt;/em&gt;: a small block of memory holding that call's arguments, its local variables, and the place to resume once it returns. Frames pile up. The frame at the top is the one currently running; everything below it is paused, waiting for the value it asked for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-factorial-stack.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-factorial-stack.svg" alt="Four stacked call frames during factorial(4), with the top frame at the base case and the three below it paused mid-multiplication" width="1000" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That picture is the entire mystery. &lt;code&gt;factorial(4)&lt;/code&gt; cannot finish its multiplication until it knows &lt;code&gt;factorial(3)&lt;/code&gt;, so it sits there holding the number 4 while a new frame runs. Four frames exist at once, each with its own private &lt;code&gt;n&lt;/code&gt;. Nothing is shared, nothing is overwritten.&lt;/p&gt;

&lt;p&gt;Two rules follow, and every recursion bug is a violation of one of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The base case must exist and must be reachable.&lt;/strong&gt; Not just present in the source — actually reached by the arguments you pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every recursive call must move strictly closer to it.&lt;/strong&gt; &lt;code&gt;factorial(n - 1)&lt;/code&gt; shrinks. &lt;code&gt;factorial(n)&lt;/code&gt; does not. Neither does &lt;code&gt;factorial(n - 1)&lt;/code&gt; when &lt;code&gt;n&lt;/code&gt; is negative and the base test is &lt;code&gt;n == 1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Trace &lt;code&gt;factorial(4)&lt;/code&gt; by hand. Going down, each call defers its multiplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;factorial(4) = 4 * factorial(3)      frame 1 opens, holds 4
factorial(3) = 3 * factorial(2)      frame 2 opens, holds 3
factorial(2) = 2 * factorial(1)      frame 3 opens, holds 2
factorial(1) = 1                     frame 4 hits the base case
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing has been multiplied yet. Four frames are open and three of them are stuck on a half-written expression. Then the base case returns and the stack unwinds, in exactly the reverse order:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-unwind.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-unwind.svg" alt="The four frames returning in reverse order, each multiplying its own n by the value returned from above" width="1000" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;factorial(1)&lt;/code&gt; hands 1 back to &lt;code&gt;factorial(2)&lt;/code&gt;, which finally computes 2 × 1 = 2 and hands that to &lt;code&gt;factorial(3)&lt;/code&gt;, which computes 3 × 2 = 6, which lets &lt;code&gt;factorial(4)&lt;/code&gt; compute 4 × 6 = 24. The work happens on the way &lt;em&gt;up&lt;/em&gt;, not on the way down. That asymmetry is worth remembering — it is why a recursive function can build an answer in an order you never explicitly wrote.&lt;/p&gt;

&lt;p&gt;Fibonacci changes the picture in one important way: the recursive case calls itself &lt;strong&gt;twice&lt;/strong&gt;. &lt;code&gt;fib(n)&lt;/code&gt; is &lt;code&gt;fib(n - 1) + fib(n - 2)&lt;/code&gt;, with &lt;code&gt;fib(0) = 0&lt;/code&gt; and &lt;code&gt;fib(1) = 1&lt;/code&gt;. One call per level becomes a branching tree of calls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-fib-frames.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-fib-frames.svg" alt="The call tree for fib(5), with the five frames that are live at one instant highlighted along the leftmost path" width="1000" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fifteen calls in total to compute &lt;code&gt;fib(5)&lt;/code&gt;. But look at the highlighted chain: at the instant the leftmost &lt;code&gt;fib(1)&lt;/code&gt; is running, only five frames exist. The other ten calls have not started yet. That distinction matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Total calls&lt;/strong&gt; determine the running time. For &lt;code&gt;fib&lt;/code&gt; they explode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum depth&lt;/strong&gt; determines the memory, because that is the tallest the stack ever gets. For &lt;code&gt;fib&lt;/code&gt; it is only n.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Count them separately, because they diverge. &lt;code&gt;fib(30)&lt;/code&gt; makes 2,692,537 calls in a stack only 30 frames tall and runs fine; &lt;code&gt;factorial(2000)&lt;/code&gt; makes 2,000 calls in a stack 2,000 frames tall and crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Factorial first, exactly as described:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;n! computed from its own definition: n times the factorial below it.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# base case: 0! and 1! are both 1, and neither needs recursion
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# recursive case: the same job, one size smaller
&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;24
[1, 1, 2, 6, 24, 120, 720]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not yet believe the stack diagram, make the function narrate itself. Indentation here is literally the stack depth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;factorial_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;factorial again, announcing each frame as it opens and as it returns.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;pad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;call factorial(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;base case, return 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;factorial_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pad&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;return &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; * factorial(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;factorial_traced&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;call factorial(4)
  call factorial(3)
    call factorial(2)
      call factorial(1)
      base case, return 1
    return 2 * factorial(1) = 2
  return 3 * factorial(2) = 6
return 4 * factorial(3) = 24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four calls down, four returns up, and every &lt;code&gt;return&lt;/code&gt; line is a frame finishing the multiplication it started. Printing a traced version of a recursion you do not understand is the fastest debugging technique in this entire series.&lt;/p&gt;

&lt;p&gt;Now Fibonacci, with a second function that counts the calls instead of guessing at them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The nth Fibonacci number, written straight from the definition.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# fib(0) = 0 and fib(1) = 1 are given, not computed
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fib_call_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Total calls the function above makes while computing fib(n).&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fib_call_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fib_call_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fib(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;) = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;   calls: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;fib_call_count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;   deepest stack: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fib( 5) =      5   calls:        15   deepest stack: 5
fib(10) =     55   calls:       177   deepest stack: 10
fib(20) =   6765   calls:     21891   deepest stack: 20
fib(30) = 832040   calls:   2692537   deepest stack: 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding ten to n multiplies the calls by about 123 while the stack grows by ten frames. Correct, and unusable past about n = 35. The fix is not to abandon recursion — it is to stop recomputing answers you already have, which is &lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;dynamic programming&lt;/a&gt;, and in Python it is one decorator: &lt;code&gt;functools.lru_cache&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The base test comes first.&lt;/strong&gt; Always. If the recursive call is written above the base test, the call happens before the test can stop it, and nothing stops it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;n - 1&lt;/code&gt; is the contract.&lt;/strong&gt; Each call receives an argument strictly closer to the base case, and the base case catches everything at or below the boundary. Writing &lt;code&gt;if n == 1&lt;/code&gt; instead of &lt;code&gt;if n &amp;lt;= 1&lt;/code&gt; looks equivalent and is not: &lt;code&gt;factorial(0)&lt;/code&gt; then recurses to −1, −2, −3 and never matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The return value is the only channel.&lt;/strong&gt; Each frame gets one value back from the frame above it and is responsible for turning that into its own answer. &lt;code&gt;factorial&lt;/code&gt; multiplies. &lt;code&gt;fib&lt;/code&gt; adds. Merge sort merges. That final combining step is where a divide-and-conquer algorithm actually does its work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;fib&lt;/code&gt; is exponential because the branches overlap, not because it recurses.&lt;/strong&gt; &lt;code&gt;fib(5)&lt;/code&gt; computes &lt;code&gt;fib(3)&lt;/code&gt; twice, &lt;code&gt;fib(2)&lt;/code&gt; three times, &lt;code&gt;fib(1)&lt;/code&gt; five times. Recursion is not slow. Recomputation is slow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where recursion breaks
&lt;/h2&gt;

&lt;p&gt;Delete the base case and something interesting happens: the program does not hang. It stops, quickly, with an exception.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;countdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;No base case: every call makes another call, one frame deeper.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;countdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;countdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RecursionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;countdown(5) stopped with RecursionError - it never hung&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frames allowed by default:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getrecursionlimit&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;countdown(5) stopped with RecursionError - it never hung
frames allowed by default: 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An infinite &lt;em&gt;loop&lt;/em&gt; spins forever using no extra memory. An infinite &lt;em&gt;recursion&lt;/em&gt; consumes a frame per call, so CPython counts frames and raises &lt;code&gt;RecursionError&lt;/code&gt; at 1000 of them. That limit is a guard rail, not a law of nature — &lt;code&gt;sys.setrecursionlimit(20000)&lt;/code&gt; raises it — but raise it only when the depth is genuinely bounded and merely larger than 1000. Frames are real memory, and on older CPython builds each Python call also consumed C stack, so a limit set high enough crashes the interpreter outright instead of raising a catchable exception.&lt;/p&gt;

&lt;p&gt;The ceiling has one blunt consequence: &lt;strong&gt;a recursion whose depth grows with input size is a bug waiting for a big input.&lt;/strong&gt; Walking a linked list of 5,000 nodes recursively passes your tests and dies in production.&lt;/p&gt;

&lt;p&gt;Functional languages solve this with &lt;strong&gt;tail-call optimisation&lt;/strong&gt;. A call is in tail position when it is the entire return expression — nothing is left to do after it comes back — so the compiler can reuse the current frame instead of stacking a new one. Scheme guarantees it. Python does not do it at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sum_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Tail recursive: the recursive call is the entire return expression.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;RecursionError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sum_to(5000) overflows anyway - Python keeps every frame&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sum_to_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same arithmetic as a loop: one frame, any n.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum_to_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum_to_loop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;320400
sum_to(5000) overflows anyway - Python keeps every frame
12502500
500000500000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sum_to&lt;/code&gt; is textbook tail recursion and it still blows the stack, while the loop handles a million in one frame. Guido van Rossum, Python's creator, wrote about this on his Neopythonic blog in 2009 and rejected the feature deliberately, not for lack of time. His main objection was debuggability: eliminating frames destroys the traceback, and a stack trace that has silently dropped its middle is a much worse tool than a slightly slower program. He also argued that Python is not a functional language, that iteration is the idiomatic way to loop, and that programmers should not have to reason about whether a call is in tail position to know whether their code will crash.&lt;/p&gt;

&lt;p&gt;So in Python, the rule is blunt. &lt;strong&gt;Use recursion when the depth is naturally small&lt;/strong&gt; — the height of a balanced tree, the number of digits in a number, log n levels of divide and conquer. &lt;strong&gt;Use a loop or an explicit &lt;a href="https://bimalkhatri.com.np/blogs/stacks" rel="noopener noreferrer"&gt;stack&lt;/a&gt; when the depth scales with the data&lt;/strong&gt; — which is exactly why the iterative version of &lt;a href="https://bimalkhatri.com.np/blogs/depth-first-search" rel="noopener noreferrer"&gt;depth-first search&lt;/a&gt; exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backtracking: choose, explore, un-choose
&lt;/h2&gt;

&lt;p&gt;Everything above computes a single value. Backtracking answers a different kind of question: &lt;em&gt;enumerate every arrangement that satisfies some constraints&lt;/em&gt;, or &lt;em&gt;find one that does&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The mechanism is three lines, repeated at every level:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose.&lt;/strong&gt; Commit to one option at the current position.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore.&lt;/strong&gt; Recurse to fill the next position, given that commitment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un-choose.&lt;/strong&gt; When the recursion returns, take the commitment back, so the next option starts from a clean state.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The un-choose step is the whole trick, and it is what makes this backtracking rather than plain recursion. The recursion explores one branch to its very end, then rewinds to the last decision point and takes the other road — depth-first search over a tree of decisions that you never build in memory. The tree exists only as the sequence of calls.&lt;/p&gt;

&lt;p&gt;Start with subsets. To list every subset of &lt;code&gt;[1, 2, 3]&lt;/code&gt; you decide, for each element in turn, take it or leave it. Three elements, two choices each, 2³ = 8 subsets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-subset-tree.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-subset-tree.svg" alt="The decision tree for subsets of 1, 2, 3, where each level takes or skips one number and each leaf is a finished subset" width="1000" height="424"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every subset of items, by taking or leaving each element in turn.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# copy, because path keeps changing
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;  &lt;span class="c1"&gt;# choose items[index]
&lt;/span&gt;        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# explore every ending that includes it
&lt;/span&gt;        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                 &lt;span class="c1"&gt;# un-choose, putting path back as it was
&lt;/span&gt;        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;         &lt;span class="c1"&gt;# explore every ending that leaves it out
&lt;/span&gt;
    &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]
32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the output against the diagram: the leaves come out left to right, take-branch first. &lt;code&gt;index == len(items)&lt;/code&gt; is the base case — every element has been decided, so the path is a finished subset. &lt;code&gt;path.append&lt;/code&gt; is the choice, &lt;code&gt;path.pop&lt;/code&gt; is the undo, and the two &lt;code&gt;explore&lt;/code&gt; calls are the two branches out of every node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permutations, and one list rewound
&lt;/h2&gt;

&lt;p&gt;Permutations are the same pattern with a different constraint. Instead of two choices per position, every unused element is a candidate, and each element must be used exactly once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;permutations&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stdlib_permutations&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every ordering of items, choosing one unused element per position.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;  &lt;span class="c1"&gt;# each element fills exactly one position
&lt;/span&gt;                &lt;span class="k"&gt;continue&lt;/span&gt;
            &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;   &lt;span class="c1"&gt;# choose
&lt;/span&gt;            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;            &lt;span class="c1"&gt;# explore
&lt;/span&gt;            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;# un-choose, both halves of the state
&lt;/span&gt;            &lt;span class="n"&gt;used&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;


&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;])))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;stdlib_permutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
720
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last &lt;code&gt;True&lt;/code&gt; says the hand-written function produces the same orderings in the same order as &lt;code&gt;itertools.permutations&lt;/code&gt;. In real code use the standard library version: it is written in C and yields results lazily instead of building a list of 720. Write your own when the constraints are custom, which is exactly when backtracking earns its place.&lt;/p&gt;

&lt;p&gt;Notice that &lt;strong&gt;two&lt;/strong&gt; things get undone in the loop: &lt;code&gt;path.pop()&lt;/code&gt; and &lt;code&gt;used[index] = False&lt;/code&gt;. Every piece of state you mutate on the way down has to be restored on the way up. Miss one and later branches inherit a corrupted world.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-path-undo.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-path-undo.svg" alt="The shared path list growing and shrinking across seven steps as choices are made, recorded and undone" width="1000" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That diagram answers the question everyone asks eventually: &lt;strong&gt;when do you copy, and when do you mutate and undo?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mutate and undo the working state.&lt;/strong&gt; There is one &lt;code&gt;path&lt;/code&gt; list for the entire search, and one &lt;code&gt;used&lt;/code&gt; array. Appending is amortised O(1), popping is O(1), and no new list is allocated per node. This is why backtracking is fast despite visiting a huge number of nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy when you record an answer.&lt;/strong&gt; &lt;code&gt;result.append(list(path))&lt;/code&gt; makes a snapshot. Writing &lt;code&gt;result.append(path)&lt;/code&gt; instead stores a reference to the one shared list, which keeps changing — so at the end every entry in &lt;code&gt;result&lt;/code&gt; is the same object, and &lt;code&gt;subsets([1, 2, 3])&lt;/code&gt; returns eight empty lists. This is the single most common backtracking bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy when you pass state down&lt;/strong&gt; only if you cannot undo it cleanly. Passing &lt;code&gt;path + [value]&lt;/code&gt; into the recursive call is correct and much easier to reason about, but it allocates a new list at every node, which turns O(1) per node into O(n).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pruning: not exploring what cannot work
&lt;/h2&gt;

&lt;p&gt;So far the search visits everything. Backtracking becomes powerful when you check the constraints &lt;em&gt;during&lt;/em&gt; the descent and abandon a branch the moment it cannot lead to a solution. Every node you skip takes its whole subtree with it.&lt;/p&gt;

&lt;p&gt;Take a concrete problem: given twelve parcel weights, find every subset weighing exactly 60 kg. The plain version decides take-or-leave for each parcel and checks the total at the leaf. The pruned version stops at any node where the running total has already passed 60 (weights are positive, so it can only grow) or where everything still undecided adds up to less than what is still needed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-pruned-tree.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Frecursion-and-backtracking-pruned-tree.svg" alt="The search tree for weights 5, 4, 3, 2 and target 6, with five branches cut early and one leaf reaching the target" width="1000" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram uses four weights so it fits on a page. &lt;code&gt;[5, 4, 3, 2]&lt;/code&gt; with target 6 has a full tree of 31 nodes; the five abandoned branches shown remove 16 of them, leaving 15 visited, and the single solution &lt;code&gt;[4, 2]&lt;/code&gt; is still found. Here are both versions on the twelve-parcel problem, counting nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;PARCELS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subsets_summing_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every subset that sums to target, exploring all 2**n leaves.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;
        &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subsets_summing_to_pruned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The same answers, abandoning branches that provably cannot work.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# remaining[index] is the sum of everything from index onwards: the most
&lt;/span&gt;    &lt;span class="c1"&gt;# that can still be added once the first index parcels are decided.
&lt;/span&gt;    &lt;span class="n"&gt;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;nonlocal&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;
        &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# weights are positive, so the total only grows
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;remaining&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# not enough left to reach target
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;explore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plain_nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subsets_summing_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PARCELS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pruned&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pruned_nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subsets_summing_to_pruned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PARCELS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;target &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: nodes &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;plain_nodes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pruned_nodes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with pruning, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;solutions &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, identical answers: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pruned&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subsets_summing_to_pruned&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the diagram&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s tree: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; found in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; nodes instead of 31&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;target 60: nodes 8191 -&amp;gt; 929 with pruning, solutions 9, identical answers: True
target 25: nodes 8191 -&amp;gt; 141 with pruning, solutions 1, identical answers: True
the diagram's tree: [[4, 2]] found in 15 nodes instead of 31
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same answers, 8,191 nodes down to 929. Target 25 does better still, at 141 nodes, because the first parcel weighs 31: taking it is already over target, so the take-it half of the tree collapses to the single node that gets rejected at depth 1 and its 4,094 descendants are never visited. Pruning pays most when it fires near the root, where the subtrees it discards are largest.&lt;/p&gt;

&lt;p&gt;Be honest about what pruning buys, though. It does not change the growth rate. There are still 2ⁿ subsets, and an input where nothing can be ruled out early walks the whole tree. Pruning turns "impossible" into "fast enough for the inputs I actually have", which is usually what you need and never a guarantee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;For any recursion, two independent quantities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time = number of calls × work per call.&lt;/strong&gt; Count the calls with the recurrence the function itself describes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;factorial(n)&lt;/code&gt; makes exactly n calls, each doing one multiplication: &lt;strong&gt;O(n)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fib(n)&lt;/code&gt; makes &lt;code&gt;2 × fib(n + 1) − 1&lt;/code&gt; calls. Fibonacci numbers grow like φⁿ where φ ≈ 1.618, so this is &lt;strong&gt;O(1.618ⁿ)&lt;/strong&gt;, loosely quoted as O(2ⁿ). The measured numbers above confirm it: 21,891 calls at n = 20 and 2,692,537 at n = 30, a factor of 123 for ten extra terms, and 1.618¹⁰ ≈ 123.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subsets(items)&lt;/code&gt; visits 2ⁿ⁺¹ − 1 nodes — a full binary tree of n + 1 levels — and copies an average of n/2 elements at each of its 2ⁿ leaves. The copying dominates: &lt;strong&gt;O(n · 2ⁿ)&lt;/strong&gt;. You cannot do better, because the output itself contains that many numbers.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;permutations(items)&lt;/code&gt; produces n! results and scans all n candidates at each of the n levels: &lt;strong&gt;O(n · n!)&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Space = maximum stack depth × frame size, plus the shared state.&lt;/strong&gt; This is where beginners overestimate the cost.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;factorial(n)&lt;/code&gt; and &lt;code&gt;sum_to(n)&lt;/code&gt; reach depth n: &lt;strong&gt;O(n)&lt;/strong&gt; stack. That is the reason both hit &lt;code&gt;RecursionError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fib(n)&lt;/code&gt; reaches depth n despite making exponentially many calls: &lt;strong&gt;O(n)&lt;/strong&gt; stack.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subsets&lt;/code&gt; and &lt;code&gt;permutations&lt;/code&gt; reach depth n and hold one &lt;code&gt;path&lt;/code&gt; of at most n elements: &lt;strong&gt;O(n)&lt;/strong&gt; working space, plus whatever the collected results occupy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For divide-and-conquer recursions that halve the input — &lt;a href="https://bimalkhatri.com.np/blogs/binary-search" rel="noopener noreferrer"&gt;binary search&lt;/a&gt;, &lt;a href="https://bimalkhatri.com.np/blogs/merge-sort" rel="noopener noreferrer"&gt;merge sort&lt;/a&gt; — the depth is log₂ n, which is 20 for a million items and 30 for a billion. That is why those algorithms recurse safely in Python and a linked-list walk does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use recursion when the problem is defined in terms of itself and the depth is bounded.&lt;/strong&gt; Trees, nested structures like JSON, grammars, divide and conquer. A recursive tree traversal is six lines and obviously correct; the iterative one needs a stack, a visited marker and careful ordering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use backtracking when you must search combinations under constraints&lt;/strong&gt; and there is no formula, no greedy rule and no polynomial algorithm. Sudoku, &lt;a href="https://bimalkhatri.com.np/blogs/n-queens-problem" rel="noopener noreferrer"&gt;N-Queens&lt;/a&gt;, crossword filling, timetabling, exact set cover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use recursion when the depth scales with input size&lt;/strong&gt; past a few hundred; convert it to a loop or keep an explicit list as your stack. &lt;strong&gt;Do not use plain recursion when subproblems repeat&lt;/strong&gt; — add &lt;code&gt;functools.lru_cache&lt;/code&gt; or rewrite it as dynamic programming, which is the difference between 2,692,537 calls and 31 distinct subproblems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use backtracking when a cheaper structure exists.&lt;/strong&gt; Subset-sum over small integers is a dynamic programming table. Shortest paths are Dijkstra. Permutations are &lt;code&gt;itertools&lt;/code&gt;. Backtracking should be a considered choice, not a reflex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Parsers.&lt;/strong&gt; CPython's own parser is recursive descent: PEP 617 replaced the old LL(1) grammar with a PEG parser in Python 3.9, and PEG parsing backtracks by design, trying alternatives in order and rewinding the input position when one fails. The standard library's JSON decoder is recursive too, in both its C and its pure-Python scanner, which is why &lt;code&gt;json.loads&lt;/code&gt; on a deeply nested document raises &lt;code&gt;RecursionError&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regular expressions.&lt;/strong&gt; Python's &lt;code&gt;re&lt;/code&gt; module is a backtracking engine. &lt;code&gt;a*&lt;/code&gt; first grabs as much as it can, and if the rest of the pattern then fails, it gives a character back and retries — choose, explore, un-choose, over the characters of a string. The same idea in twelve lines, matching glob patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Glob-style matching where &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is any one character and &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; is any run.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Let the star match nothing; if the rest of the pattern then fails,
&lt;/span&gt;        &lt;span class="c1"&gt;# backtrack and hand the star one more character.
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a*c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abbbc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a?c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ac&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*x*y*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;axolotly&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; vs &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*.py   vs main.py   -&amp;gt; True
*.py   vs main.txt  -&amp;gt; False
a*c    vs abbbc     -&amp;gt; True
a?c    vs ac        -&amp;gt; False
*x*y*  vs axolotly  -&amp;gt; True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;or&lt;/code&gt; is the backtrack: try the shorter match, and only if the whole rest of the pattern fails does the star swallow another character. The standard library equivalent is &lt;code&gt;fnmatch.fnmatch&lt;/code&gt;, which works by translating the glob into a regex. The cost of that same mechanism is real: in 2016 Stack Overflow was taken offline for 34 minutes by catastrophic backtracking in a single regular expression scanning a post with a very long run of whitespace. Backtracking engines can go exponential on innocent-looking patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraint satisfaction and SAT solvers.&lt;/strong&gt; The DPLL algorithm from 1962, still the skeleton inside modern SAT solvers, is backtracking search over variable assignments with propagation as its pruning rule. Prolog's entire execution model is backtracking. Sudoku solvers, exam timetablers and puzzle generators are all the same shape: choose a value for the next empty slot, check the constraints, explore, undo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested data.&lt;/strong&gt; A directory tree, an HTML document and a JSON object are recursive structures, so the code that walks them is recursive too — &lt;code&gt;shutil.rmtree&lt;/code&gt; descends into each subdirectory by calling itself, and every tree algorithm in this series does the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No base case, or an unreachable one.&lt;/strong&gt; &lt;code&gt;if n == 1&lt;/code&gt; misses &lt;code&gt;n = 0&lt;/code&gt; and every negative number. Test the boundary: &lt;code&gt;factorial(0)&lt;/code&gt;, &lt;code&gt;subsets([])&lt;/code&gt;, an empty tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recursing on the same size.&lt;/strong&gt; &lt;code&gt;factorial(n)&lt;/code&gt; inside &lt;code&gt;factorial&lt;/code&gt; compiles fine and dies with &lt;code&gt;RecursionError&lt;/code&gt;. Every call must strictly shrink the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storing the shared path instead of a copy.&lt;/strong&gt; &lt;code&gt;result.append(path)&lt;/code&gt; gives you a list of identical, mutated lists. &lt;code&gt;result.append(list(path))&lt;/code&gt; is the fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to undo part of the state.&lt;/strong&gt; Popping &lt;code&gt;path&lt;/code&gt; but leaving &lt;code&gt;used[index] = True&lt;/code&gt; marks every element consumed forever, so the search never backs up past its first descent — &lt;code&gt;permutations([1, 2, 3])&lt;/code&gt; returns &lt;code&gt;[[1, 2, 3]]&lt;/code&gt; and nothing else, one ordering instead of six. Undo everything you did, in the reverse order you did it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assuming recursion is why your code is slow.&lt;/strong&gt; Usually it is recomputation. Add &lt;code&gt;functools.lru_cache&lt;/code&gt; before rewriting anything as a loop, and measure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Returning nothing from the recursive branch.&lt;/strong&gt; Writing &lt;code&gt;factorial(n - 1)&lt;/code&gt; instead of &lt;code&gt;return n * factorial(n - 1)&lt;/code&gt; makes the function return &lt;code&gt;None&lt;/code&gt; — a bug that Python reports far away from where it happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write a recursive function that reverses a string, with the empty string as the base case.&lt;/li&gt;
&lt;li&gt;Write a recursive &lt;code&gt;count_leaves&lt;/code&gt; for a nested list such as &lt;code&gt;[1, [2, [3, 4]], 5]&lt;/code&gt;, then state its maximum stack depth.&lt;/li&gt;
&lt;li&gt;Convert &lt;code&gt;factorial&lt;/code&gt; into a loop, and confirm both agree for every n from 0 to 20.&lt;/li&gt;
&lt;li&gt;Generate all subsets of size exactly k by pruning any branch whose path is already longer than k.&lt;/li&gt;
&lt;li&gt;Write a Sudoku solver: find the first empty cell, try digits 1 to 9, keep any digit that breaks no row, column or box constraint, recurse, and undo it if the recursion fails.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Recursion is two parts and one data structure: a base case, a strictly smaller recursive case, and the call stack that remembers everything half-finished. Backtracking adds one line — undo the last choice — and turns that stack into a systematic search over every arrangement, with pruning as the lever that makes it practical. Count calls for time, count depth for space, and remember that Python gives you 1000 frames and no tail-call optimisation, so depth is a design constraint rather than a detail.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time, linear recursion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — one call per level, as in &lt;code&gt;factorial&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time, branching recursion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(bᵈ) — b calls per level, d levels deep, so &lt;code&gt;fib&lt;/code&gt; is O(1.618ⁿ)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time, full enumeration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n · 2ⁿ) for all subsets, O(n · n!) for all permutations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(depth) — one frame per open call, plus one shared path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python depth limit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1000 frames by default, from &lt;code&gt;sys.getrecursionlimit()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tail-call optimisation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None, and deliberately so — it would destroy tracebacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The call stack, plus one mutable &lt;code&gt;path&lt;/code&gt; list you undo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The problem is self-similar, or you must enumerate under constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depth grows with input size, or subproblems repeat — loop or memoize&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recursive descent and PEG parsers, regex engines, SAT and CSP solvers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;itertools.permutations&lt;/code&gt;, &lt;code&gt;functools.lru_cache&lt;/code&gt;, &lt;code&gt;re&lt;/code&gt;, &lt;code&gt;fnmatch&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/dynamic-programming-introduction" rel="noopener noreferrer"&gt;Dynamic Programming&lt;/a&gt; — the fix for the exponential &lt;code&gt;fib&lt;/code&gt; above, in one decorator or one table.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/n-queens-problem" rel="noopener noreferrer"&gt;The N-Queens Problem&lt;/a&gt; — backtracking with real constraints, and the clearest pruning you will see.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/depth-first-search" rel="noopener noreferrer"&gt;Depth-First Search&lt;/a&gt; — the same descent applied to a graph, both recursively and with an explicit stack.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/stacks" rel="noopener noreferrer"&gt;Stacks&lt;/a&gt; — what the call stack actually is, and how to build your own when recursion runs out of frames.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/merge-sort" rel="noopener noreferrer"&gt;Merge Sort&lt;/a&gt; — divide and conquer, where recursion depth is log n and the combining step is the algorithm.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Huffman Coding in Python: How Compression Actually Compresses</title>
      <dc:creator>Bimal Kshetri</dc:creator>
      <pubDate>Sun, 16 Aug 2026 16:36:18 +0000</pubDate>
      <link>https://dev.to/bimal-py/huffman-coding-in-python-how-compression-actually-compresses-3m3</link>
      <guid>https://dev.to/bimal-py/huffman-coding-in-python-how-compression-actually-compresses-3m3</guid>
      <description>&lt;p&gt;A plain text file spends eight bits on the letter &lt;code&gt;e&lt;/code&gt; and eight bits on the letter &lt;code&gt;q&lt;/code&gt;, even though English uses &lt;code&gt;e&lt;/code&gt; about a hundred times more often. That is the waste Huffman coding removes. It hands every symbol a bit pattern whose length depends on how often the symbol appears — one bit for the workhorses, ten or twelve for the oddities — and the result is provably the best you can do with whole numbers of bits.&lt;/p&gt;

&lt;p&gt;"Provably the best" is a strong claim and Huffman earns it. Among all codes that give each symbol a whole number of bits and can be decoded without separators between symbols, none produces a shorter output than Huffman's on the same frequency table. The construction that achieves this is a loop with two heap pops and one heap push in it.&lt;/p&gt;

&lt;p&gt;It is also still running on your machine right now. Huffman codes are the entropy stage of DEFLATE, so they are inside nearly every &lt;code&gt;.zip&lt;/code&gt; archive, nearly every &lt;code&gt;Content-Encoding: gzip&lt;/code&gt; response and nearly every PNG image — nearly, because DEFLATE can also emit uncompressed blocks and ZIP permits other methods entirely. They are inside baseline JPEG and inside MP3 too. This post builds a working coder on &lt;code&gt;abracadabra&lt;/code&gt;, decodes a bit string by hand, measures the real compression ratio &lt;em&gt;including&lt;/em&gt; the table you have to ship alongside the bits, and is honest about where modern codecs have moved past it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why variable-length codes need a rule
&lt;/h3&gt;

&lt;p&gt;Start with the boring option. &lt;code&gt;abracadabra&lt;/code&gt; uses five distinct characters, so you could number them 0 to 4 and spend three bits on each: 11 characters, 33 bits. Decoding is trivial — chop the stream into three-bit pieces.&lt;/p&gt;

&lt;p&gt;Now try to do better. &lt;code&gt;a&lt;/code&gt; appears five times out of eleven, so give it a short code and the rare &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;d&lt;/code&gt; long ones. The moment code lengths differ, decoding stops being obvious. Assign &lt;code&gt;E&lt;/code&gt; the code &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;T&lt;/code&gt; the code &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;A&lt;/code&gt; the code &lt;code&gt;01&lt;/code&gt;, and the received bits &lt;code&gt;01&lt;/code&gt; mean either &lt;code&gt;E&lt;/code&gt; then &lt;code&gt;T&lt;/code&gt;, or a single &lt;code&gt;A&lt;/code&gt;. No amount of cleverness in the decoder fixes that; the information is genuinely not there.&lt;/p&gt;

&lt;p&gt;The rule that saves you is &lt;strong&gt;prefix-free&lt;/strong&gt;: no symbol's code may be a prefix of another symbol's code. &lt;code&gt;01&lt;/code&gt; is broken because &lt;code&gt;0&lt;/code&gt; is a prefix of it. With that rule, decoding becomes a single left-to-right scan with no lookahead. Read bits until what you hold matches some code, and that match is forced — a longer code that started the same way would have your bits as a prefix, which the rule forbids.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codes are paths down a binary tree
&lt;/h3&gt;

&lt;p&gt;There is a neat way to guarantee prefix-freeness by construction rather than by checking. Build a binary tree. Label every left edge &lt;code&gt;0&lt;/code&gt; and every right edge &lt;code&gt;1&lt;/code&gt;. Put the symbols &lt;strong&gt;only at the leaves&lt;/strong&gt;. A symbol's code is the sequence of edge labels from the root down to its leaf.&lt;/p&gt;

&lt;p&gt;Prefix-freeness now comes for free. Code &lt;code&gt;X&lt;/code&gt; is a prefix of code &lt;code&gt;Y&lt;/code&gt; exactly when the path to &lt;code&gt;X&lt;/code&gt; is the start of the path to &lt;code&gt;Y&lt;/code&gt; — which would mean &lt;code&gt;X&lt;/code&gt; sits on the way to &lt;code&gt;Y&lt;/code&gt;, and therefore is not a leaf. Symbols live at leaves, so it cannot happen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-prefix-tree.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-prefix-tree.svg" alt="A four-symbol prefix code drawn as a binary tree, with the symbols E, T, A and O at the leaves and their bit codes read off the path from the root" width="1000" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Decoding is now a walk. Put a finger on the root. For each bit, step left on &lt;code&gt;0&lt;/code&gt; and right on &lt;code&gt;1&lt;/code&gt;. When the finger lands on a leaf, emit that symbol and jump back to the root. Here is the bit string &lt;code&gt;0101101110&lt;/code&gt; decoded against the tree above, one line per symbol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bits     path from the root                       lands on   emit
0        left                                     leaf       E
1 0      right, left                              leaf       T
1 1 0    right, right, left                       leaf       A
1 1 1    right, right, right                      leaf       O
0        left                                     leaf       E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten bits in, &lt;code&gt;ETAOE&lt;/code&gt; out. The decoder never looked ahead, never backtracked and never needed a separator or a length field. That is the entire payoff of the prefix-free property.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which tree, though?
&lt;/h3&gt;

&lt;p&gt;Any tree with the symbols at the leaves gives a valid code. The one you want is the cheapest. The total number of bits for a message is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cost(tree) = sum over symbols of (frequency of symbol) x (depth of its leaf)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;because a symbol at depth &lt;code&gt;d&lt;/code&gt; costs &lt;code&gt;d&lt;/code&gt; bits every time it appears. Minimising that means pushing frequent symbols towards the root and rare ones towards the bottom.&lt;/p&gt;

&lt;p&gt;Huffman's rule for building that tree is one sentence: &lt;strong&gt;take the two lowest-weight nodes, join them under a new parent whose weight is their sum, and put the parent back in the pool. Repeat until one node is left.&lt;/strong&gt; Start with one leaf node per symbol, weighted by its frequency. Each merge removes two nodes and adds one, so &lt;code&gt;n&lt;/code&gt; symbols take exactly &lt;code&gt;n - 1&lt;/code&gt; merges.&lt;/p&gt;

&lt;p&gt;The reason this greedy rule is correct is easier to see with a second way of writing the same cost. Every merge you perform adds one edge above everything already inside the two nodes you merged, so it adds one bit to every occurrence of every symbol underneath. That means each merged node's weight gets billed exactly once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cost(tree) = sum of the weights of all the internal nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cheap merges early, expensive merges late — and that is what "always merge the two lightest" does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it work
&lt;/h2&gt;

&lt;p&gt;Take the string &lt;code&gt;abracadabra&lt;/code&gt;. Count the characters first:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Frequency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Eleven characters, five distinct symbols. Fixed-width would cost 33 bits and ASCII costs 88.&lt;/p&gt;

&lt;p&gt;Two of those frequencies tie at 2 and two tie at 1, so before any merging you have to decide what "the two lowest" means when several nodes are equal. Pick a tie-break and stick to it, or two runs of your own code can produce two different (equally good) trees. The rule used here: nodes enter the pool in alphabetical order and carry a strictly increasing counter, and ties are broken by the lower counter. So the initial pool, in the order it will be drained, is &lt;code&gt;c=1&lt;/code&gt;, &lt;code&gt;d=1&lt;/code&gt;, &lt;code&gt;b=2&lt;/code&gt;, &lt;code&gt;r=2&lt;/code&gt;, &lt;code&gt;a=5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Four merges, since there are five symbols:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge 1.&lt;/strong&gt; The two lightest are &lt;code&gt;c=1&lt;/code&gt; and &lt;code&gt;d=1&lt;/code&gt;. Join them under a node of weight 2. The pool is now &lt;code&gt;b=2&lt;/code&gt;, &lt;code&gt;r=2&lt;/code&gt;, &lt;code&gt;(cd)=2&lt;/code&gt;, &lt;code&gt;a=5&lt;/code&gt; — the new node sorts last among the twos because it received the newest counter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge 2.&lt;/strong&gt; The two lightest are &lt;code&gt;b=2&lt;/code&gt; and &lt;code&gt;r=2&lt;/code&gt;. Join them under a node of weight 4. Pool: &lt;code&gt;(cd)=2&lt;/code&gt;, &lt;code&gt;(br)=4&lt;/code&gt;, &lt;code&gt;a=5&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge 3.&lt;/strong&gt; The two lightest are &lt;code&gt;(cd)=2&lt;/code&gt; and &lt;code&gt;(br)=4&lt;/code&gt;. Join them under a node of weight 6. Pool: &lt;code&gt;a=5&lt;/code&gt;, &lt;code&gt;(cdbr)=6&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge 4.&lt;/strong&gt; Only two nodes are left, so they merge into the root, weight 11 — which is the length of the string, as it must be, because every character is counted exactly once somewhere below the root.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-merge-pool.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-merge-pool.svg" alt="The pool of nodes waiting to be merged, shown at each stage of the build, with the two lightest nodes highlighted as the pair that merges next" width="1000" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now read the tree. Left edges are &lt;code&gt;0&lt;/code&gt;, right edges are &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-code-tree.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-code-tree.svg" alt="The finished Huffman tree for abracadabra, with a alone under the left edge of the root and the four rarer symbols packed under the right" width="1000" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;Frequency&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Bits used&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;110&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;100&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;101&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;111&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Total: &lt;strong&gt;23 bits&lt;/strong&gt;, against 33 for fixed width and 88 for ASCII. Check it against the other cost formula — the internal nodes weigh 2, 4, 6 and 11, and 2 + 4 + 6 + 11 = 23. The two ways of counting agree, as they must.&lt;/p&gt;

&lt;p&gt;Encoding &lt;code&gt;abracadabra&lt;/code&gt; is now a table lookup per character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a   b    r    a   c    a   d    a   b    r    a
0   110  111  0   100  0   101  0   110  111  0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which runs together as &lt;code&gt;01101110100010101101110&lt;/code&gt;. Feed that back into the decoding walk and you get &lt;code&gt;abracadabra&lt;/code&gt; — the first &lt;code&gt;0&lt;/code&gt; lands immediately on the &lt;code&gt;a&lt;/code&gt; leaf, then &lt;code&gt;110&lt;/code&gt; steps right, right, left to &lt;code&gt;b&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why greedy is right here
&lt;/h3&gt;

&lt;p&gt;Greedy algorithms are usually wrong, so the argument matters. It has two halves.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;there is an optimal tree in which the two rarest symbols are siblings at the deepest level.&lt;/strong&gt; Take any optimal tree. No internal node in it has only one child, because deleting such a node would shorten a code and cut the cost. So the deepest leaf has a sibling, and that sibling must be a leaf at the same depth — anything hanging below it would be deeper still. Call those two slots &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt;, and swap the rarest symbol into &lt;code&gt;p&lt;/code&gt; and the second-rarest into &lt;code&gt;q&lt;/code&gt;. Each swap moves a lower frequency to a greater-or-equal depth and a higher frequency to a lesser-or-equal depth, so the total cost cannot go up. The tree was optimal, so the swapped tree is optimal too, and in it the two rarest symbols are siblings at the bottom.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;merging them leaves a smaller version of the same problem.&lt;/strong&gt; Replace those two siblings with a single symbol whose frequency is their sum. Any tree for the smaller alphabet turns into a tree for the original by splitting that leaf in two, and the cost goes up by exactly the merged weight either way. So an optimal tree for the smaller problem gives an optimal tree for the bigger one.&lt;/p&gt;

&lt;p&gt;Put them together and induct on the number of symbols: the first merge is safe, and what remains is the same problem one symbol shorter. That is a textbook exchange argument, and it is what separates Huffman from the greedy rules that merely look sensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;Counting first. &lt;code&gt;collections.Counter&lt;/code&gt; is the standard-library tool for this and there is no reason to write your own tally loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Counter&lt;/span&gt;

&lt;span class="n"&gt;TEXT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abracadabra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; characters, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; distinct symbols&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;three bits each (fixed width): &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eight bits each (ASCII):       &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a  5
b  2
c  1
d  1
r  2
11 characters, 5 distinct symbols
three bits each (fixed width): 33 bits
eight bits each (ASCII):       88 bits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the tree. The pool of nodes waiting to be merged needs one operation — "give me the lightest" — repeated &lt;code&gt;2(n - 1)&lt;/code&gt; times, which is exactly what a min-heap is for. Python's is &lt;code&gt;heapq&lt;/code&gt;, and it works on a plain list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;heapq&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;One node of the code tree. A leaf carries a symbol; an internal node
    carries None and exists only to hold two children together.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Node&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Merge the two lightest nodes until one node is left, and return it.

    The heap holds `(weight, tiebreak, node)`. The tiebreak counter is not
    decoration: it fixes the order of equal weights so the output is
    reproducible, and it guarantees heapq never has to compare two Nodes.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="n"&gt;tiebreak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
        &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiebreak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="n"&gt;tiebreak&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heapify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;left_weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;right_weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left_weight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;right_weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;merge &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; + &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                  &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;   pool: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiebreak&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiebreak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;tiebreak&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A readable label: the symbol for a leaf, its leaves for a merged node.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;symbols_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;symbols_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;symbols_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;symbols_of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiebreak&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The nodes still waiting to be merged, in the order the heap will pop.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;waiting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heap&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tiebreak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
                     &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;waiting&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root weight: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;merge c=1 + d=1 -&amp;gt; 2   pool: b=2  r=2  (cd)=2  a=5
merge b=2 + r=2 -&amp;gt; 4   pool: (cd)=2  (br)=4  a=5
merge (cd)=2 + (br)=4 -&amp;gt; 6   pool: a=5  (cdbr)=6
merge a=5 + (cdbr)=6 -&amp;gt; 11   pool: (acdbr)=11
root weight: 11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four merges, matching the hand trace line for line.&lt;/p&gt;

&lt;p&gt;Turning the tree into a lookup table is one depth-first walk that carries the path so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_codes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Map each symbol to its bit string. A left edge is 0, a right edge is 1.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# A one-symbol alphabet has no edges at all, so the walk below would
&lt;/span&gt;        &lt;span class="c1"&gt;# hand it the empty code and nothing could ever be decoded.
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;


&lt;span class="n"&gt;codes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_codes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;total_bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
    &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;total_bits&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; x&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;total: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;total_bits&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a  0    x5  =  5 bits
b  110  x2  =  6 bits
c  100  x1  =  3 bits
d  101  x1  =  3 bits
r  111  x2  =  6 bits
total: 23 bits
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Encoding is a join over table lookups. Decoding is the finger-on-the-tree walk, written as a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Take one edge per bit. Landing on a leaf emits a symbol and restarts.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bit&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_leaf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bit string ran out part-way down the tree&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;bits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;codes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits, against &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits of ASCII&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;01101110100010101101110
23 bits, against 88 bits of ASCII
abracadabra
True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How the code maps to the idea
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The heap tuple is &lt;code&gt;(weight, tiebreak, node)&lt;/code&gt; and every part earns its place.&lt;/strong&gt; &lt;code&gt;weight&lt;/code&gt; is the key the algorithm cares about. &lt;code&gt;tiebreak&lt;/code&gt; increments on every push, so no two tuples are ever equal in their first two fields — which makes the output reproducible across runs and, less obviously, stops Python from ever reaching the third element during a comparison. Drop it and the first frequency tie crashes with a &lt;code&gt;TypeError&lt;/code&gt;, because &lt;code&gt;Node&lt;/code&gt; has no ordering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;heapq.heapify&lt;/code&gt; seeds the heap in O(n), not O(n log n)&lt;/strong&gt;, which is why the leaves are appended to a plain list first rather than pushed one at a time. It does not change the overall bound, but it is free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The loop condition is &lt;code&gt;len(heap) &amp;gt; 1&lt;/code&gt;, not a fixed range.&lt;/strong&gt; Each pass pops two and pushes one, so the heap shrinks by exactly one and ends holding the root. It also handles a single-symbol alphabet for nothing: the body never runs and the lone leaf is returned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;build_codes&lt;/code&gt; carries the path in an argument, not a shared list.&lt;/strong&gt; &lt;code&gt;prefix + "0"&lt;/code&gt; gives each branch its own string, so there is nothing to undo on the way back up — a shared list you append to and forget to pop is the classic way to get this function subtly wrong. The single-symbol case still needs its own line, though: a tree of one leaf and no edges would hand &lt;code&gt;aaaa&lt;/code&gt; the empty code and encode it to zero bits, so it gets the one-bit code &lt;code&gt;0&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;decode&lt;/code&gt; ends by checking that the finger came back to the root.&lt;/strong&gt; A bit string that stops half-way down a branch is truncated or corrupt, and saying so beats silently dropping the last symbol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-pipeline.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-pipeline.svg" alt="The five stages of a Huffman round trip, from counting symbol frequencies to storing the header alongside the encoded bits" width="1000" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The header you cannot skip
&lt;/h2&gt;

&lt;p&gt;Here is the part that tutorials tend to leave out. The 23 bits above are meaningless on their own. The decoder needs the same tree the encoder used, and it has no way to derive it — the frequencies came from the original message, which is precisely what the decoder does not have. So a real Huffman file is &lt;strong&gt;header plus payload&lt;/strong&gt;, and the header is not free.&lt;/p&gt;

&lt;p&gt;You have two reasonable ways to write one. Storing raw frequencies means a symbol plus a count per entry, and the count needs four bytes to hold a large file's tally — five bytes an entry. Storing &lt;strong&gt;code lengths&lt;/strong&gt; is much cheaper: given only "&lt;code&gt;a&lt;/code&gt; is 1 bit, &lt;code&gt;b&lt;/code&gt; is 3 bits, &lt;code&gt;c&lt;/code&gt; is 3 bits, ..." you can rebuild a code with exactly those lengths by handing out bit patterns in a fixed order — shortest length first, symbols in order within a length. That is canonical Huffman, it is what DEFLATE does, and it costs two bytes an entry rather than five. The codes come out different from the ones the tree gave you, but the lengths are the same, so the payload is the same size.&lt;/p&gt;

&lt;p&gt;The measurement below uses that cheaper header: a two-byte count of entries, then one byte of symbol and one byte of code length each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return (distinct symbols, payload bits, header bits, stored bytes).

    The header is the honest part. Storing one byte of symbol and one byte of
    code length per entry, plus a two-byte entry count, is enough for the
    decoder to rebuild an identical canonical tree.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_codes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;build_tree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;SAMPLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Compression works because real data is lumpy. The letter e turns up in &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;English text roughly a hundred times more often than the letter q, and yet &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a plain text file spends exactly eight bits on each of them. Huffman coding &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;removes that waste by giving common symbols short codes and rare symbols &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;long ones, and it does so optimally: no other code that spends a whole &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number of bits per symbol can beat it on the same frequency table.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;syms&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;header&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stored&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ratio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abracadabra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sample paragraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sample x 4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sample x 40&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stored&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;distinct&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;ratio&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mf"&gt;7.2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input                  syms  payload  header  stored   ratio
abracadabra               5       23      96      15   1.36x
sample paragraph         32     1843     528     297   0.69x
sample x 4               32     7372     528     988   0.57x
sample x 40              32    73720     528    9281   0.54x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the first row carefully: &lt;code&gt;abracadabra&lt;/code&gt; compresses from 11 bytes to &lt;strong&gt;15 bytes&lt;/strong&gt;. Huffman made it bigger. The payload did shrink, from 88 bits to 23, but 96 bits of header swamped the saving. That is arithmetic, not a bug — header cost is proportional to alphabet size while the saving is proportional to message length, so a message loses when its alphabet is wide relative to its length. The ratio decides, not the length alone: eleven characters over five symbols stores as 15 bytes, but eleven characters over two symbols carries a six-byte header and stores as 8, a real win. DEFLATE knows this and offers a "stored" block type that copies bytes verbatim, used whenever compression would expand the data.&lt;/p&gt;

&lt;p&gt;Repeat the same distribution down the table and the header amortises away: 0.69x, then 0.57x, then 0.54x, converging on the payload-only ratio of 1843 / (432 × 8) = 0.53.&lt;/p&gt;

&lt;p&gt;The other number worth knowing is the floor. Shannon's source coding theorem says no code that assigns bits to symbols independently can beat the entropy of the distribution, and Huffman always lands within one bit per symbol of it — usually far closer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;entropy_bits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Shannon&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s lower bound: no per-symbol code can beat this on this text.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;


&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;abracadabra&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sample paragraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE&lt;/span&gt;&lt;span class="p"&gt;)]:&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;floor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;entropy_bits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; huffman &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bits/symbol, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entropy &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, overhead &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;abracadabra          huffman 2.091 bits/symbol, entropy 2.040, overhead 0.051
sample paragraph     huffman 4.266 bits/symbol, entropy 4.224, overhead 0.042
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five hundredths of a bit per symbol off the theoretical floor. The gap exists because Huffman must round every code to a whole number of bits, and it is the one thing arithmetic coding fixes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;Let &lt;code&gt;n&lt;/code&gt; be the number of &lt;strong&gt;distinct symbols&lt;/strong&gt; and &lt;code&gt;m&lt;/code&gt; the length of the message. Keeping those two separate is essential — for English text &lt;code&gt;n&lt;/code&gt; is a few dozen while &lt;code&gt;m&lt;/code&gt; can be gigabytes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Counting frequencies: O(m).&lt;/strong&gt; One pass, one dictionary update per character.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the tree: O(n log n).&lt;/strong&gt; Seeding costs O(n log n) as written, because the code sorts the symbols before it heapifies — that sort buys the alphabetical tie-break, not the heap, and &lt;code&gt;heapify&lt;/code&gt; itself is O(n). The loop then runs exactly &lt;code&gt;n - 1&lt;/code&gt; times, because each pass consumes two nodes and produces one, taking the pool from &lt;code&gt;n&lt;/code&gt; down to 1. Each pass does two &lt;code&gt;heappop&lt;/code&gt; calls and one &lt;code&gt;heappush&lt;/code&gt;, and every heap operation walks one root-to-leaf path of a heap holding at most &lt;code&gt;n&lt;/code&gt; items, which is at most &lt;code&gt;log₂ n&lt;/code&gt; steps. So the merging is &lt;code&gt;3(n - 1)&lt;/code&gt; heap operations at O(log n) each — that is the &lt;code&gt;n&lt;/code&gt; and that is the &lt;code&gt;log n&lt;/code&gt;. For 256 byte values, &lt;code&gt;n - 1&lt;/code&gt; is 255 merges and &lt;code&gt;log₂ 256&lt;/code&gt; is 8, so the whole tree build is around 6,000 elementary steps regardless of file size.&lt;/p&gt;

&lt;p&gt;If the frequencies arrive already sorted you can drop the heap entirely and do it in &lt;strong&gt;O(n)&lt;/strong&gt; with two FIFO queues: one holding the sorted leaves, one holding the merged nodes in the order they were created, which is automatically non-decreasing. The next-lightest node is always at the front of one of the two queues. That is a real technique, not a curiosity — it is why sorting-based Huffman implementations exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the code table: O(total code length).&lt;/strong&gt; The walk visits each of the &lt;code&gt;2n - 1&lt;/code&gt; nodes once, but the strings it builds cost the sum of all code lengths. That sum is &lt;code&gt;n × (average depth)&lt;/code&gt;, and the worst case is worse than it looks: frequencies that grow like the Fibonacci sequence produce a completely lopsided tree with a leaf at depth &lt;code&gt;n - 1&lt;/code&gt;, giving O(n²) characters of code strings. In practice depth stays small, and there is a hard ceiling — to force a code of length &lt;code&gt;d&lt;/code&gt; you need each level to be at least as heavy as the sum of the two below it, so total frequency must grow at least as fast as the Fibonacci numbers. A message of fewer than 4.8 billion symbols can never produce a code longer than 44 bits. DEFLATE tightens that to 15 bits by construction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encoding: O(m + B)&lt;/strong&gt;, where &lt;code&gt;B&lt;/code&gt; is the number of output bits. One table lookup per input character plus the cost of writing the bits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decoding: O(B).&lt;/strong&gt; Exactly one tree step per bit — no searching, no backtracking. Real decoders speed this up with a lookup table that consumes several bits at once, trading memory for a shorter walk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space: O(n) for the tree, plus the code table, plus O(B) for the output.&lt;/strong&gt; The tree has &lt;code&gt;2n - 1&lt;/code&gt; nodes, since a binary tree with &lt;code&gt;n&lt;/code&gt; leaves and no one-child nodes has &lt;code&gt;n - 1&lt;/code&gt; internal nodes. The code table is the part that is easy to under-count: it holds every code as a string, so it costs the same sum of code lengths that building it cost — O(n²) characters on the Fibonacci worst case above, not O(n). &lt;code&gt;build_codes&lt;/code&gt; is also recursive, so it holds one stack frame per level, up to &lt;code&gt;n - 1&lt;/code&gt; of them, which is why a deep enough tree hits Python's recursion limit rather than merely running slowly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-growth.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvusqwphywysmdyteyjzt.supabase.co%2Fstorage%2Fv1%2Fobject%2Fpublic%2Fblog-images%2Falgorithms%2Fhuffman-coding-growth.svg" alt="Linear, n log n and quadratic growth compared, with n log n highlighted" width="1000" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;n log n&lt;/code&gt; here is cheap in absolute terms because &lt;code&gt;n&lt;/code&gt; is bounded by your alphabet, not your data. Compressing a 1 GB file with a byte alphabet still builds the tree in 255 merges. The linear O(m) passes over the data are what actually dominate the clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it, and when not to
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it when you control a format and the symbol frequencies are genuinely skewed.&lt;/strong&gt; Huffman decoding is fast, branch-light and easy to make correct, which is why it survives inside formats that need to decode at video frame rates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it as the last stage of a pipeline, not as the whole pipeline.&lt;/strong&gt; This is the important limitation. Huffman models only how often each symbol appears; it is blind to order. A file that is &lt;code&gt;ab&lt;/code&gt; repeated a million times has two symbols at 50% each, so Huffman spends one bit on each and saves nothing over the fixed-width baseline — even though a human can describe that file in one sentence. Repetition is caught by a dictionary method like LZ77, which replaces repeats with back-references. DEFLATE is exactly that pairing: LZ77 first, Huffman second on whatever skew is left.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not reach for it when a general-purpose compressor will do.&lt;/strong&gt; If you just want a file to be smaller, use &lt;code&gt;zlib&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt; or &lt;code&gt;lzma&lt;/code&gt; from the standard library — C implementations of complete formats that will beat a hand-rolled symbol coder on essentially any real input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it on short messages.&lt;/strong&gt; As the table above shows, an eleven-byte input with five distinct symbols comes back as fifteen bytes: the payload fell from 88 bits to 23, saving eight bytes, and the twelve-byte header more than swallowed them. If you must compress small records, share one pre-agreed table across all of them so the header is paid once — which is what HTTP/2's HPACK does with its static Huffman table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not use it when you need the last few percent.&lt;/strong&gt; A symbol with probability 0.9 deserves 0.15 bits; Huffman must spend a whole one. Arithmetic coding, range coding and the newer asymmetric numeral systems all encode at fractional bit cost and close that gap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it shows up in the real world
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DEFLATE (RFC 1951)&lt;/strong&gt; is the big one, and it is everywhere: &lt;code&gt;.zip&lt;/code&gt; archives, &lt;code&gt;gzip&lt;/code&gt;, the zlib library, PNG's image data, and HTTP responses sent with &lt;code&gt;Content-Encoding: gzip&lt;/code&gt;. Each DEFLATE block runs LZ77 and then Huffman-codes the resulting literals, match lengths and distances. A block either uses a fixed table baked into the spec or carries its own dynamic one — and that dynamic table is stored as canonical code lengths which are themselves Huffman-coded, a second layer of the same trick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Baseline JPEG&lt;/strong&gt; Huffman-codes the quantised DCT coefficients, with separate tables for the DC and AC coefficients of the luminance and chrominance channels. JPEG also defines an arithmetic-coding mode that compresses a few percent better, but patent worries in the 1990s meant almost nothing implemented it, and baseline Huffman became the version everyone actually ships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MP3&lt;/strong&gt; (MPEG-1 Audio Layer III) picks from a set of predefined Huffman tables to code the quantised frequency-domain values in each granule. &lt;strong&gt;Brotli&lt;/strong&gt;, the algorithm behind &lt;code&gt;Content-Encoding: br&lt;/code&gt;, uses Huffman codes alongside a large built-in dictionary and context modelling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zstandard&lt;/strong&gt; uses Huffman for literals but switches to Finite State Entropy, a tANS coder, for the sequence data — a deliberate split, because tANS handles skewed distributions at fractional bit cost while Huffman decodes literals faster.&lt;/p&gt;

&lt;p&gt;Be accurate about the trend, though. The highest-compression modern codecs have largely moved past Huffman for their main entropy stage: H.264 and H.265 use CABAC, a context-adaptive binary arithmetic coder, and AV1 uses a multi-symbol arithmetic coder. Huffman persists where decode speed and simplicity matter more than the last few percent — which is still an enormous amount of the world's data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pushing bare nodes into the heap.&lt;/strong&gt; Without a tie-break value, the first frequency tie makes &lt;code&gt;heapq&lt;/code&gt; compare two &lt;code&gt;Node&lt;/code&gt; objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;broken_heap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;broken_heap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TypeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TypeError: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: '&amp;lt;' not supported between instances of 'Node' and 'Node'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is the counter in the middle of the tuple. &lt;code&gt;@dataclass(order=True)&lt;/code&gt; looks like the shortcut and is not one: it compares fields in declaration order, so the first field it reaches is &lt;code&gt;symbol&lt;/code&gt; — a string on a leaf, &lt;code&gt;None&lt;/code&gt; on an internal node. Leaves and merged nodes tie all the time, and the moment they do you swap one crash for another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderedNode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderedNode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OrderedNode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="n"&gt;leaf_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;merged_cd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OrderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OrderedNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;heapq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;heappush&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leaf_b&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;merged_cd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TypeError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TypeError: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: '&amp;lt;' not supported between instances of 'NoneType' and 'str'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the pool after merge 1 of &lt;code&gt;abracadabra&lt;/code&gt;, where &lt;code&gt;(cd)=2&lt;/code&gt; meets &lt;code&gt;b=2&lt;/code&gt; — ordered nodes do not survive even the worked example. The counter sidesteps the question entirely: two tuples never tie on their first two fields, so the &lt;code&gt;Node&lt;/code&gt; is never compared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to store the table.&lt;/strong&gt; An encoder that returns only the bit string has produced something nobody can decode, including itself after a restart. Ship the header or agree the table in advance; there is no third option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-deterministic tie-breaking.&lt;/strong&gt; Iterating a dictionary you built in arbitrary order, or breaking ties by object identity, gives you a different-but-equally-optimal tree each run. Every such tree is fine on its own, but if any part of your system rebuilds the tree separately it will disagree with the encoder, and the output will decode to garbage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Putting a symbol on an internal node.&lt;/strong&gt; It is tempting when an alphabet feels small. It destroys the prefix-free property and makes decoding ambiguous — the whole reason for the tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating a bit string as bytes.&lt;/strong&gt; &lt;code&gt;23&lt;/code&gt; bits is not a whole number of bytes. You must pad the last byte and store how many padding bits you added, or the decoder will walk a few extra edges at the end and emit a phantom symbol. Do not lean on the &lt;code&gt;if node is not root&lt;/code&gt; check in &lt;code&gt;decode&lt;/code&gt; for this: it only fires when the padding happens to strand the walk part-way down a branch. Pad &lt;code&gt;abracadabra&lt;/code&gt;'s 23 bits with one &lt;code&gt;0&lt;/code&gt; and the extra step lands cleanly on the &lt;code&gt;a&lt;/code&gt; leaf, so the check passes and you get &lt;code&gt;abracadabraa&lt;/code&gt; back with no complaint at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compressing already-compressed data.&lt;/strong&gt; Running Huffman over a JPEG or a ZIP gives you a nearly uniform byte distribution, near-maximal entropy, and an output slightly larger than the input thanks to the header.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add byte packing: convert the bit string into a &lt;code&gt;bytes&lt;/code&gt; object with the padding length recorded, and confirm the round trip still returns the original text.&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;serialise_header&lt;/code&gt; and &lt;code&gt;parse_header&lt;/code&gt; pair that stores the code lengths, then rebuild the code table from lengths alone using canonical ordering — shortest length first, alphabetical within a length.&lt;/li&gt;
&lt;li&gt;Find the break-even point: for the sample paragraph's distribution, how many characters must a message have before the compressed form is smaller than the original?&lt;/li&gt;
&lt;li&gt;Replace the recursive &lt;code&gt;build_codes&lt;/code&gt; walk with an explicit stack, so it cannot hit Python's recursion limit on a pathologically deep tree.&lt;/li&gt;
&lt;li&gt;Build the Fibonacci worst case — frequencies 1, 1, 2, 3, 5, 8, 13, ... for 20 symbols — and check that the longest code really is 19 bits.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Huffman coding is the clearest example in the whole greedy family of a local rule that provably reaches a global optimum, and the exchange argument that proves it is short enough to hold in your head. Build a min-heap of leaves, merge the two lightest &lt;code&gt;n - 1&lt;/code&gt; times, read the codes off the tree. Then remember the two things the theory does not tell you: the header has to travel with the bits, and symbol frequencies alone cannot see repetition, which is why every serious format wraps Huffman around something else.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n log n) — &lt;code&gt;n - 1&lt;/code&gt; merges, three heap operations each&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Build time, sorted input&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) — two FIFO queues instead of a heap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(m + B) — one table lookup per input symbol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Decode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(B) — exactly one tree step per bit, no backtracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;O(n) for the &lt;code&gt;2n - 1&lt;/code&gt; tree nodes, plus the code table (the sum of all code lengths, O(n²) at worst), plus O(n) of recursion stack in &lt;code&gt;build_codes&lt;/code&gt;, plus O(B) for the output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Optimal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes, among prefix codes with whole-bit symbol codes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Within&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 bit per symbol of the entropy floor, usually far less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data structure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Binary tree built with a min-heap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frequencies are skewed and known, and decode speed matters&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid it when&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The data is repetitive rather than skewed, or messages are tiny&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-world use&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DEFLATE (ZIP, gzip, PNG), baseline JPEG, MP3, Brotli, zstd literals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python equivalent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;heapq&lt;/code&gt; for the queue; &lt;code&gt;zlib&lt;/code&gt; / &lt;code&gt;gzip&lt;/code&gt; / &lt;code&gt;lzma&lt;/code&gt; for actual compression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Keep reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/heaps-and-priority-queues" rel="noopener noreferrer"&gt;Heaps and Priority Queues&lt;/a&gt; — the structure doing all the work in the merge loop, built from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/greedy-algorithms" rel="noopener noreferrer"&gt;Greedy Algorithms&lt;/a&gt; — the family Huffman belongs to, and how to prove a greedy rule before trusting it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/tries-prefix-trees" rel="noopener noreferrer"&gt;Tries (Prefix Trees)&lt;/a&gt; — the other tree where the path spells the answer, used for autocomplete instead of compression.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/coin-change-problem" rel="noopener noreferrer"&gt;The Coin Change Problem&lt;/a&gt; — a greedy rule that looks just as reasonable and is wrong, with the DP that fixes it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bimalkhatri.com.np/blogs/big-o-notation-and-complexity-analysis" rel="noopener noreferrer"&gt;Big O Notation&lt;/a&gt; — the counting arguments behind every bound quoted above.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>python</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
