<?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>Algorithm on Tech Explorer</title><link>https://byronhe.com/categories/algorithm/</link><description>C++、Linux、算法、数据结构与密码学的学习笔记与实践记录。</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 BH</copyright><lastBuildDate>Sun, 14 Jun 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://byronhe.com/categories/algorithm/index.xml" rel="self" type="application/rss+xml"/><item><title>倒排索引求交算法相关资料调研</title><link>https://byronhe.com/post/2020/06/14/posting-list-intersection-algorithms/</link><pubDate>Sun, 14 Jun 2020 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2020/06/14/posting-list-intersection-algorithms/</guid><description>&lt;h2 class="relative group"&gt;1. IR-book 经典教材中的思路&#10; &lt;div id="1-ir-book-经典教材中的思路" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-ir-book-%e7%bb%8f%e5%85%b8%e6%95%99%e6%9d%90%e4%b8%ad%e7%9a%84%e6%80%9d%e8%b7%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Faster postings list intersection via skip pointers&#10;通过跳指针，实现更快的 posting list 求交&#10;&lt;a href="https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html" target="_blank" rel="noreferrer"&gt;https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html&lt;/a&gt;&lt;/p&gt;</description><content:encoded>&#10;&lt;h2 class="relative group"&gt;1. IR-book 经典教材中的思路&#10; &lt;div id="1-ir-book-经典教材中的思路" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-ir-book-%e7%bb%8f%e5%85%b8%e6%95%99%e6%9d%90%e4%b8%ad%e7%9a%84%e6%80%9d%e8%b7%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Faster postings list intersection via skip pointers&#10;通过跳指针，实现更快的 posting list 求交&#10;&lt;a href="https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html" target="_blank" rel="noreferrer"&gt;https://nlp.stanford.edu/IR-book/html/htmledition/faster-postings-list-intersection-via-skip-pointers-1.html&lt;/a&gt;&lt;/p&gt;&#10;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="" src="https://nlp.stanford.edu/IR-book/html/htmledition/img106.png"&gt;&lt;/figure&gt;&#10;&lt;p&gt;首先有个最 trival 的算法，两个 posting list，各一个指针，谁不匹配，就把谁右移一个，&lt;/p&gt;&#10;&lt;p&gt;跳表方法，思路类似 skip list，是要在建索引的时候，额外构建 skip list，这样就不是右移一个，而是按照跳表来右移。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;2. lemire 教授的 SIMDCompressionAndIntersection 库中有多种算法&#10; &lt;div id="2-lemire-教授的-simdcompressionandintersection-库中有多种算法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-lemire-%e6%95%99%e6%8e%88%e7%9a%84-simdcompressionandintersection-%e5%ba%93%e4%b8%ad%e6%9c%89%e5%a4%9a%e7%a7%8d%e7%ae%97%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://github.com/lemire/SIMDCompressionAndIntersection/blob/master/src/intersection.cpp" target="_blank" rel="noreferrer"&gt;https://github.com/lemire/SIMDCompressionAndIntersection/blob/master/src/intersection.cpp&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;可以看到实现了这些求交算法，并且还有个 benchmark 程序 benchintersection 可以做对比&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&amp;ldquo;simd&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;galloping&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;scalar&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v1&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v3&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simdgalloping&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simd_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v1_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;v3_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;simdgalloping_avx2&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;highlyscalable_intersect_SIMD&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;lemire_highlyscalable_intersect_SIMD&amp;rdquo;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;其中根据输入数据的规模，会选用不同的算法，并且还有 avx2 指令集的优化算法。&lt;/p&gt;&#10;&lt;p&gt;其中提到的 sse 优化的 fast-intersection-sorted-lists-sse&#10;&lt;a href="https://highlyscalable.wordpress.com/2012/06/05/fast-intersection-sorted-lists-sse/" target="_blank" rel="noreferrer"&gt;https://highlyscalable.wordpress.com/2012/06/05/fast-intersection-sorted-lists-sse/&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;3. &amp;ldquo;The magic of Algorithms! &amp;quot; 中的算法&#10; &lt;div id="3--the-magic-of-algorithms--中的算法" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3--the-magic-of-algorithms--%e4%b8%ad%e7%9a%84%e7%ae%97%e6%b3%95" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Chap. 6, Algorithm 6.1 Intersection based on Mutual Partitioning&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="http://didawiki.di.unipi.it/doku.php/magistraleinformaticanetworking/ae/ae2019/start#books_notes_etc" target="_blank" rel="noreferrer"&gt;http://didawiki.di.unipi.it/doku.php/magistraleinformaticanetworking/ae/ae2019/start#books_notes_etc&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;pdf 是 &lt;a href="https://www.dropbox.com/s/dqttqigvuf4ydkd/main.pdf?dl=0" target="_blank" rel="noreferrer"&gt;https://www.dropbox.com/s/dqttqigvuf4ydkd/main.pdf?dl=0&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;思路很简单，就是嵌套的 binary search，实测效果不错，而且实现简单，可以直接递归实现。&lt;/p&gt;&#10;&lt;p&gt;我实测效果还不错，提了个 issue&#10;&lt;a href="https://github.com/lemire/SIMDCompressionAndIntersection/pull/29" target="_blank" rel="noreferrer"&gt;https://github.com/lemire/SIMDCompressionAndIntersection/pull/29&lt;/a&gt;&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;4. List Intersection for Web Search: Algorithms, Cost Models, and Optimizations&#10; &lt;div id="4--list-intersection-for-web-search-algorithms-cost-models-and-optimizations" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4--list-intersection-for-web-search-algorithms-cost-models-and-optimizations" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="http://www.vldb.org/pvldb/vol12/p1-kim.pdf" target="_blank" rel="noreferrer"&gt;http://www.vldb.org/pvldb/vol12/p1-kim.pdf&lt;/a&gt;&#10;微软的文章，搞了个 cost model， 来评估各种 list 求交集算法的性能，但是不开源。&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;本文提出一种基于代价的模型，来估算各种算法组合的代价，并使用代价模型来搜索代价最低的算法，得到的计划通常是 2-way 算法的一个组合，&#10;并且比传统的 2-way 和 k-way 算法性能都好。&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;并列举了常见的 List Intersection 算法：&#10;2-way:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;2-Gallop&lt;/li&gt;&#10;&lt;li&gt;2-Merge&lt;/li&gt;&#10;&lt;li&gt;2-SIMD&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;k-way:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;k-Gallop&lt;/li&gt;&#10;&lt;li&gt;k-Merge&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;2-way vs. k-way&#10;本文断言，对 k-list 求交，通过 k-1 次重复 2-way 求交算法，比通过单次 k-way 求交算法，更快。&lt;/p&gt;&#10;&lt;p&gt;结论，通过本文 cost model optimizer 规划出来的 2-way 求交计算，通常比 k-way 求交更快。&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://arxiv.org/pdf/1401.6399.pdf" target="_blank" rel="noreferrer"&gt;https://arxiv.org/pdf/1401.6399.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;blockquote&gt;&lt;p&gt;We demonstrated that combining unpacking and differential coding resulted in faster decompression&#10;speeds, which were approximately 30 % better than the best speeds reported previously [3]. To match&#10;the performance of these fast compression schemes, we additionally vectorized and optimized the&#10;intersection of posting lists. To this end, we introduced a family of algorithms exploiting commonly available SIMD instructions (V1, V3 and SIMD GALLOPING). They are often twice as fast as the&#10;best non-SIMD algorithms. Then, we used our fast SIMD routines for decompression and posting&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;Efficient set intersection for inverted indexing&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.415.2636&amp;amp;rep=rep1&amp;amp;type=pdf" target="_blank" rel="noreferrer"&gt;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.415.2636&amp;rep=rep1&amp;type=pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Intersection in integer inverted indices&#10;&lt;a href="https://pdfs.semanticscholar.org/db30/9935b512ef4d0c8941c96e987ad70400ebde.pdf" target="_blank" rel="noreferrer"&gt;https://pdfs.semanticscholar.org/db30/9935b512ef4d0c8941c96e987ad70400ebde.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;lookup 算法最快，非常简单，两层存储，按位分成两层。&#10;首层直接定位，二层直接遍历。 二层可以压缩。&lt;/p&gt;&#10;</content:encoded></item><item><title>【翻译/介绍】jump consistent hash 零内存消耗，均匀，快速，简洁，来自Google的一致性哈希算法</title><link>https://byronhe.com/post/2015/03/13/jump_consistent_hash/</link><pubDate>Fri, 13 Mar 2015 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2015/03/13/jump_consistent_hash/</guid><description>&lt;h2 class="relative group"&gt;一，简介&#10; &lt;div id="一简介" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e7%ae%80%e4%bb%8b" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;jump consistent hash是一种一致性哈希算法, 此算法&lt;strong&gt;零内存消耗&lt;/strong&gt;，&lt;strong&gt;均匀分配&lt;/strong&gt;，&lt;strong&gt;快速&lt;/strong&gt;，并且&lt;strong&gt;只有5行代码&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;此算法适合使用在分shard的分布式存储系统中 。&lt;/p&gt;&#10;&lt;p&gt;此算法的作者是 Google 的 John Lamping 和 Eric Veach，论文原文在 &lt;a href="http://arxiv.org/ftp/arxiv/papers/1406/1406.2294.pdf" target="_blank" rel="noreferrer"&gt;http://arxiv.org/ftp/arxiv/papers/1406/1406.2294.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;完整代码：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="nf"&gt;JumpConsistentHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&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;j&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2862933555777941757ULL&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&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="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="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&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="kt"&gt;double&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;33&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;输入是一个64位的key，和桶的数量（一般对应服务器的数量），输出是一个桶的编号。&lt;/p&gt;</description><content:encoded>&lt;h2 class="relative group"&gt;一，简介&#10; &lt;div id="一简介" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%80%e7%ae%80%e4%bb%8b" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;jump consistent hash是一种一致性哈希算法, 此算法&lt;strong&gt;零内存消耗&lt;/strong&gt;，&lt;strong&gt;均匀分配&lt;/strong&gt;，&lt;strong&gt;快速&lt;/strong&gt;，并且&lt;strong&gt;只有5行代码&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;此算法适合使用在分shard的分布式存储系统中 。&lt;/p&gt;&#10;&lt;p&gt;此算法的作者是 Google 的 John Lamping 和 Eric Veach，论文原文在 &lt;a href="http://arxiv.org/ftp/arxiv/papers/1406/1406.2294.pdf" target="_blank" rel="noreferrer"&gt;http://arxiv.org/ftp/arxiv/papers/1406/1406.2294.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;完整代码：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="nf"&gt;JumpConsistentHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int64_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&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;j&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&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="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2862933555777941757ULL&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&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="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="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1LL&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&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="kt"&gt;double&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;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;33&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;输入是一个64位的key，和桶的数量（一般对应服务器的数量），输出是一个桶的编号。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;二，原理解释：&#10; &lt;div id="二原理解释" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%ba%8c%e5%8e%9f%e7%90%86%e8%a7%a3%e9%87%8a" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;下面byron根据论文的推导过程，做个翻译：&lt;/p&gt;&#10;&lt;p&gt;jump consistent hash的设计目标是：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;平衡性，把对象均匀地分布在所有桶中。&lt;/li&gt;&#10;&lt;li&gt;单调性，当桶的数量变化时，只需要把一些对象从旧桶移动到新桶，不需要做其它移动。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;jump consistent hash的设计思路是：&lt;strong&gt;计算当bucket数量变化时，有哪些输出需要变化&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;让我们循序渐进地思考：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;记 ch(key,num_buckets) 为num_buckets时的hash函数。&lt;/li&gt;&#10;&lt;li&gt;当num_buckets=1时，由于只有1个桶，显而易见，对任意k，有ch(k,1)==0。&lt;/li&gt;&#10;&lt;li&gt;当num_buckets=2时，为了使hash的结果保持均匀，ch(k,2)的结果应该有占比1/2的结果保持为0，有1/2跳变为1。&lt;/li&gt;&#10;&lt;li&gt;由此，一般规律是：num_buckets从n变化到n+1后，ch(k,n+1) 的结果中，应该有占比 n/(n+1) 的结果保持不变，而有 1/(n+1) 跳变为 n+1。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;因此，我们可以用一个随机数生成器，来决定每次要不要跳变，并且让这个随机数生成器的状态仅仅依赖于key。就得到下面这个初步代码：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&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="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&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="c1"&gt;// This will track ch(key, j +1) .&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&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;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&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;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&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="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;j&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;显而易见，这个算法是O(n)的。同时我们可以发现，大多数情况下b=j 是不会执行的，而且随着 j 越来越大，这个概率越来越低。&#10;那么有没有办法根据一个随机数，直接得出下一个跳变的 j ，降低时间复杂度呢？&lt;/p&gt;&#10;&lt;p&gt;ok,请把你的大脑切换到概率论模式。&lt;/p&gt;&#10;&lt;p&gt;我们可以把 ch(key,bum_buckets) 看做一个随机变量，&lt;/p&gt;&#10;&lt;p&gt;上述算法，追踪了桶编号的的跳变过程，我们记上一个跳变结果是b，假设下一个结果以一定概率是 j ，那么从b+1到j-1，这中间的多次增加桶都不能跳变。 对于在区间 (b, j) 内的任意整数 i ，j是下一个结果的概率可以记为:&lt;/p&gt;&#10;$$P(j&gt;=i) = P(ch(k,i)=ch(k,b+1))$$&lt;p&gt;其中 $ch(k,i)=ch(k,b+1)$ 意即从 $b+1$ 到 $i$ 的过程中，连续多次增加桶的时候都没有跳变，这个概率也就是连续多次不跳变事件概率的乘积，因此：&lt;/p&gt;&#10;$$ P(j&gt;=i) = P(ch(k,b+1)=ch(k,b+2)) * $$&lt;p&gt;&#10;&lt;/p&gt;&#10;$$ P(ch(k,b+2)=ch(k,b+3)) * $$&lt;p&gt;&#10;&lt;/p&gt;&#10;$$ P(ch(k,b+3)=ch(k,b+4)) * $$&lt;p&gt;&#10;&lt;/p&gt;&#10;$$ ...... * $$&lt;p&gt;&#10;&lt;/p&gt;&#10;$$ P(ch(k,i-1)=ch(k,i)) $$&lt;p&gt;由于单次不跳变的概率：&lt;/p&gt;&#10;$$P( ch(k,i)=ch(k,i+1) ) = i/(i+1)$$&lt;p&gt;所以连续多次不跳变的概率&lt;/p&gt;&#10;$$P(j&gt;=i) = (b+1)/(b+2) * (b+2)/(b+3) * ... * (i-1)/i$$&lt;p&gt;前后项分子分母相互抵消，得到：&lt;/p&gt;&#10;$$P(j&gt;=i) = (b+1)/i$$&lt;p&gt;意即：$j&gt;=i$ 的概率为 $(b+1)/i$&lt;/p&gt;&#10;&lt;p&gt;此时，我们取一个在 [0,1] 区间均匀分布的随机数 $r$，规定 $r&lt;(b+1)/i$，就有 $j&gt;=i$，&#10;所以有 $i&lt;(b+1)/r$，这样就得到了i的上界是 $(b+1)/r$，由于对任意的i都要有$j&gt;=i$，所以&#10;$j=floor( (b+1)/r )$，这样我们用一个随机数 $r$ 得到了 $j$ 。&lt;/p&gt;&#10;&lt;p&gt;因此，代码可以改进为：&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;seed&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="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&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;// bucket number before the previous jump&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&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;// bucket number before the current jump&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;num_buckets&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 0&amp;lt;r&amp;lt;1.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;j&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="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="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;r&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;这个算法的时间复杂度，可以假设每次r都取0.5，则可以认为每次 $j=2*j$，因此时间复杂度为 $O(log(n))$ 。&lt;/p&gt;&#10;&lt;p&gt;此处需要一个均匀的伪随机数生成器，论文中使用了一个64位的线性同余随机数生成器。&lt;/p&gt;&#10;&lt;p&gt;需要指出的是：不像割环法，jump consistent hash不需要对 key 做 hash ，这是由于 jump consistent hash 使用内置的伪随机数生成器，来对每一次 key 做再 hash，（byron的理解：所以结果分布的均匀性与输入key的分布无关，由伪随机数生成器的均匀性保证）。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;三，各项指标对比分析&#10; &lt;div id="三各项指标对比分析" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e4%b8%89%e5%90%84%e9%a1%b9%e6%8c%87%e6%a0%87%e5%af%b9%e6%af%94%e5%88%86%e6%9e%90" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;consistent hash的概念出自David Karger的论文，经典并且应用广泛的割环法即出自这篇论文：&lt;a href="http://www.ra.ethz.ch/cdstore/www8/data/2181/pdf/pd1.pdf" target="_blank" rel="noreferrer"&gt;http://www.ra.ethz.ch/cdstore/www8/data/2181/pdf/pd1.pdf&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;Karger提出2种实现:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&amp;ldquo;version A&amp;rdquo;，用 std::map&amp;lt;uint64_t, int32_t&amp;gt;表示key的hash 到桶id的映射。&lt;/li&gt;&#10;&lt;li&gt;&amp;ldquo;version B&amp;rdquo;，用 vector&amp;lt;pair&amp;lt;uint64_t, int32_t&amp;gt; &amp;gt;存储，vector事先排好序，用二分查找。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;这两种实现的查找时间复杂度也都是O(log(n))&lt;/p&gt;&#10;&lt;p&gt;jump consistent hash的论文中，用jump consistent hash和Karger的割环算法做了对比，结果如下：&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;1. key分布的均匀性&#10; &lt;div id="1-key分布的均匀性" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-key%e5%88%86%e5%b8%83%e7%9a%84%e5%9d%87%e5%8c%80%e6%80%a7" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;直接从论文中摘录如下表格：&lt;/p&gt;&#10;&lt;figure&gt;&lt;img&#10; class="my-0 rounded-md"&#10; loading="lazy"&#10; decoding="async"&#10; alt=""&#10; width="775"&#10; height="321"&#10; src="https://byronhe.com/images/jmp_consistent_hash_distribution_hu_9662f4b0475e1760.webp"&#10; srcset="https://byronhe.com/images/jmp_consistent_hash_distribution_hu_52a23ef899d6dbde.webp 480w, https://byronhe.com/images/jmp_consistent_hash_distribution_hu_9662f4b0475e1760.webp 775w"&#10; sizes="(min-width: 768px) 50vw, 92vw"&#10; data-zoom-src="https://byronhe.com/images/jmp_consistent_hash_distribution.png"&gt;&lt;/figure&gt;&#10;&lt;p&gt;从标准差(Standard Error)这一列可见，jump consistent hash的均匀性要胜过割环法。&lt;/p&gt;&#10;&lt;p&gt;并且显而易见，jump consistent hash，当 扩/缩容 时，跳变key数量已经是理论最少值 1/n。&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;2. 执行耗时&#10; &lt;div id="2-执行耗时" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e6%89%a7%e8%a1%8c%e8%80%97%e6%97%b6" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;下面是论文中的执行耗时对比图，其中k=1000。&lt;/p&gt;&#10;&lt;figure&gt;&lt;img&#10; class="my-0 rounded-md"&#10; loading="lazy"&#10; decoding="async"&#10; alt=""&#10; width="767"&#10; height="597"&#10; src="https://byronhe.com/images/jmp_consistent_hash_cpu_hu_89a1ea0e065a3f3.webp"&#10; srcset="https://byronhe.com/images/jmp_consistent_hash_cpu_hu_a6837a6601562847.webp 480w, https://byronhe.com/images/jmp_consistent_hash_cpu_hu_89a1ea0e065a3f3.webp 767w"&#10; sizes="(min-width: 768px) 50vw, 92vw"&#10; data-zoom-src="https://byronhe.com/images/jmp_consistent_hash_cpu.png"&gt;&lt;/figure&gt;&#10;&#10;&lt;h3 class="relative group"&gt;3. 内存占用对比&#10; &lt;div id="3-内存占用对比" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e5%86%85%e5%ad%98%e5%8d%a0%e7%94%a8%e5%af%b9%e6%af%94" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;显而易见，请自行脑补&lt;/p&gt;&#10;&#10;&lt;h3 class="relative group"&gt;4. 初始化耗时对比&#10; &lt;div id="4-初始化耗时对比" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e5%88%9d%e5%a7%8b%e5%8c%96%e8%80%97%e6%97%b6%e5%af%b9%e6%af%94" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h3&gt;&#10;&lt;p&gt;显而易见，请自行脑补&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;四，相关链接&#10; &lt;div id="四相关链接" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%9b%9b%e7%9b%b8%e5%85%b3%e9%93%be%e6%8e%a5" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;jump consistent hash 适合节点数只增加的业务场景，比如存储系统分 shard。&lt;/p&gt;&#10;&lt;p&gt;如果节点数会随机减少，比如 web caching 场景下服务器节点随机故障。&lt;/p&gt;&#10;&lt;p&gt;假设节点 i ( 0 &amp;lt;= i &amp;lt; num_buckets ) 故障，此时如果简单地把 num_buckets 减 1，就会导致编号在 i 之后的节点上的数据全部错位到前一个节点。&#10;因此 jump consistent hash 不适合节点随机故障的场景。&lt;/p&gt;&#10;&lt;p&gt;在 Hacker News上面的讨论：&lt;a href="https://news.ycombinator.com/item?id=8136408" target="_blank" rel="noreferrer"&gt;https://news.ycombinator.com/item?id=8136408&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;这个算法最早在Google的guava库里面开源：&lt;a href="https://github.com/google/guava/blob/master/guava/src/com/google/common/hash/Hashing.java#L392" target="_blank" rel="noreferrer"&gt;https://github.com/google/guava/blob/master/guava/src/com/google/common/hash/Hashing.java#L392&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>递增子序列数目计算的算法</title><link>https://byronhe.com/post/2011/10/17/inc-sub-seq-count-algo/</link><pubDate>Mon, 17 Oct 2011 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2011/10/17/inc-sub-seq-count-algo/</guid><description>&lt;p&gt;这是前几天笔试时，考场上想出来的算法，但是算了两次都不一样，最后只好蒙了一个选项，悲催！&lt;/p&gt;&#10;&lt;p&gt;问题如下：&lt;/p&gt;&#10;&lt;p&gt;给定一个整数序列，例如 4,2,6,3,7,1 ，该序列有多少个递增子序列？&lt;/p&gt;</description><content:encoded>&lt;p&gt;这是前几天笔试时，考场上想出来的算法，但是算了两次都不一样，最后只好蒙了一个选项，悲催！&lt;/p&gt;&#10;&lt;p&gt;问题如下：&lt;/p&gt;&#10;&lt;p&gt;给定一个整数序列，例如 4,2,6,3,7,1 ，该序列有多少个递增子序列？&lt;/p&gt;&#10;&lt;p&gt;我的算法如下：&lt;/p&gt;&#10;&lt;p&gt;记第i个元素为arr[i] ，记以第i个元素结尾的递增子序列有N[i]个，&lt;/p&gt;&#10;&lt;p&gt;则，考虑以第i+1个元素结尾的所有递增子序列，&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;首先置N[i+1]=1;//表示只有第i+1个元素一个元素的子序列。&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;对j=1...i&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 如果arr[j]&amp;lt;arr[i+1]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; N[i+1]+=N[j]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;最后结果等于N数组里所有元素之和。&lt;/p&gt;&#10;</content:encoded></item></channel></rss>