<?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: zeyad daowd</title>
    <description>The latest articles on DEV Community by zeyad daowd (@zeyaddaowd).</description>
    <link>https://dev.to/zeyaddaowd</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%2F4106142%2Fbfbfbc4a-c31f-43b9-a4cf-5d4a2741fc77.jpg</url>
      <title>DEV Community: zeyad daowd</title>
      <link>https://dev.to/zeyaddaowd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zeyaddaowd"/>
    <language>en</language>
    <item>
      <title>Bloom Filters</title>
      <dc:creator>zeyad daowd</dc:creator>
      <pubDate>Wed, 30 Sep 2026 20:53:18 +0000</pubDate>
      <link>https://dev.to/zeyaddaowd/bloom-filters-4id9</link>
      <guid>https://dev.to/zeyaddaowd/bloom-filters-4id9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Bloom filters (and friends) are a family of probabilistic data structures that answer the question "is this element in the set?" with &lt;strong&gt;no false negatives&lt;/strong&gt;, a &lt;strong&gt;bounded false-positive rate&lt;/strong&gt;, and a &lt;strong&gt;small memory footprint&lt;/strong&gt;: about 14 bits per element at a 0.1% false-positive rate, no matter how long the elements are. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Let's say you are building an application with millions of users with unique usernames. You get a request with a username and want to check if it exists before allowing a response.&lt;/p&gt;

&lt;p&gt;You could send a select query to the database, but that might not be reasonable if you don't want to overload it (and accessing disk is a bit slow).&lt;/p&gt;

