<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>算法 on Cloudside</title><link>https://cloudside.icyyan.com/tags/%E7%AE%97%E6%B3%95/</link><description>Recent content in 算法 on Cloudside</description><generator>Hugo</generator><language>en</language><lastBuildDate>Thu, 21 May 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://cloudside.icyyan.com/tags/%E7%AE%97%E6%B3%95/index.xml" rel="self" type="application/rss+xml"/><item><title>布隆过滤器：用少量内存判断一个元素是否可能存在</title><link>https://cloudside.icyyan.com/posts/bloom-filter-guide/</link><pubDate>Thu, 21 May 2026 10:00:00 +0800</pubDate><guid>https://cloudside.icyyan.com/posts/bloom-filter-guide/</guid><description>&lt;p&gt;如果一个接口被大量查询不存在的数据，比如用户不断请求不存在的商品 ID、缓存里没有的用户 ID，系统很容易被拖进一个尴尬局面：缓存没命中，请求继续打到数据库；数据库查不到，下一次同样的请求又会重复发生。&lt;/p&gt;
&lt;p&gt;这类问题的关键不是“如何更快地查到数据”，而是先回答一个更便宜的问题：&lt;strong&gt;这个东西是不是一定不存在？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;布隆过滤器解决的正是这个问题。它不能像哈希集合那样给出完全精确的答案，但它能用很少的内存，在极快的时间内告诉你：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果结果是“不存在”，那它一定不存在。&lt;/li&gt;
&lt;li&gt;如果结果是“存在”，那它只是可能存在。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个“可能”就是布隆过滤器最重要的取舍。它用少量误判，换来了很高的空间效率。&lt;/p&gt;
&lt;h2 id="先从一个普通集合说起"&gt;先从一个普通集合说起&lt;/h2&gt;
&lt;p&gt;假设我们要记录 1 亿个已经注册过的用户 ID，最直接的做法是把这些 ID 放进一个集合：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;registered_users &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;registered_users&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add(&lt;span style="color:#e6db74"&gt;&amp;#34;user:10086&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;user:10086&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; registered_users:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(&lt;span style="color:#e6db74"&gt;&amp;#34;exists&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个方案很好理解，也很精确。但问题是：集合为了支持快速查询，通常不只存储原始元素，还要维护哈希表、桶、指针、扩容状态等额外结构。数据量一大，内存会明显上去。&lt;/p&gt;
&lt;p&gt;如果我们的目标只是提前过滤掉明显不存在的请求，真的需要保存完整的字符串吗？&lt;/p&gt;
&lt;p&gt;布隆过滤器的答案是：不需要。它只保存一些 bit。&lt;/p&gt;
&lt;h2 id="布隆过滤器的直觉"&gt;布隆过滤器的直觉&lt;/h2&gt;
&lt;p&gt;可以把布隆过滤器想成一个很大的位图，也就是一串只包含 0 和 1 的数组：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;普通位图通常要求元素能直接映射成整数位置，比如数字 13 就对应第 13 个 bit。但现实里的元素可能是字符串、URL、邮箱、订单号，不一定能直接当数组下标。&lt;/p&gt;
&lt;p&gt;布隆过滤器在中间加了一层哈希函数。&lt;/p&gt;
&lt;p&gt;对于一个元素，它不用一个位置表示，而是用多个哈希函数算出多个位置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;element = &amp;#34;user:10086&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hash1(element) -&amp;gt; 3
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hash2(element) -&amp;gt; 9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hash3(element) -&amp;gt; 14
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;插入这个元素时，就把这些位置都置为 1：&lt;/p&gt;</description></item><item><title>位图 Bitmap：用一个 bit 记录海量状态</title><link>https://cloudside.icyyan.com/posts/bitmap-data-structure-guide/</link><pubDate>Mon, 18 May 2026 10:00:00 +0800</pubDate><guid>https://cloudside.icyyan.com/posts/bitmap-data-structure-guide/</guid><description>&lt;p&gt;如果让你记录“今天哪些用户登录过”，最直接的想法可能是放进一个集合：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logged_in_users &lt;span style="color:#f92672"&gt;=&lt;/span&gt; set()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logged_in_users&lt;span style="color:#f92672"&gt;.&lt;/span&gt;add(&lt;span style="color:#ae81ff"&gt;10086&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样当然能用。但如果用户 ID 范围很大、查询又特别频繁，集合会带来不少额外开销。有没有一种更省空间的办法？&lt;/p&gt;
&lt;p&gt;位图，也就是 Bitmap，解决的就是这类问题：&lt;strong&gt;用一个 bit 表示一个状态&lt;/strong&gt;。这个状态通常只有两种结果：有或没有、是或否、出现过或没出现过。&lt;/p&gt;
&lt;h2 id="位图到底是什么"&gt;位图到底是什么&lt;/h2&gt;
&lt;p&gt;先从一个字节说起。&lt;/p&gt;
&lt;p&gt;一个字节有 8 个 bit：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0 0 0 0 0 0 0 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个 bit 都可以表示一个编号是否存在。比如我们用 bit 记录数字是否出现过：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bit 0 表示数字 0 是否出现
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bit 1 表示数字 1 是否出现
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bit 2 表示数字 2 是否出现
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bit 7 表示数字 7 是否出现
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果数字 3 出现过，就把第 3 个 bit 置为 1：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;0 0 0 0 1 0 0 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ↑
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 数字 3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当编号超过 7 时，就放到下一个字节里。比如数字 13：&lt;/p&gt;</description></item></channel></rss>