&lt;p&gt;What about storing the usernames in a hashset in memory (e.g. C++ &lt;code&gt;unordered_set&lt;/code&gt;)? Now here comes the interesting part: hashsets use O(n) memory with a big constant. Storing 1 billion strings (don't ask me why you would have a billion users, just follow the example, it generalizes to other data) of 10-20 characters costs around 10 GB to 20 GB of memory, and that's ignoring the overhead of the data structure itself.&lt;/p&gt;

&lt;p&gt;So the question is: can we do better on memory without sacrificing fast lookups?&lt;/p&gt;

&lt;p&gt;The answer is yes, you can use a Bloom filter.&lt;/p&gt;

&lt;p&gt;A Bloom filter answers this exact question with no false negatives (if it says the user doesn't exist, then they don't exist). If it says the user exists, there is a small probability that they actually don't, but we can accept that to save memory :D&lt;/p&gt;

&lt;h2&gt;
  
  
  How do they work?
&lt;/h2&gt;

&lt;p&gt;A Bloom filter uses an array of &lt;code&gt;m&lt;/code&gt; bits and &lt;code&gt;k&lt;/code&gt; hash functions.&lt;/p&gt;

&lt;p&gt;When an element is added, we hash it with each of the &lt;code&gt;k&lt;/code&gt; hash functions and, for each result, set the bit at position &lt;code&gt;hash % m&lt;/code&gt; to 1.&lt;/p&gt;

&lt;p&gt;To look an element up, we do the same: hash it with each of the &lt;code&gt;k&lt;/code&gt; hash functions and check that all &lt;code&gt;k&lt;/code&gt; bits are 1. If they are, the element might be in the set. If any of them is 0, the element was definitely never added.&lt;/p&gt;

&lt;p&gt;An example: say we have 3 hash functions and a 16-bit array, and we insert &lt;code&gt;alice&lt;/code&gt; and &lt;code&gt;bob&lt;/code&gt;. We run the 3 hash functions on each and set the corresponding bits (mod 16).&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiko1nz4bnnnp1ouh6y7o.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiko1nz4bnnnp1ouh6y7o.png" alt="Inserting alice and bob into a 16-bit array using 3 hash functions each; both set bit 6" width="798" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now what if we want to query &lt;code&gt;carol&lt;/code&gt; and &lt;code&gt;dave&lt;/code&gt;? We run the same method and check the corresponding 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpb9j6u0x873zbnpfwe2u.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpb9j6u0x873zbnpfwe2u.png" alt="Querying carol finds a zero bit so she is definitely absent; querying dave finds all three bits set by other items, a false positive" width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Carol has a bit that is 0, so she was definitely never added. All of dave's bits happen to be 1 (they were set by other names), so the filter says he might exist. That is exactly the false positive case Bloom filters can give you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sizing
&lt;/h2&gt;

&lt;p&gt;Key formulas [1]:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;p&lt;/code&gt; = desired false-positive rate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;n&lt;/code&gt; = expected number of unique elements&lt;/li&gt;
&lt;li&gt;Bit array size: &lt;code&gt;m = -(n * ln(p)) / (ln(2)^2)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Number of hash functions: &lt;code&gt;k = (m / n) * ln(2)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Actual false-positive rate: &lt;code&gt;p = (1 - e^(-kn/m))^k&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What about some numbers? For the same example with 1 billion elements and a 0.1% false-positive rate, we use &lt;code&gt;n = 10^9&lt;/code&gt; and &lt;code&gt;p = 0.001&lt;/code&gt;. That gives &lt;code&gt;m ≈ 14.4 * 10^9&lt;/code&gt; bits, which is about 1.8 GB (~1.7 GiB), with &lt;code&gt;k ≈ 10&lt;/code&gt; hash functions.&lt;/p&gt;

&lt;p&gt;Compared with 10 to 20 GB for the hashset, this looks pretty good. Notice that the length of the strings doesn't matter to a Bloom filter, only the number of strings does. If we doubled the username length, the Bloom filter would use the same memory, while the hashset's footprint would roughly double.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros and cons
&lt;/h2&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small memory footprint (about 14 bits per element at 0.1%)&lt;/li&gt;
&lt;li&gt;Constant-time lookups (&lt;code&gt;k&lt;/code&gt; hash computations)&lt;/li&gt;
&lt;li&gt;Zero false negatives&lt;/li&gt;
&lt;li&gt;Supports parallel operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;False positives&lt;/li&gt;
&lt;li&gt;Parameter sensitivity and hash function dependence&lt;/li&gt;
&lt;li&gt;You can't remove an item from the filter&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Traditional Bloom filters don't support deletion. You can't just set the hashed bits back to zero, because those bits may be shared with other elements, so you would effectively delete more than one element.&lt;/p&gt;

&lt;h2&gt;
  
  
  Counting Bloom filters
&lt;/h2&gt;

&lt;p&gt;...But what if, instead of setting a bit to 1, we used more than one bit per entry (we call it a counter) and incremented the counter at position &lt;code&gt;hash % m&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Well, you just created a counting Bloom filter. Now you increment the counters when inserting an item, and you can decrement them to delete it.&lt;/p&gt;

&lt;p&gt;Here is an example of inserting alice and bob and then deleting alice, with 4-bit counters (they can reach 15) instead of 1 bit per entry.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckmpkxn8zx8ebbt8i257.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fckmpkxn8zx8ebbt8i257.png" alt="Counting Bloom filter after inserting alice and bob, then after deleting alice; bob is still found because his counters stay above zero" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keep in mind: if a counter reaches its maximum (1111 with 4 bits), it becomes saturated and we stop changing it, neither incrementing nor decrementing.&lt;/p&gt;

&lt;p&gt;Why? Let's imagine a very naive scenario. We use a single hash function and 2-bit counters (max value 3), and the hash function for some reason maps &lt;code&gt;alice&lt;/code&gt;, &lt;code&gt;bob&lt;/code&gt;, &lt;code&gt;carol&lt;/code&gt; and &lt;code&gt;dave&lt;/code&gt; to the same counter. After adding the first three names, the counter is 3 (11 in binary). Adding dave would overflow it, so we can't let it wrap around to 0, and we leave it at 3.&lt;/p&gt;

&lt;p&gt;Now what if we remove alice, bob and carol? If we decremented the counter each time, it would reach 0, and querying dave would say he doesn't exist. That's a false negative, and we hate those.&lt;/p&gt;

&lt;p&gt;The deeper problem is that once the counter reads 3, the filter can't tell whether 3, 4 or 100 names landed there, so any decrement is a guess. Leaving saturated counters untouched is the only safe choice. The cost is that such a counter may stay non-zero even after every item using it is gone, which can only cause extra false positives, never false negatives. (With 4-bit counters and a properly sized filter, reaching 15 is rare.)&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fypj63kts4rio10x90kpa.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fypj63kts4rio10x90kpa.png" alt="Four names share one 2-bit counter; decrementing it after removing three names causes a false negative for dave, while leaving the saturated counter alone keeps him found" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scalable Bloom filters
&lt;/h2&gt;

&lt;p&gt;Another issue: notice how we fixed the array size and the number of hash functions based on the expected input size (or an assumption of our maximum input size)? Let's say that after 1 billion usernames it turns out you still need more.&lt;/p&gt;

&lt;p&gt;You can't simply grow the bit array, because hashes are taken modulo the array size, so every existing element would now map to different bits. And you can't rehash either, because the filter never stored the original elements.&lt;/p&gt;

&lt;p&gt;Luckily there is a variant that fixes this: scalable Bloom filters.&lt;/p&gt;

&lt;p&gt;We start with a normal Bloom filter. Once it is full, we stack another filter on top of it, usually a bigger one, while keeping the overall false-positive rate bounded. New elements are inserted into the newest (active) filter, and when querying we have to check every layer of the stack.&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64syocnedyhyu5ku7j3d.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F64syocnedyhyu5ku7j3d.png" alt="A stack of three Bloom filters where each newer filter is larger; inserts go to the active top filter and lookups check every filter" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: it is still worth starting the first layer with a good estimate of the input size. A low initial guess means many layers, and every lookup has to run the hash functions of each layer, which hurts performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they're used
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Databases like Apache Cassandra use Bloom filters on SSTables (Sorted String Tables) to avoid reading data files that can't contain the requested key.&lt;/li&gt;
&lt;li&gt;The MyRocks storage engine in MariaDB uses Bloom filters to speed up point lookups (queries that ask for a single key).&lt;/li&gt;
&lt;li&gt;CDNs (content delivery networks) like Akamai use Bloom filters to handle "one-hit wonders". They don't want to cache objects that are only requested once, so when an object is requested and isn't in the Bloom filter, it isn't cached and is added to the filter. If it's requested again, it's found in the filter and gets cached.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you found this interesting, there are more variants worth a look, like Cuckoo filters and Ribbon filters.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] &lt;a href="https://bloomy.hexdocs.pm/Bloomy.Params.html" rel="noopener noreferrer"&gt;https://bloomy.hexdocs.pm/Bloomy.Params.html&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://redis.io/blog/bloom-filter/" rel="noopener noreferrer"&gt;https://redis.io/blog/bloom-filter/&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://www.geeksforgeeks.org/system-design/bloom-filters-in-system-design/" rel="noopener noreferrer"&gt;https://www.geeksforgeeks.org/system-design/bloom-filters-in-system-design/&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://grindengineer.substack.com/p/bloom-filters-how-databases-skip-millions-of-disk-reads" rel="noopener noreferrer"&gt;https://grindengineer.substack.com/p/bloom-filters-how-databases-skip-millions-of-disk-reads&lt;/a&gt;&lt;br&gt;
[5] &lt;a href="https://medium.com/@suresh.sk1691/bloom-filter-0d3ad654536c" rel="noopener noreferrer"&gt;https://medium.com/@suresh.sk1691/bloom-filter-0d3ad654536c&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://www.geeksforgeeks.org/python/bloom-filters-introduction-and-python-implementation/" rel="noopener noreferrer"&gt;https://www.geeksforgeeks.org/python/bloom-filters-introduction-and-python-implementation/&lt;/a&gt;&lt;br&gt;
[7] &lt;a href="https://corte.si/posts/code/bloom-filter-rules-of-thumb/" rel="noopener noreferrer"&gt;https://corte.si/posts/code/bloom-filter-rules-of-thumb/&lt;/a&gt;&lt;br&gt;
[8] &lt;a href="https://mariadb.com/docs/server/server-usage/storage-engines/myrocks/myrocks-and-bloom-filters" rel="noopener noreferrer"&gt;https://mariadb.com/docs/server/server-usage/storage-engines/myrocks/myrocks-and-bloom-filters&lt;/a&gt;&lt;br&gt;
[9] &lt;a href="https://medium.com/@nitishmehta3/optimize-lookups-with-bloom-filters-fast-cheap-and-almost-honest-dbec172f720f" rel="noopener noreferrer"&gt;https://medium.com/@nitishmehta3/optimize-lookups-with-bloom-filters-fast-cheap-and-almost-honest-dbec172f720f&lt;/a&gt;&lt;br&gt;
[10]&lt;a href="https://cassandra.apache.org/doc/latest/cassandra/managing/operating/bloom_filters.html" rel="noopener noreferrer"&gt;https://cassandra.apache.org/doc/latest/cassandra/managing/operating/bloom_filters.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>datastructures</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Count-Min Sketch</title>
      <dc:creator>zeyad daowd</dc:creator>
      <pubDate>Wed, 02 Sep 2026 15:18:06 +0000</pubDate>
      <link>https://dev.to/zeyaddaowd/count-min-sketch-5a17</link>
      <guid>https://dev.to/zeyaddaowd/count-min-sketch-5a17</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Count-Min Sketch lets you estimate how many times something occurred in a massive stream, using a fixed-size matrix (~54KB) instead of a hashmap that could reach terabytes, with a small error that's bounded with high probability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Imagine the following leetcode-style problem:&lt;/p&gt;

&lt;p&gt;You have a stream of visitors to a website, each visitor can visit your website multiple times. The target is relatively simple: you need to know how many times a specific person visited your website.&lt;/p&gt;

&lt;p&gt;Looks easy - we can use a hashmap and increment the entry for the user by each visit. Lookups would be &lt;code&gt;O(1)&lt;/code&gt;, which is good, and you'd need &lt;code&gt;O(n)&lt;/code&gt; memory, where &lt;code&gt;n&lt;/code&gt; is the number of unique users - which looks good as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it breaks
&lt;/h3&gt;

&lt;p&gt;Now imagine you are tracking post visibility for a social media application. We can just store a hashmap for each post - now the space complexity would be &lt;code&gt;O(n * m)&lt;/code&gt;, where &lt;code&gt;m&lt;/code&gt; is the number of posts.&lt;/p&gt;

&lt;p&gt;Imagine there are 1 million users and 1 million posts, and we want to keep an exact counter for every user/post pair. Even ignoring the storage required for the keys and the overhead of the hashmap itself, we'd need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 × 10^6 × 1 × 10^6 × 4 bytes = 4 × 10^12 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's more than &lt;strong&gt;3.5 terabytes&lt;/strong&gt; of memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt; hashmap storage explodes when trying to count over a lot of data. That's where Count-Min Sketch comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count-Min Sketch
&lt;/h2&gt;

&lt;p&gt;Count-Min Sketch utilizes a fixed 2D matrix that doesn't grow with input size, with width &lt;code&gt;W&lt;/code&gt; and depth &lt;code&gt;D&lt;/code&gt;. The width and depth can be chosen by setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w = ⌈e/ε⌉
d = ⌈ln 1/δ⌉
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;e&lt;/code&gt; = 2.718 (Euler's constant)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;δ&lt;/code&gt; = probability of failure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ε&lt;/code&gt; = error rate&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt; Count-Min Sketch is a probabilistic data structure - it doesn't provide the accurate answer (this was my first time hearing about probabilistic data structures). If you found that interesting take a look onto Hyperloglog and bloom filters&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It guarantees the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;The estimated frequency is never smaller than the true frequency.&lt;/li&gt;
&lt;li&gt;With probability at least &lt;code&gt;1 - δ&lt;/code&gt;, the estimate is at most &lt;code&gt;ε × N&lt;/code&gt; larger than the true frequency, where &lt;code&gt;N&lt;/code&gt; is the total number of observations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;true count ≤ estimated count ≤ true count + ε * N&lt;/code&gt; (with probability ≥ &lt;code&gt;1 - δ&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that we have a depth × width matrix, we'll also have some hash functions (equal to depth).&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faaa3a1nl7q04rbob4v1m.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faaa3a1nl7q04rbob4v1m.png" alt="Empty CMS Matrix" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For each input, we do the following:&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;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="mf"&gt;0.&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;d&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;column&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hash_i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;element&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;matrix&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;column&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;p&gt;&lt;em&gt;(Each row has its own independent hash function - &lt;code&gt;hash_i&lt;/code&gt; refers to the hash function for row &lt;code&gt;i&lt;/code&gt;.)&lt;/em&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgh6yy8lfj2ilqn237xkv.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgh6yy8lfj2ilqn237xkv.png" alt="First Element Insertion" width="799" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After some input entries, you might have the following matrix:&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cymsb3sd6c8xwlcqhv3.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9cymsb3sd6c8xwlcqhv3.png" alt="CMS Lookup" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking up a frequency
&lt;/h3&gt;

&lt;p&gt;If you want to get the frequency of element &lt;code&gt;E1&lt;/code&gt;, you look at the hashed values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hash function&lt;/th&gt;
&lt;th&gt;Hashed value % width&lt;/th&gt;
&lt;th&gt;Matrix entry&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hashFunction1(E1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;matrix[0][1]&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;hashFunction2(E1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;code&gt;matrix[1][6]&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;hashFunction3(E1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;matrix[2][3]&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;hashFunction4(E1)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;matrix[3][1]&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;result = min(3, 2, 2, 2) = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the final frequency, you take the minimum of the values in the corresponding matrix entries: &lt;strong&gt;2&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the minimum?
&lt;/h3&gt;

&lt;p&gt;Every counter we look at contains the true count of &lt;code&gt;E1&lt;/code&gt; plus any counts added by other elements that happened to collide with it. Therefore, collisions can only make a counter larger, never smaller. Taking the minimum across the rows gives us the best estimate among the counters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why give up exact answers?
&lt;/h2&gt;

&lt;p&gt;At first, giving up exact answers might sound like a bad idea. But imagine you're trying to find the top 100 most-viewed posts out of billions of events. You probably don't care whether a post was viewed 1,002,341 or 1,002,350 times - you care that you can identify the posts being viewed a lot, without storing a counter for every possible item.&lt;/p&gt;

&lt;p&gt;That's where the tradeoff becomes useful: a tiny amount of error in exchange for much less memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example calculation for high accuracy
&lt;/h3&gt;

&lt;p&gt;For failure probability &lt;code&gt;δ = 0.01&lt;/code&gt; (confidence = 99%) and error rate &lt;code&gt;ε = 0.001&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;width ≈ &lt;strong&gt;2,718&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;depth = &lt;strong&gt;5&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;total matrix size = &lt;code&gt;5 × 2718 × 4 bytes&lt;/code&gt; = &lt;strong&gt;54,360 bytes (&amp;lt; 54 KB)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The time complexity for retrieval is &lt;code&gt;O(depth)&lt;/code&gt;, which is a constant - so basically &lt;code&gt;O(1)&lt;/code&gt; as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where is this used in the industry?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; - Count-Min Sketch is implemented natively as part of Redis' core probabilistic data structures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network analysis&lt;/strong&gt; - tracking IP addresses to detect DDoS attacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heavy hitters&lt;/strong&gt; - finding items that occur more than a threshold of the stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, next time a counting problem mentions "billions of events," you know there's a structure for exactly that.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/dsa/count-min-sketch-in-java-with-examples/" rel="noopener noreferrer"&gt;Count-Min Sketch in Java with Examples - GeeksforGeeks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch" rel="noopener noreferrer"&gt;Count–min sketch - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/search?utf8=%E2%9C%93&amp;amp;q=count-min-sketch"&gt;Count-Min Sketch posts on dev.to&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/docs/latest/develop/data-types/probabilistic/count-min-sketch/" rel="noopener noreferrer"&gt;Redis Docs - Count-Min Sketch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://singhajit.com/data-structures/count-min-sketch/" rel="noopener noreferrer"&gt;singhajit.com - Count-Min Sketch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
    </item>
  </channel>
</rss>
