{"id":389488,"date":"2024-06-29T08:37:44","date_gmt":"2024-06-29T08:37:44","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=389488"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=389488","title":{"rendered":"<span>Overview of Morris&#8217;s counters<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/182\/0fc\/98c\/1820fc98c03554d3d8842bb658731f5f.jpeg\" alt=\"An artist's concept showing counting of elements in a stream with limited resources\" title=\"An artist's concept showing counting of elements in a stream with limited resources\" width=\"980\" height=\"550\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/182\/0fc\/98c\/1820fc98c03554d3d8842bb658731f5f.jpeg\" data-blurred=\"true\"\/><figcaption>An artist&#8217;s concept showing counting of elements in a stream with limited resources<\/figcaption><\/figure>\n<p><sub>We are glad to present you an article written by Qrator Labs&#8217; engineer Dmitry Kamaldinov. If you want to be a part of our Core team, write us at\u00a0<\/sub><a href=\"mailto:hr@qrator.net\"><sub>hr@qrator.net<\/sub><\/a><sub>.<\/sub><\/p>\n<h3>1 Introduction<\/h3>\n<p>On implementing streaming algorithms, counting of events often occurs, where an event means something like a packet arrival or a connection establishment. Since the number of events is large, the available memory can become a bottleneck: an ordinary\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/05a\/b38\/974\/05ab38974c8df3f3295561a6b5ddd454.svg\"\/>-bit counter allows to take into account no more than\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"2^n - 1\" alt=\"2^n - 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a1b\/9a7\/35a\/a1b9a735a257209cf00659ddffca6b1b.svg\"\/>events.<br \/>One way to handle a larger range of values using the same amount of memory would be approximate counting. This article provides an overview of the well-known Morris algorithm and some generalizations of it.<\/p>\n<p>Another way to reduce the number of bits required for counting mass events is to use decay. We discuss such an approach\u00a0<a href=\"https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5\">here<\/a>\u00a0[3], and we are going to publish another blog post on this particular topic shortly.<\/p>\n<p>In the beginning of this article, we analyse one straightforward probabilistic calculation algorithm and highlight its shortcomings (Section 2). Then (Section 3), we describe the algorithm proposed by Robert Morris in 1978 and indicate its most essential properties and advantages. For most non-trivial formulas and statements, the text contains our proofs, the demanding reader can find them in the inserts. In the following three sections, we outline valuable extensions of the classic algorithm: you can learn what Morris&#8217;s counters and exponential decay have in common, how to improve the accuracy by sacrificing the maximum value, and how to handle weighted events efficiently.<\/p>\n<h3>2 Probabilistic Counting, problems of the trivial approach<\/h3>\n<p>The main idea of probabilistic counting is taking into account the next event with a certain probability. Let us first consider an example with a constant update rate:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $p = const$}  \\\\          C_n     &amp; \\textit{with probability $1-p$.}     \\end{array}\\right. \\end{equation*}\" alt=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $p = const$}  \\\\          C_n     &amp; \\textit{with probability $1-p$.}     \\end{array}\\right. \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ffa\/b7c\/fdb\/ffab7cfdba998f20947ad856a2fdc171.svg\" width=\"377\" height=\"50\"\/><\/p>\n<p>Where\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"C_n\" alt=\"C_n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/425\/257\/923\/42525792353434a38ccddf62f1dbfd62.svg\"\/>denotes the counter value after the\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b7a\/a70\/5b4\/b7aa705b4c6e3fcaadb92028c870db86.svg\"\/>th update attempt. It is easy to see that\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"C_n\" alt=\"C_n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5dc\/63d\/2f6\/5dc63d2f61f65cda64c80761f050beb4.svg\"\/>gives the number of successes among\u00a0nn\u00a0independent Bernoulli trials. In other words,\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"C_n\" alt=\"C_n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1dc\/893\/0b6\/1dc8930b6a49dfbdceafa11bb5452f1f.svg\"\/>has a\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Binomial_distribution\">binomial distribution<\/a>:  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}             C_n \\sim Bin(&amp;n, p) \\\\     {\\sf E} C_n    = &amp;np        \\\\     {\\sf D} C_n    = &amp;np(1-p). \\end{align*}\" alt=\"\\begin{align*}             C_n \\sim Bin(&amp;n, p) \\\\     {\\sf E} C_n    = &amp;np        \\\\     {\\sf D} C_n    = &amp;np(1-p). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/242\/42d\/584\/24242d584a0caee6005c21794303d8db.svg\" width=\"180\" height=\"74\"\/><\/p>\n<p>The problem we are to solve is to estimate the actual number of events <img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/efa\/022\/a9d\/efa022a9d093f7e4f5c6ed795c6e6b50.svg\"\/>given only the random value <img decoding=\"async\" class=\"formula inline\" source=\"C_n\" alt=\"C_n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3ab\/87e\/f23\/3ab87ef238fde99a4f9a95066e77967f.svg\"\/>obtained accordingly to the described update rule. It is natural to use <img decoding=\"async\" class=\"formula inline\" source=\"\\widehat{n}(C) := C\/p\" alt=\"\\widehat{n}(C) := C\/p\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c78\/896\/386\/c78896386f7e1e4d2b208a8159d2d1d8.svg\"\/>as an estimator, since we know <img decoding=\"async\" class=\"formula inline\" source=\"C_n = np\" alt=\"C_n = np\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f2e\/ef2\/902\/f2eef2902c2f54dae6e06bc775f3a6b3.svg\"\/>on average.<\/p>\n<p>The described approach has several drawbacks: first, it allows to count up to <img decoding=\"async\" class=\"formula inline\" source=\"1\/p = const\" alt=\"1\/p = const\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b99\/96e\/e10\/b9996ee10f47bed7fa2db9c7b95ae42b.svg\"\/>times larger numbers comparing to the ordinary incremental counter, which is still noticeably less than, as we will see below, we get with Morris&#8217;s counters. Second, the <em>relative error<\/em> is high for small <img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0eb\/8b8\/f25\/0eb8b8f25208801eadcb316fd549ea7b.svg\"\/>. For example, if <img decoding=\"async\" class=\"formula inline\" source=\"n = 1\" alt=\"n = 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9ed\/9a0\/9ce\/9ed9a09cefa2ffc7782515605283f075.svg\"\/>it is up to 100%. The coefficient of variation can estimate the relative error:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     {\\sf CV}\\widehat{n}(C_n) = \\frac{\\sqrt{{\\sf D}\\widehat{n}(C_n)}}{{\\sf E} \\widehat{n}(C_n)} = \\sqrt{\\frac{1-p}{pn}}. \\end{align*}\" alt=\"\\begin{align*}     {\\sf CV}\\widehat{n}(C_n) = \\frac{\\sqrt{{\\sf D}\\widehat{n}(C_n)}}{{\\sf E} \\widehat{n}(C_n)} = \\sqrt{\\frac{1-p}{pn}}. \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/af5\/80d\/da9\/af580dda9f4cf362989bb293a83b6a54.svg\" width=\"298\" height=\"62\"\/><\/p>\n<h3>3\u00a0Morris&#8217;s counters<\/h3>\n<p>The Morris&#8217;s counter updates with a probability dependent on the current value: the first update occurs with a probability of 1, the next with 1\/2, then 1\/4, etc.:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $2^{-C_n}$} \\\\          C_n     &amp; \\textit{with probability $1 - 2^{-C_n}$.}      \\end{array}\\right. \\end{equation*}\" alt=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $2^{-C_n}$} \\\\          C_n     &amp; \\textit{with probability $1 - 2^{-C_n}$.}      \\end{array}\\right. \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fd3\/66f\/cee\/fd366fceeca4a726c2a3da638dda96fb.svg\" width=\"375\" height=\"53\"\/><\/p>\n<p>How to estimate the number of events by the current counter value? Updating the counter from <img decoding=\"async\" class=\"formula inline\" source=\"x\" alt=\"x\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ca0\/ed5\/c74\/ca0ed5c7490ce09e8eba1bb96c629173.svg\"\/>to <img decoding=\"async\" class=\"formula inline\" source=\"x + 1\" alt=\"x + 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/779\/2c1\/cf1\/7792c1cf15f8dbc5d4c6c25eb48e8649.svg\"\/>occurs on average after <img decoding=\"async\" class=\"formula inline\" source=\"2^x\" alt=\"2^x\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1a8\/611\/13a\/1a861113a5b8ed42d102bd369418d6e0.svg\"\/>iterations, hence a reasonable estimator would be<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\widehat{n}(C) := \\sum_{i=0}^{C-1} 2^i = 2^C - 1\" alt=\"\\widehat{n}(C) := \\sum_{i=0}^{C-1} 2^i = 2^C - 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5ff\/409\/2b6\/5ff4092b632a683ec228eb97dd55cce8.svg\" width=\"200\" height=\"60\"\/><\/p>\n<p>The most important properties of this estimator are the following:<\/p>\n<ul>\n<li>\n<p>it is\u00a0<strong>unbiased<\/strong>, that is, the estimate after\u00a0nn\u00a0updates is on average\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/526\/27a\/686\/52627a6860897ce5a442ad77be4e6d66.svg\"\/>,<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\"><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\sf E}\\left(2^{C_n} | C_{n-1} = y\\right) =&amp; \\underbrace{2^{y + 1} 2^{-y}}_{\\textit{increment happened}} + \\underbrace{2^y (1 - 2^{-y})}_{\\textit{no change}} = 2^y + 1 \\implies \\\\         {\\sf E}(2^{C_n}|C_{n-1}) =&amp; 2^{C_{n-1}} + 1 \\implies {\\sf E}2^{C_n} = {\\sf E}2^{C_{n-1}} + 1 = {\\sf E}2^{C_{n-2}} + 2 = \\dots     \\end{align*}\" alt=\"\\begin{align*}         {\\sf E}\\left(2^{C_n} | C_{n-1} = y\\right) =&amp; \\underbrace{2^{y + 1} 2^{-y}}_{\\textit{increment happened}} + \\underbrace{2^y (1 - 2^{-y})}_{\\textit{no change}} = 2^y + 1 \\implies \\\\         {\\sf E}(2^{C_n}|C_{n-1}) =&amp; 2^{C_{n-1}} + 1 \\implies {\\sf E}2^{C_n} = {\\sf E}2^{C_{n-1}} + 1 = {\\sf E}2^{C_{n-2}} + 2 = \\dots     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cf7\/4bd\/4fa\/cf74bd4fa9a481d6cf152a77a6830b73.svg\" width=\"607\" height=\"85\"\/><\/p>\n<p>At the very beginning, the counter value is 0: <img decoding=\"async\" class=\"formula inline\" source=\"C_0 = 0\" alt=\"C_0 = 0\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cd1\/184\/6bf\/cd11846bfd1e82f94fc604adec3094c7.svg\"\/>then <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}2^{C_0} = 2^{0} = 1\" alt=\"{\\sf E}2^{C_0} = 2^{0} = 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3f4\/5c6\/7df\/3f45c67df30d8fbff4d8cd6a03d47014.svg\"\/>, so <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E} 2^{C_n} = \\dots = n + 1\" alt=\"{\\sf E} 2^{C_n} = \\dots = n + 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/439\/159\/64f\/43915964f604a077a7aa92e3ef3ba1ad.svg\"\/>. Finally, <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}\\widehat{n}(C_n) = {\\sf E}2^{C_n} - 1 = n\" alt=\"{\\sf E}\\widehat{n}(C_n) = {\\sf E}2^{C_n} - 1 = n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/62e\/c7b\/78d\/62ec7b78d441077056033bcec618db62.svg\"\/>.<\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>and its\u00a0<strong>relative error<\/strong>\u00a0is\u00a0<strong>independent<\/strong>\u00a0of\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4bb\/b9a\/849\/4bbb9a8498cbf207e666db2680cac5c7.svg\"\/>.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>First, let&#8217;s calculate the variance of <img decoding=\"async\" class=\"formula inline\" source=\"\\widehat{n}(C_n) \" alt=\"\\widehat{n}(C_n) \" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/33d\/600\/e6a\/33d600e6acea9ce012c9e2c1837d28c2.svg\"\/>. According to the well-known formula, <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf D}\\widehat{n} = {\\sf E}\\widehat{n}^2 - ({\\sf E}\\widehat{n})^2\" alt=\"{\\sf D}\\widehat{n} = {\\sf E}\\widehat{n}^2 - ({\\sf E}\\widehat{n})^2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/692\/ca8\/cff\/692ca8cff772cef50a060243707c14dd.svg\"\/>. We already know <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}\\widehat{n}(C_n) = n\" alt=\"{\\sf E}\\widehat{n}(C_n) = n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f2b\/a44\/c21\/f2ba44c210921679640070ae4b76fcfa.svg\"\/>. It remains to get <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}\\widehat{n}(C_n)^2\" alt=\"{\\sf E}\\widehat{n}(C_n)^2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/13c\/f6a\/e33\/13cf6ae334344f1b298ac7359a00015c.svg\"\/>. Notice, that<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\sf E}\\left(2^{C_n} - 1\\right)^2 = {\\sf E}2^{2C_n} - 2(n+1) + 1 = {\\sf E}2^{2C_n} - 2n - 1.     \\end{align*}\" alt=\"\\begin{align*}         {\\sf E}\\left(2^{C_n} - 1\\right)^2 = {\\sf E}2^{2C_n} - 2(n+1) + 1 = {\\sf E}2^{2C_n} - 2n - 1.     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/95a\/682\/394\/95a6823941790bebf22f455c9ecb4d33.svg\" width=\"460\" height=\"31\"\/><\/p>\n<p>So, we are only to find <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}2^{2C_n}\" alt=\"{\\sf E}2^{2C_n}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6cb\/559\/d0f\/6cb559d0fb5938ab30d2a52ac442bf90.svg\"\/>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\sf E}\\left(2^{2C_n} | C_{n-1} = y\\right) &amp;=         \\underbrace{2^{2(y + 1)} 2^{-y}}_{\\textit{increment happened}} +         \\underbrace{2^{2y}(1 - 2^{-y})}_{\\textit{no change}} = \\\\         = 3 \\cdot 2^y +2^{2y} &amp;\\implies         {\\sf E}\\left(2^{2C_n} | C_{n-1}\\right) = 3 \\cdot 2^{C_{n-1}} + 2^{2C_{n-1}} \\implies \\\\         {\\sf E} 2^{2C_n} = 3n &amp;+ {\\sf E}2^{2C_{n-1}} = 3n + 3(n-1) + {\\sf E}2^{2C_{n-2}} = \\dots = \\sum_{i=1}^n 3i     \\end{align*}\" alt=\"\\begin{align*}         {\\sf E}\\left(2^{2C_n} | C_{n-1} = y\\right) &amp;=         \\underbrace{2^{2(y + 1)} 2^{-y}}_{\\textit{increment happened}} +         \\underbrace{2^{2y}(1 - 2^{-y})}_{\\textit{no change}} = \\\\         = 3 \\cdot 2^y +2^{2y} &amp;\\implies         {\\sf E}\\left(2^{2C_n} | C_{n-1}\\right) = 3 \\cdot 2^{C_{n-1}} + 2^{2C_{n-1}} \\implies \\\\         {\\sf E} 2^{2C_n} = 3n &amp;+ {\\sf E}2^{2C_{n-1}} = 3n + 3(n-1) + {\\sf E}2^{2C_{n-2}} = \\dots = \\sum_{i=1}^n 3i     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/ede\/e49\/5fd\/edee495fd0aa4f8a67426a48fd6b5d82.svg\" width=\"582\" height=\"147\"\/><\/p>\n<p>Finally,<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}         {\\sf D}\\widehat{n}(C_n) = \\frac{3n(n+1)}{2} - 2n - 1 - n^2 = \\frac{n^2 - n - 2}{2}     \\end{equation*}\" alt=\"\\begin{equation*}         {\\sf D}\\widehat{n}(C_n) = \\frac{3n(n+1)}{2} - 2n - 1 - n^2 = \\frac{n^2 - n - 2}{2}     \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c79\/ad0\/c14\/c79ad0c14198a5da9f45af9af6b9ef29.svg\" width=\"421\" height=\"46\"\/><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}  {\\sf CV}\\widehat{n}(C_n) = \\frac{\\sqrt{{\\sf D}\\widehat{n}(C_n)}}{{\\sf E}\\widehat{n}(C_n)} = \\sqrt{\\frac{n^2-n-2}{2n^2}} \\sim \\frac{1}{\\sqrt{2}}.     \\end{equation*}\" alt=\"\\begin{equation*}  {\\sf CV}\\widehat{n}(C_n) = \\frac{\\sqrt{{\\sf D}\\widehat{n}(C_n)}}{{\\sf E}\\widehat{n}(C_n)} = \\sqrt{\\frac{n^2-n-2}{2n^2}} \\sim \\frac{1}{\\sqrt{2}}.     \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fb3\/760\/986\/fb3760986173f393829d10b0d6d0f492.svg\" width=\"396\" height=\"56\"\/><\/p>\n<p>And also, we can bound tail probabilities by\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Chebyshev's_inequality\">Chebyshev&#8217;s inequality<\/a>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\sf P}(|\\widehat{n}(C_n) - n| \\ge \\varepsilon n) \\le \\frac{{\\sf D}\\widehat{n}(C_n)}{\\varepsilon^2n^2} \\sim \\frac{1}{2\\varepsilon^2}.     \\end{align*}\" alt=\"\\begin{align*}         {\\sf P}(|\\widehat{n}(C_n) - n| \\ge \\varepsilon n) \\le \\frac{{\\sf D}\\widehat{n}(C_n)}{\\varepsilon^2n^2} \\sim \\frac{1}{2\\varepsilon^2}.     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/10e\/57a\/714\/10e57a7145a5d588ea268aee4309f67b.svg\" width=\"338\" height=\"46\"\/><\/p>\n<p>Thus, the relative error of the number of events estimate\u00a0<strong>is bounded, approximately constant and almost independent of<\/strong>\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3cd\/3ef\/317\/3cd3ef317023447e9d2c9c787b4e6c98.svg\"\/>.  <\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>If we are restricted with\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\bf X}\" alt=\"{\\bf X}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dad\/246\/1be\/dad2461beeff2e37b25aa3e0dfcb6962.svg\"\/>bits of memory, the event number range we can cover is\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\bf N}_{max} = 2^{2^{\\bf X}} - 1\" alt=\"{\\bf N}_{max} = 2^{2^{\\bf X}} - 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/38d\/824\/712\/38d824712ce02a2b7a316ea099fbfe79.svg\"\/>. In other words,\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"\\mathcal{O}(\\log \\log n)\" alt=\"\\mathcal{O}(\\log \\log n)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7fc\/ab8\/f37\/7fcab8f373ee97cd962d7e0bc231c1c4.svg\"\/>bits are sufficient to account up to\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d10\/d6f\/127\/d10d6f1276437132aa595fb03aa67bd7.svg\"\/>events!<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5b3\/158\/561\/5b31585614173622b45ef2e328ca9403.png\" alt=\"Figure 1: relative error of the event number estimate while using binomial counters and Morris's counters (the plot was obtained by sample_size=100 times simulating the evolution process of the counter and mapping each point n onto the average relative error of estimate at this point)\" title=\"Figure 1: relative error of the event number estimate while using binomial counters and Morris's counters (the plot was obtained by sample_size=100 times simulating the evolution process of the counter and mapping each point n onto the average relative error of estimate at this point)\" width=\"980\" height=\"420\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5b3\/158\/561\/5b31585614173622b45ef2e328ca9403.png\"\/><figcaption>Figure 1: relative error of the event number estimate while using binomial counters and Morris&#8217;s counters (the plot was obtained by sample_size=100 times simulating the evolution process of the counter and mapping each point n onto the average relative error of estimate at this point)<\/figcaption><\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0b9\/fe9\/b62\/0b9fe9b62215793ce3fabb9049e201ae.png\" width=\"499\" height=\"208\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0b9\/fe9\/b62\/0b9fe9b62215793ce3fabb9049e201ae.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>4\u00a0Weighted updates<\/h3>\n<p>Morris&#8217;s counters can be used to count weighted events: for example, to sum up the sizes of incoming packets. The simplest way \u2014 updating the counter &lt;event weight> times \u2014 leads the update complexity to be linear in weight.<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/dbb\/8db\/05e\/dbb8db05e3c08d9dc2c81ab02cafa17a.png\" alt=\"\" title=\"\" width=\"497\" height=\"191\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dbb\/8db\/05e\/dbb8db05e3c08d9dc2c81ab02cafa17a.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Note that at each moment, we know how the number of update failures before the subsequent success is distributed; this is the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Geometric_distribution\">geometric distribution<\/a>:  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     {\\sf P}(n_{fail} = k) = (1 - 2^{-C})^k 2^{-C} \\implies n_{fail} \\sim Geom(2^{-C}). \\end{align*}\" alt=\"\\begin{align*}     {\\sf P}(n_{fail} = k) = (1 - 2^{-C})^k 2^{-C} \\implies n_{fail} \\sim Geom(2^{-C}). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/699\/c31\/658\/699c316583af2ccdbb95c2257cc148a2.svg\" width=\"473\" height=\"26\"\/><\/p>\n<p>Given this fact, we can speed up the algorithm by immediately skipping all consecutive unsuccessful update attempts:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b6e\/28c\/621\/b6e28c6214187535f58f1178bd2aaa67.png\" width=\"496\" height=\"187\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b6e\/28c\/621\/b6e28c6214187535f58f1178bd2aaa67.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>5\u00a0Decay<\/h3>\n<p>There is often a need to take into account the most recent events with a higher priority. For instance, when looking for intensive keys in a stream, we might be more interested in detecting keys intense right now, rather than ones having a high average intensity due to the events in the past. In the <a href=\"https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5\">article<\/a> [3], we discussed the decay counters explicitly designed for this purpose. Another prerequisite for some form of decay is that despite the super-economical use of memory, even Morris&#8217;s counters once may overflow. Fortunately, it turns out Morris&#8217;s counters can also decay, moreover, it is very similar to EMA but much more efficient.<\/p>\n<p>The basic idea is quite simple: we periodically subtract <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1b0\/394\/5c4\/1b03945c410b410711fae44cc17363b2.svg\"\/>from the counter. Recall the counter value of <img decoding=\"async\" class=\"formula inline\" source=\"C\" alt=\"C\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/619\/62e\/8eb\/61962e8eb889b68260abd12977013577.svg\"\/>estimates the number of events as <img decoding=\"async\" class=\"formula inline\" source=\"\\sum_{i = 0}^{C-1} 2^i = 2^C - 1 =: n_0\" alt=\"\\sum_{i = 0}^{C-1} 2^i = 2^C - 1 =: n_0\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1f3\/a9c\/713\/1f3a9c71343bc1fbe8973b6853de268f.svg\"\/>. <\/p>\n<p>Accordingly, after subtracting <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c97\/64e\/2f6\/c9764e2f60eac1cd4202bb78a8657f8c.svg\"\/>the estimate becomes <img decoding=\"async\" class=\"formula inline\" source=\"2^{C-1} - 1 = \\frac{1}{2} n_0 - \\frac{1}{2}\" alt=\"2^{C-1} - 1 = \\frac{1}{2} n_0 - \\frac{1}{2}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9ad\/318\/5db\/9ad3185db71101eca83759ce768beda0.svg\"\/>. <\/p>\n<p>In other words, subtracting <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d56\/9c4\/b9d\/d569c4b9d7f45fe7d668f17d83c8b535.svg\"\/>decreases the estimate by a factor of <img decoding=\"async\" class=\"formula inline\" source=\"\\sim 2\" alt=\"\\sim 2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/89c\/14a\/bc1\/89c14abc127e4a1421bc0fa6d8688963.svg\"\/>! We can achieve exactly <img decoding=\"async\" class=\"formula inline\" source=\"2\" alt=\"2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bd7\/a1e\/98e\/bd7a1e98e1f85a30808b3419e1a90f1c.svg\"\/>times decrease by updating the counter with probability <img decoding=\"async\" class=\"formula inline\" source=\"\\frac{1}{2}\" alt=\"\\frac{1}{2}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9b7\/412\/74d\/9b741274d08465e9db781f75690cb260.svg\"\/>immediately after subtracting <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5f0\/d4d\/701\/5f0d4d701f5253bf6bbb3a6fc2632b0e.svg\"\/>:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f11\/325\/32c\/f1132532cb68505a1b4a033ead547ae5.png\" width=\"497\" height=\"174\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f11\/325\/32c\/f1132532cb68505a1b4a033ead547ae5.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>6\u00a0Mantissa and exponent<\/h3>\n<p>Consider the following generalization of Morris&#8217;s counters: the probability of an update is halved not on every successful update, but on every\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\bf X}\" alt=\"{\\bf X}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6d5\/d56\/4f9\/6d5d564f992be883c1d2977d6e49279b.svg\"\/>updates. One possible implementation of this approach would be splitting the counter bits into two parts:\u00a0<em>exponential<\/em>, responsible for the probability, and\u00a0<em>significand<\/em>, counting the number of successful updates within the current probability (the notions are taken from\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Floating-point_arithmetic\">floating point arithmetic<\/a>).<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/fc2\/aa9\/365\/fc2aa9365bddd7bdf2285af5933f6bdb.png\" alt=\"Figure 2: splitting counter bits into E=3 exponential and  M=5 significand\" title=\"Figure 2: splitting counter bits into E=3 exponential and  M=5 significand\" width=\"355\" height=\"72\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/fc2\/aa9\/365\/fc2aa9365bddd7bdf2285af5933f6bdb.png\"\/><figcaption>Figure 2: splitting counter bits into E=3 exponential and  M=5 significand<\/figcaption><\/figure>\n<p>Let us introduce the notation for the exponential and significand parts:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     {\\rm e}(C)  &amp;= C \\gg {\\bf M} \\\\     {\\rm m}(C)  &amp;= C \\&amp;  (2^{\\bf M} - 1). \\end{align*}\" alt=\"\\begin{align*}     {\\rm e}(C)  &amp;= C \\gg {\\bf M} \\\\     {\\rm m}(C)  &amp;= C \\&amp;  (2^{\\bf M} - 1). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/28f\/d85\/538\/28fd8553840d6df6b45a6ae011c2ed7f.svg\" width=\"187\" height=\"51\"\/><\/p>\n<p>Now the update rule and the estimator of the number of events are written as follows:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     C_{n+1} &amp;= \\left\\{\\begin{array}{ll}          C_n + 1, &amp; \\textit{with probability $2^{-{\\rm e}(C)}$}  \\\\          C_n,     &amp; \\textit{with probability $1 - 2^{-{\\rm e}(C)}$}     \\end{array}\\right. \\\\     \\widehat{n}(C) &amp;= (2^{{\\rm e}(C)} - 1)2^{\\bf M} + 2^{{\\rm e}(C)}{\\rm m}(C). \\end{align*}\" alt=\"\\begin{align*}     C_{n+1} &amp;= \\left\\{\\begin{array}{ll}          C_n + 1, &amp; \\textit{with probability $2^{-{\\rm e}(C)}$}  \\\\          C_n,     &amp; \\textit{with probability $1 - 2^{-{\\rm e}(C)}$}     \\end{array}\\right. \\\\     \\widehat{n}(C) &amp;= (2^{{\\rm e}(C)} - 1)2^{\\bf M} + 2^{{\\rm e}(C)}{\\rm m}(C). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8f0\/782\/400\/8f0782400990a8dbc89bde1354edc8d7.svg\" width=\"390\" height=\"83\"\/><\/p>\n<p>For example, on figure 2 counter value is\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"C = 89\" alt=\"C = 89\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/668\/976\/431\/668976431dfd58135d6f5493b257552a.svg\"\/>, the mantissa and exponent are equal to\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\rm m} = 25\" alt=\"{\\rm m} = 25\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6a2\/c4f\/5d3\/6a2c4f5d388077b32e553679caf66a14.svg\"\/>and\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\rm e} = 2\" alt=\"{\\rm e} = 2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/611\/352\/e66\/611352e6633a82405cdb442b2ea89816.svg\"\/>correspondingly. So the event number estimate is\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"\\widehat{n} = (2^2 - 1) \\times 2^5 + 2^2 \\times 25 = 196\" alt=\"\\widehat{n} = (2^2 - 1) \\times 2^5 + 2^2 \\times 25 = 196\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/e93\/046\/efb\/e93046efbd25dc6d2f094cc716be896f.svg\"\/>.  <\/p>\n<p>With such a partition, the bits of the exponent are responsible for the covered range of the number of events, and the bits of the mantissa determine the accuracy of the estimate (these claims are described in detail below). The best partition of the counter bits depends on the application; we suggest giving as many bits to the mantissa as possible, but with enough exponential bits to cover the target value range.<\/p>\n<p>The described variant of Morris&#8217;s counters has the following properties:<\/p>\n<ul>\n<li>\n<p>the covered range of values is<\/p>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\bf N}_{max} = 2^{2^{\\bf E} + {\\bf M}} - \\left(2^{2^{\\bf E} - 1} + 2^{\\bf M}\\right);     \\end{align*}\" alt=\"\\begin{align*}         {\\bf N}_{max} = 2^{2^{\\bf E} + {\\bf M}} - \\left(2^{2^{\\bf E} - 1} + 2^{\\bf M}\\right);     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/045\/635\/d62\/045635d62cb80be42ca9ceee51ca19c7.svg\" width=\"280\" height=\"38\"\/><\/p>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>The covered range is easy to get by substituting the maximum possible values  <img decoding=\"async\" class=\"formula inline\" source=\"{\\rm m} = 2^{\\bf M} - 1\" alt=\"{\\rm m} = 2^{\\bf M} - 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dc2\/5cd\/fc2\/dc25cdfc23272552de0381c2ec17a2c3.svg\"\/>and <img decoding=\"async\" class=\"formula inline\" source=\"{\\rm e} = 2^{\\bf E} - 1\" alt=\"{\\rm e} = 2^{\\bf E} - 1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4f3\/cdf\/d59\/4f3cdfd599706e71ef4dfe9f11692e0d.svg\"\/>into the estimator <img decoding=\"async\" class=\"formula inline\" source=\"\\widehat{n}\" alt=\"\\widehat{n}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/741\/887\/679\/7418876794726b3dea31fbf8963ac5fd.svg\"\/>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}             {\\bf N}_{max} = \\left(2^{2^{\\bf E} - 1} - 1\\right)2^{\\bf M} + 2^{2^{\\bf E} - 1}\\left(2^{\\bf M} - 1\\right) = 2^{2^{\\bf E} + {\\bf M}} - \\left(2^{2^{\\bf E} - 1} + 2^{\\bf M}\\right)         \\end{align*}\" alt=\"\\begin{align*}             {\\bf N}_{max} = \\left(2^{2^{\\bf E} - 1} - 1\\right)2^{\\bf M} + 2^{2^{\\bf E} - 1}\\left(2^{\\bf M} - 1\\right) = 2^{2^{\\bf E} + {\\bf M}} - \\left(2^{2^{\\bf E} - 1} + 2^{\\bf M}\\right)         \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a1f\/316\/be9\/a1f316be90151f0e163e71d1fc8e2c91.svg\" width=\"576\" height=\"38\"\/><\/div>\n<\/details>\n<ul>\n<li>\n<p>a new estimator\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"\\widehat{n}\" alt=\"\\widehat{n}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d74\/4d4\/e8e\/d744d4e8e5484f18f94124ba0e497450.svg\"\/>is also unbiased:\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}\\widehat{n}(C_n) = n\" alt=\"{\\sf E}\\widehat{n}(C_n) = n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c67\/83e\/cf2\/c6783ecf2efab717abaf158221a7904f.svg\"\/>;<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>Similarly to the proof of the statement for the ordinary Morris&#8217;s counters, we calculate the conditional expectation <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf E}(C_n | C_{n - 1} = y)\" alt=\"{\\sf E}(C_n | C_{n - 1} = y)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/aa7\/aaa\/cd8\/aa7aaacd85b970262cabbc4634c80453.svg\"\/>. And we will do it separately for <img decoding=\"async\" class=\"formula inline\" source=\"y \\equiv -1 \\pmod{2^{\\bf M}}\" alt=\"y \\equiv -1 \\pmod{2^{\\bf M}}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a52\/972\/345\/a52972345b9dbbb3a3ffc7b2e632782e.svg\"\/>(the probability of the update changes on success) and <img decoding=\"async\" class=\"formula inline\" source=\"y \\not\\equiv -1 \\pmod{2^{\\bf M}}\" alt=\"y \\not\\equiv -1 \\pmod{2^{\\bf M}}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2ec\/821\/fe0\/2ec821fe09f5de8bf5c4c97ad2ae27fd.svg\"\/>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}             y \\not\\equiv -1 \\pmod{2^{\\bf M}} &amp;\\implies             {\\rm e}(y + 1) = {\\rm e}(y), \\quad {\\rm m}(y + 1) = {\\rm m}(y) + 1 \\\\             &amp;\\implies \\widehat{n}(y + 1) = \\widehat{n}(y) + 2^{{\\rm e}(y)} \\\\             y \\equiv -1 \\pmod{2^{\\bf M}} &amp; \\implies {\\rm e}(y + 1) = {\\rm e}(y) + 1,\\\\             &amp;\\phantom{\\implies} \\phantom{Z} {\\rm m}(y) = 2^{\\bf M} - 1, \\quad {\\rm m}(y + 1) = 0 \\implies \\\\             \\widehat{n}(y + 1) = \\widehat{n}(y) &amp;- 2^{{\\rm e}(y)}\\left(2^{\\bf M} - 1\\right) + 2^{{\\rm e}(y) + {\\bf M}} = \\widehat{n}(y) + 2^{{\\rm e}(y)}         \\end{align*}\" alt=\"\\begin{align*}             y \\not\\equiv -1 \\pmod{2^{\\bf M}} &amp;\\implies             {\\rm e}(y + 1) = {\\rm e}(y), \\quad {\\rm m}(y + 1) = {\\rm m}(y) + 1 \\\\             &amp;\\implies \\widehat{n}(y + 1) = \\widehat{n}(y) + 2^{{\\rm e}(y)} \\\\             y \\equiv -1 \\pmod{2^{\\bf M}} &amp; \\implies {\\rm e}(y + 1) = {\\rm e}(y) + 1,\\\\             &amp;\\phantom{\\implies} \\phantom{Z} {\\rm m}(y) = 2^{\\bf M} - 1, \\quad {\\rm m}(y + 1) = 0 \\implies \\\\             \\widehat{n}(y + 1) = \\widehat{n}(y) &amp;- 2^{{\\rm e}(y)}\\left(2^{\\bf M} - 1\\right) + 2^{{\\rm e}(y) + {\\bf M}} = \\widehat{n}(y) + 2^{{\\rm e}(y)}         \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a48\/d05\/49b\/a48d0549b6002231e5689c58943dbb9f.svg\" width=\"552\" height=\"140\"\/><\/p>\n<p>In both cases  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}             {\\sf E}\\left(2^{C_n} | C_{n-1} = y\\right) &amp;= \\widehat{n}(y)\\left(1 - 2^{-{\\rm e}(y)}\\right) + \\left(\\widehat{n}(y) + 2^{{\\rm e}(y)}\\right)2^{-{\\rm e}(y)} = \\widehat{n}(y) + 1.         \\end{align*}\" alt=\"\\begin{align*}             {\\sf E}\\left(2^{C_n} | C_{n-1} = y\\right) &amp;= \\widehat{n}(y)\\left(1 - 2^{-{\\rm e}(y)}\\right) + \\left(\\widehat{n}(y) + 2^{{\\rm e}(y)}\\right)2^{-{\\rm e}(y)} = \\widehat{n}(y) + 1.         \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8bc\/a4b\/5cb\/8bca4b5cb0989df193537c4ac613fc96.svg\" width=\"606\" height=\"38\"\/><\/div>\n<\/details>\n<ul>\n<li>\n<p>the coefficient of variation (and the relative error) gets even smaller! We do not know its exact value, but there is a bound:<\/p>\n<\/li>\n<\/ul>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}         {\\sf CV}\\widehat{n}(C_n) \\le 2^{-\\frac{{\\bf M} + 1}{2}}.     \\end{align*}\" alt=\"\\begin{align*}         {\\sf CV}\\widehat{n}(C_n) \\le 2^{-\\frac{{\\bf M} + 1}{2}}.     \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/93c\/1d2\/142\/93c1d2142150f13a38d076526f0540f4.svg\" width=\"165\" height=\"30\"\/><\/p>\n<p>\u00a0(recall the\u00a0analogous value\u00a0for classic Morris&#8217;s counter is\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"2^{-1\/2}\" alt=\"2^{-1\/2}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a69\/70a\/6dd\/a6970a6ddf25b82fa00179a00d68fedc.svg\"\/>)  <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/27d\/8bd\/05e\/27d8bd05ed84b079b37d4312a3025188.png\" alt=\"Figure 3\" title=\"Figure 3\" width=\"980\" height=\"420\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/27d\/8bd\/05e\/27d8bd05ed84b079b37d4312a3025188.png\"\/><figcaption>Figure 3<\/figcaption><\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/da5\/43e\/03a\/da543e03acacc01f33104907134b0b8a.png\" width=\"501\" height=\"246\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/da5\/43e\/03a\/da543e03acacc01f33104907134b0b8a.png\"\/><figcaption><\/figcaption><\/figure>\n<p>Weighted update and decay algorithms will also change slightly.<\/p>\n<p>Similarly to the approach described in\u00a0Section 4, to perform a weighted update efficiently, we are to figure out how many update attempts are needed to change the probability. Formerly, without the mantissa, the probability changes on each successful update, but now it changes after every\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"2^{\\bf M}\" alt=\"2^{\\bf M}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/494\/cc0\/f6a\/494cc0f6a3101f3e0c2142e28871d2f8.svg\"\/>updates. So we need the distribution of the number of Bernoulli trials (the required number of events), among which there are precisely\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"2^{\\bf M}\" alt=\"2^{\\bf M}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/28f\/3f5\/b7c\/28f3f5b7c4f49c5f0e1c834328ad91a9.svg\"\/>successful ones. This distribution is called\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Negative_binomial_distribution\">negative binomial<\/a>.<\/p>\n<p><strong><sub>Remark.<\/sub><\/strong><sub>\u00a0<\/sub><em><sub>Actually, there are several definitions of the negative binomial distribution. We will use the one that claims it as a distribution of the number of failures with a given number of successes. Then, to get the number of events, we need to add the number of successes to the random value:<\/sub><\/em>  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     n \\sim \\underbrace{NegativeBinomial\\left(2^{-{\\rm e}(C)}, 2^{\\bf M} - {\\rm m}(C)\\right)}_{\\textit{failures}}         + \\underbrace{\\left(2^{\\bf M} - {\\rm m}(C)\\right)}_{\\textit{successes}}. \\end{align*}\" alt=\"\\begin{align*}     n \\sim \\underbrace{NegativeBinomial\\left(2^{-{\\rm e}(C)}, 2^{\\bf M} - {\\rm m}(C)\\right)}_{\\textit{failures}}         + \\underbrace{\\left(2^{\\bf M} - {\\rm m}(C)\\right)}_{\\textit{successes}}. \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/744\/78b\/0cd\/74478b0cdfe5cb3e5f259903437ea90c.svg\" width=\"525\" height=\"69\"\/><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a86\/e0e\/4f5\/a86e0e4f5bd40e4c691411501637b72c.png\" width=\"502\" height=\"313\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a86\/e0e\/4f5\/a86e0e4f5bd40e4c691411501637b72c.png\"\/><figcaption><\/figcaption><\/figure>\n<p>In order to avoid generating the negative binomial distribution, one can simplify the algorithm by just using the mean of this distribution:  <\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b4d\/8ff\/c8c\/b4d8ffc8c1ae9c1a3eefb078d68168c5.png\" width=\"499\" height=\"297\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b4d\/8ff\/c8c\/b4d8ffc8c1ae9c1a3eefb078d68168c5.png\"\/><figcaption><\/figcaption><\/figure>\n<p>To perform the decay, we subtract\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/425\/4dd\/f02\/4254ddf020e8055af5e64b18c1f877a4.svg\"\/>from the\u00a0<strong>exponential part<\/strong>\u00a0(in other words,\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"2^{\\bf M}\" alt=\"2^{\\bf M}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/324\/5f1\/aa5\/3245f1aa5e9e2016feb2c191ce5ce4dd.svg\"\/>from the counter value). Then  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     \\widehat{n}\\left(C - 2^{\\bf M}\\right) = (2^{{\\rm e}(C) - 1} - 1)2^{\\bf M} + 2^{{\\rm e}(C) - 1}{\\rm m}(C) = 1\/2\\widehat{n}(C) - 2^{{\\bf M}-1}. \\end{align*}\" alt=\"\\begin{align*}     \\widehat{n}\\left(C - 2^{\\bf M}\\right) = (2^{{\\rm e}(C) - 1} - 1)2^{\\bf M} + 2^{{\\rm e}(C) - 1}{\\rm m}(C) = 1\/2\\widehat{n}(C) - 2^{{\\bf M}-1}. \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0ca\/642\/c79\/0ca642c7974078720b6f9d31bf689e2c.svg\" width=\"553\" height=\"28\"\/><\/p>\n<p>Note that for <img class=\"formula inline\" source=\"{\\bf M}>0&#8243; alt=&#187;{\\bf M}>0&#8243; src=&#187;https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c52\/67b\/c08\/c5267bc086a7aab144a9df69971e6a59.svg&#187;\/>(namely, this case is studied in this section) <img decoding=\"async\" class=\"formula inline\" source=\" 2^{{\\bf M}-1} \\in \\mathbb{N}\" alt=\" 2^{{\\bf M}-1} \\in \\mathbb{N}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3cb\/bcf\/244\/3cbbcf24492f9bccab7dc37ff4a5a652.svg\"\/>. Therefore, the estimate can be corrected by updating by <img decoding=\"async\" class=\"formula inline\" source=\"2^{{\\bf M}-1}\" alt=\"2^{{\\bf M}-1}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/57a\/cc3\/6b6\/57acc36b65036b6719429320c58d4421.svg\"\/>:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/10f\/cd6\/ba5\/10fcd6ba5ce32f3bd69443c5565e2a98.png\" width=\"499\" height=\"272\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/10f\/cd6\/ba5\/10fcd6ba5ce32f3bd69443c5565e2a98.png\"\/><figcaption><\/figcaption><\/figure>\n<h3>7 Conclusion<\/h3>\n<p>Morris&#8217;s counters are awesome! Use them!<\/p>\n<p>The last section does not contain practical results, but &#8230;<\/p>\n<h3>8\u00a0Generalized Probabilistic Counters<\/h3>\n<p>In general, the rule for updating the counter is determined by the probability function\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"F : C \\mapsto [0, 1]\" alt=\"F : C \\mapsto [0, 1]\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6c9\/c91\/03b\/6c9c9103b1b75b431d96fdf0ed01b5a5.svg\"\/>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $F(C_n)$} \\\\          C_n     &amp; \\textit{with probability $1 - F(C_n)$.}      \\end{array}\\right. \\end{equation*}\" alt=\"\\begin{equation*}     C_{n+1} = \\left\\{\\begin{array}{ll}          C_n + 1 &amp; \\textit{with probability $F(C_n)$} \\\\          C_n     &amp; \\textit{with probability $1 - F(C_n)$.}      \\end{array}\\right. \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/40f\/77e\/923\/40f77e923e7e29ade826faa5b649f723.svg\" width=\"389\" height=\"51\"\/><\/p>\n<p>In the case of Morris&#8217;s counters <img decoding=\"async\" class=\"formula inline\" source=\"F(C_n) = 2^{-C_n} \" alt=\"F(C_n) = 2^{-C_n} \" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0a7\/6fe\/a4a\/0a76fea4abc442767e673f882b8c8db3.svg\"\/>. When studying probabilistic counters, we tried to take a linear probability function: <img decoding=\"async\" class=\"formula inline\" source=\"F(C_n) = 1 - \\frac{C_n}{C_{max}}\" alt=\"F(C_n) = 1 - \\frac{C_n}{C_{max}}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/365\/415\/75d\/36541575d91de0be98286391266f0d7b.svg\"\/>( <img decoding=\"async\" class=\"formula inline\" source=\"C_{max}\" alt=\"C_{max}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d77\/a35\/c9c\/d77a35c9c87fd40c90c065855b8f2c4f.svg\"\/>\u2014 some constant, the upper bound of the counter). <\/p>\n<p>This had led to some interesting results, which are summarized in this section.<\/p>\n<p>Before presenting these results, let us recall two mathematical entities. A\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Complete_homogeneous_symmetric_polynomial\"><em>complete symmetric homogeneous polynomial<\/em><\/a>\u00a0of degree\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7ca\/704\/6fe\/7ca7046fe29420000cf77c750f388631.svg\"\/>in\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"m\" alt=\"m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4bd\/a57\/3ca\/4bda573ca3eb36a6ff342bb606ad99da.svg\"\/>variables\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"x_1, \\dots, x_m\" alt=\"x_1, \\dots, x_m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2eb\/95f\/f68\/2eb95ff68e54bb68a93dd3580a649732.svg\"\/>is the sum of all possible monomials of degree\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2c3\/f91\/2a3\/2c3f912a3f8732ccdf3fd7db775ecf4f.svg\"\/>in these variables with coefficients\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/463\/028\/3d4\/4630283d47ea671cfaee6f8c9065990c.svg\"\/>:<\/p>\n<p> <img decoding=\"async\" class=\"formula inline\" source=\"\\begin{align*}     \\mathcal{H}_n(x_1, \\dots x_m) =      \\sum_{\\begin{array}{cc}             &amp; 0 \\le i_j \\le n \\\\             &amp; i_1 + \\dots + i_m = n         \\end{array}} x_1^{i_1} \\cdots x_m^{i_m}. \\end{align*}\" alt=\"\\begin{align*}     \\mathcal{H}_n(x_1, \\dots x_m) =      \\sum_{\\begin{array}{cc}             &amp; 0 \\le i_j \\le n \\\\             &amp; i_1 + \\dots + i_m = n         \\end{array}} x_1^{i_1} \\cdots x_m^{i_m}. \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/14b\/550\/127\/14b5501276ad64625bb5e3c79e674559.svg\"\/><\/p>\n<p>Another construction that we need is the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Stirling_numbers_of_the_second_kind\"><em>Stirling number of the second kind<\/em><\/a>. One of the ways to define this number is combinatorial: the Stirling number of the second kind\u00a0<img decoding=\"async\" class=\"formula inline\" source=\"\\genfrac\\{\\}{0pt}{}{n}{m}\" alt=\"\\genfrac\\{\\}{0pt}{}{n}{m}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6c7\/bfa\/32c\/6c7bfa32c76abaeecdfba4b9ee5e7ff4.svg\"\/>is the number of partitions of the\u00a0nn-element set into\u00a0mm\u00a0non-empty subsets. Stirling numbers are associated with complete symmetric homogeneous polynomials:  <\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     \\genfrac\\{\\}{0pt}{}{n+m}{m} = \\mathcal{H}_m(1, 2, \\dots, m). \\end{align*}\" alt=\"\\begin{align*}     \\genfrac\\{\\}{0pt}{}{n+m}{m} = \\mathcal{H}_m(1, 2, \\dots, m). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a40\/3ef\/336\/a403ef3361efd50dea9f21c93c3d0791.svg\" width=\"251\" height=\"49\"\/><\/p>\n<p>With the help of <img decoding=\"async\" class=\"formula inline\" source=\"\\mathcal{H}\" alt=\"\\mathcal{H}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/030\/656\/1be\/0306561beb5ea5287f91159df06c5b42.svg\"\/>, we can write in general form the probability of counter value after <img decoding=\"async\" class=\"formula inline\" source=\"n\" alt=\"n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/134\/d80\/6ac\/134d806acadf915bf9ddbedecad17a92.svg\"\/>updates being equal to <img decoding=\"async\" class=\"formula inline\" source=\"m\" alt=\"m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/009\/fbf\/6b8\/009fbf6b8871547a1cf91e93420c7836.svg\"\/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     {\\sf P}(C_n = m) = \\left[\\prod_{k=0}^{m-1} F(k) \\right] \\mathcal{H}_{n-m}(1-F(1), \\dots 1-F(m)). \\end{align*}\" alt=\"\\begin{align*}     {\\sf P}(C_n = m) = \\left[\\prod_{k=0}^{m-1} F(k) \\right] \\mathcal{H}_{n-m}(1-F(1), \\dots 1-F(m)). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/282\/792\/e5d\/282792e5d1499a6ccd8c8d6a574c0b47.svg\" width=\"479\" height=\"61\"\/><\/p>\n<p>This formula is easy to prove by induction. However, it can also be deduced from simple combinatorial reasoning: in fact, it says that in order to get the value of <img decoding=\"async\" class=\"formula inline\" source=\"m\" alt=\"m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f68\/9cf\/ee8\/f689cfee86a241e336fdfb50156bfbc2.svg\"\/>, <img decoding=\"async\" class=\"formula inline\" source=\"m\" alt=\"m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cbe\/50a\/a00\/cbe50aa00331af7b63eb1e7ec5e4dccc.svg\"\/>successful updates are needed: from <img decoding=\"async\" class=\"formula inline\" source=\"0\" alt=\"0\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/86c\/4b7\/570\/86c4b7570d003de29bf1b1f381282995.svg\"\/>to <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1b4\/a53\/68b\/1b4a5368ba11c9df9be6545a1be1af28.svg\"\/>, from <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1d9\/97e\/eb8\/1d997eeb8d9a8253f121c1153513cd34.svg\"\/>to <img decoding=\"async\" class=\"formula inline\" source=\"2\" alt=\"2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9f0\/11e\/526\/9f011e526062a38f8706f618354f0d7a.svg\"\/>, and etc., \u2014 the factor <img decoding=\"async\" class=\"formula inline\" source=\"\\prod_{k=0}^{m-1} F(k) \" alt=\"\\prod_{k=0}^{m-1} F(k) \" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/161\/cbe\/d3c\/161cbed3cb3906b6cc4adeae7b243d1e.svg\"\/>stands for this; <\/p>\n<p>and <img decoding=\"async\" class=\"formula inline\" source=\"n-m\" alt=\"n-m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7a5\/324\/237\/7a5324237a8f712670d84d6345da8607.svg\"\/>unsuccessful, which can be logically divided into <img decoding=\"async\" class=\"formula inline\" source=\"m\" alt=\"m\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/527\/f16\/92a\/527f1692aa2db7fbac1373c1b40df66e.svg\"\/>groups: <img decoding=\"async\" class=\"formula inline\" source=\"i_1\" alt=\"i_1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/226\/d56\/85e\/226d5685e2d722a2e18251ce10a40986.svg\"\/>failed updates from <img decoding=\"async\" class=\"formula inline\" source=\"1\" alt=\"1\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eeb\/59a\/d0b\/eeb59ad0b2b6798fe9844eac1933cd48.svg\"\/>to <img decoding=\"async\" class=\"formula inline\" source=\"2\" alt=\"2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/df9\/5ab\/6a0\/df95ab6a02404c70945a23d8100a673d.svg\"\/>, <img decoding=\"async\" class=\"formula inline\" source=\"i_2\" alt=\"i_2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/824\/a25\/37b\/824a2537ba83f724f75a0b234d2424de.svg\"\/>from <img decoding=\"async\" class=\"formula inline\" source=\"2\" alt=\"2\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/876\/2eb\/0fb\/8762eb0fb333b82f0ef2b4de766ebd9a.svg\"\/>to <img decoding=\"async\" class=\"formula inline\" source=\"3\" alt=\"3\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b6f\/6b2\/6b9\/b6f6b26b90ea477d1becbe549b1a342b.svg\"\/>, and so on, which is expressed as <img decoding=\"async\" class=\"formula inline\" source=\"\\mathcal{H}_{n-m}\" alt=\"\\mathcal{H}_{n-m}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bbf\/121\/0a9\/bbf1210a9115fa139e79668c5017702b.svg\"\/>.<\/p>\n<p>Finally, when using the linear probability function <img decoding=\"async\" class=\"formula inline\" source=\"F(C) = 1 - \\frac{C}{C_{max}} \" alt=\"F(C) = 1 - \\frac{C}{C_{max}} \" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/019\/cc4\/481\/019cc4481bbb2eb54c36bc0e128ccb8f.svg\"\/>, the probability takes the form<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     {\\sf P}(C_n = m) = \\genfrac\\{\\}{0pt}{}{n}{m} \\frac{C_{max}!}{(C_{max} - m)!C_{max}^n}. \\end{equation*}\" alt=\"\\begin{equation*}     {\\sf P}(C_n = m) = \\genfrac\\{\\}{0pt}{}{n}{m} \\frac{C_{max}!}{(C_{max} - m)!C_{max}^n}. \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3f3\/a2c\/972\/3f3a2c972e3e8e8cfa85c843ade22ec9.svg\" width=\"324\" height=\"49\"\/><\/p>\n<p>That is the exciting result announced above. We noticed the Stirling number in the probability value quite accidentally and still do not know whether it is possible to justify its presence by combinatorial reasoning (relying on its combinatorial definition). One another question is whether there is also a nice formula for <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf P}(C_n = m)\" alt=\"{\\sf P}(C_n = m)\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/15f\/f3b\/cda\/15ff3bcda44741083b6d610c82c34bbc.svg\"\/>in the case of Morris&#8217;s counters.<\/p>\n<p>The expected value and variance of <img decoding=\"async\" class=\"formula inline\" source=\"C_n\" alt=\"C_n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a39\/d1b\/361\/a39d1b36118e3e63e61f5e52cd3d9510.svg\"\/>(we omit the proofs since this type of counters is not the main subject of this article, but they simply follow from the properties of the Stirling numbers and the equation for <img decoding=\"async\" class=\"formula inline\" source=\"{\\sf P}\" alt=\"{\\sf P}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/40f\/c88\/ccb\/40fc88ccb792ec650a6c7e6b36cfc339.svg\"\/>):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{align*}     {\\sf E}C_n &amp;= C_{max}\\left(1 - \\left(1 - \\frac{1}{C_{max}}\\right)^n\\right) \\\\     {\\sf D}C_n &amp;= C_{max}^2\\left(\\frac{1}{C_{max}}\\left(1 - \\frac{1}{C_{max}}\\right)^n + \\left(1 - \\frac{1}{C_{max}}\\right)\\left(1 - \\frac{2}{C_{max}}\\right)^n - \\left(1 - \\frac{1}{C_{max}}\\right)^{2n}\\right). \\end{align*}\" alt=\"\\begin{align*}     {\\sf E}C_n &amp;= C_{max}\\left(1 - \\left(1 - \\frac{1}{C_{max}}\\right)^n\\right) \\\\     {\\sf D}C_n &amp;= C_{max}^2\\left(\\frac{1}{C_{max}}\\left(1 - \\frac{1}{C_{max}}\\right)^n + \\left(1 - \\frac{1}{C_{max}}\\right)\\left(1 - \\frac{2}{C_{max}}\\right)^n - \\left(1 - \\frac{1}{C_{max}}\\right)^{2n}\\right). \\end{align*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/457\/aa4\/490\/457aa44900d4b46a73d81124d5b06736.svg\" width=\"737\" height=\"114\"\/><\/p>\n<p>The number of events can be estimated using the method of moments:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     \\widehat{n}_{MM} := \\frac{\\ln \\left(1 - \\frac{C_n}{C_{max}}\\right)}{\\ln \\left(1 - \\frac{1}{C_{max}}\\right)} \\end{equation*}\" alt=\"\\begin{equation*}     \\widehat{n}_{MM} := \\frac{\\ln \\left(1 - \\frac{C_n}{C_{max}}\\right)}{\\ln \\left(1 - \\frac{1}{C_{max}}\\right)} \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cc3\/ef3\/006\/cc3ef3006210d5b4379c95b833308cc7.svg\" width=\"189\" height=\"81\"\/><\/p>\n<p>or similarly to Morris&#8217;s counters, taking into account that <img decoding=\"async\" class=\"formula inline\" source=\"1 + 1\/2 + \\dots + 1\/n \\sim \\ln n\" alt=\"1 + 1\/2 + \\dots + 1\/n \\sim \\ln n\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/048\/e63\/98f\/048e6398f92e481b6468230ae4ec7d6e.svg\"\/>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"formula\" source=\"\\begin{equation*}     \\widehat{n} := C_{max}\\ln \\left(\\frac{1}{1 - \\frac{C_n}{C_{max}}}\\right). \\end{equation*}\" alt=\"\\begin{equation*}     \\widehat{n} := C_{max}\\ln \\left(\\frac{1}{1 - \\frac{C_n}{C_{max}}}\\right). \\end{equation*}\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/281\/1a9\/239\/2811a923945ac4af73a8510c1e5ed942.svg\" width=\"221\" height=\"71\"\/><\/p>\n<h3>References<\/h3>\n<p>[1] Morris, R.Counting large numbers of events in small registers Communications of the ACM, 1978 21(10), 840-842.<\/p>\n<p>[2]\u00a0<a href=\"http:\/\/gregorygundersen.com\/blog\/2019\/11\/11\/morris-algorithm\/\">http:\/\/gregorygundersen.com\/blog\/2019\/11\/11\/morris-algorithm\/<\/a>\u00a0\u2212\u00a0an overview of Morris\u2019s counters<\/p>\n<p>[3]\u00a0<a href=\"https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5\">https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5<\/a>\u00a0\u2212 our article about EMA counters<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/559858\/\"> https:\/\/habr.com\/ru\/articles\/559858\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption>An artist&#8217;s concept showing counting of elements in a stream with limited resources<\/figcaption><\/figure>\n<p><sub>We are glad to present you an article written by Qrator Labs&#8217; engineer Dmitry Kamaldinov. If you want to be a part of our Core team, write us at\u00a0<\/sub><a href=\"mailto:hr@qrator.net\"><sub>hr@qrator.net<\/sub><\/a><sub>.<\/sub><\/p>\n<h3>1 Introduction<\/h3>\n<p>On implementing streaming algorithms, counting of events often occurs, where an event means something like a packet arrival or a connection establishment. Since the number of events is large, the available memory can become a bottleneck: an ordinary\u00a0-bit counter allows to take into account no more than\u00a0events.<br \/>One way to handle a larger range of values using the same amount of memory would be approximate counting. This article provides an overview of the well-known Morris algorithm and some generalizations of it.<\/p>\n<p>Another way to reduce the number of bits required for counting mass events is to use decay. We discuss such an approach\u00a0<a href=\"https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5\">here<\/a>\u00a0[3], and we are going to publish another blog post on this particular topic shortly.<\/p>\n<p>In the beginning of this article, we analyse one straightforward probabilistic calculation algorithm and highlight its shortcomings (Section 2). Then (Section 3), we describe the algorithm proposed by Robert Morris in 1978 and indicate its most essential properties and advantages. For most non-trivial formulas and statements, the text contains our proofs, the demanding reader can find them in the inserts. In the following three sections, we outline valuable extensions of the classic algorithm: you can learn what Morris&#8217;s counters and exponential decay have in common, how to improve the accuracy by sacrificing the maximum value, and how to handle weighted events efficiently.<\/p>\n<h3>2 Probabilistic Counting, problems of the trivial approach<\/h3>\n<p>The main idea of probabilistic counting is taking into account the next event with a certain probability. Let us first consider an example with a constant update rate:<\/p>\n<p>Where\u00a0denotes the counter value after the\u00a0th update attempt. It is easy to see that\u00a0gives the number of successes among\u00a0nn\u00a0independent Bernoulli trials. In other words,\u00a0has a\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Binomial_distribution\">binomial distribution<\/a>:  <\/p>\n<p>The problem we are to solve is to estimate the actual number of events given only the random value obtained accordingly to the described update rule. It is natural to use as an estimator, since we know on average.<\/p>\n<p>The described approach has several drawbacks: first, it allows to count up to times larger numbers comparing to the ordinary incremental counter, which is still noticeably less than, as we will see below, we get with Morris&#8217;s counters. Second, the <em>relative error<\/em> is high for small . For example, if it is up to 100%. The coefficient of variation can estimate the relative error:<\/p>\n<h3>3\u00a0Morris&#8217;s counters<\/h3>\n<p>The Morris&#8217;s counter updates with a probability dependent on the current value: the first update occurs with a probability of 1, the next with 1\/2, then 1\/4, etc.:<\/p>\n<p>How to estimate the number of events by the current counter value? Updating the counter from to occurs on average after iterations, hence a reasonable estimator would be<\/p>\n<p>The most important properties of this estimator are the following:<\/p>\n<ul>\n<li>\n<p>it is\u00a0<strong>unbiased<\/strong>, that is, the estimate after\u00a0nn\u00a0updates is on average\u00a0,<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>At the very beginning, the counter value is 0: then , so . Finally, .<\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>and its\u00a0<strong>relative error<\/strong>\u00a0is\u00a0<strong>independent<\/strong>\u00a0of\u00a0.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>First, let&#8217;s calculate the variance of . According to the well-known formula, . We already know . It remains to get . Notice, that<\/p>\n<p>So, we are only to find .<\/p>\n<p>Finally,<\/p>\n<p>And also, we can bound tail probabilities by\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Chebyshev's_inequality\">Chebyshev&#8217;s inequality<\/a>:<\/p>\n<p>Thus, the relative error of the number of events estimate\u00a0<strong>is bounded, approximately constant and almost independent of<\/strong>\u00a0.  <\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>If we are restricted with\u00a0bits of memory, the event number range we can cover is\u00a0. In other words,\u00a0bits are sufficient to account up to\u00a0events!<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><figcaption>Figure 1: relative error of the event number estimate while using binomial counters and Morris&#8217;s counters (the plot was obtained by sample_size=100 times simulating the evolution process of the counter and mapping each point n onto the average relative error of estimate at this point)<\/figcaption><\/figure>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>4\u00a0Weighted updates<\/h3>\n<p>Morris&#8217;s counters can be used to count weighted events: for example, to sum up the sizes of incoming packets. The simplest way \u2014 updating the counter &lt;event weight> times \u2014 leads the update complexity to be linear in weight.<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Note that at each moment, we know how the number of update failures before the subsequent success is distributed; this is the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Geometric_distribution\">geometric distribution<\/a>:  <\/p>\n<p>Given this fact, we can speed up the algorithm by immediately skipping all consecutive unsuccessful update attempts:<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>5\u00a0Decay<\/h3>\n<p>There is often a need to take into account the most recent events with a higher priority. For instance, when looking for intensive keys in a stream, we might be more interested in detecting keys intense right now, rather than ones having a high average intensity due to the events in the past. In the <a href=\"https:\/\/qratorlabs.medium.com\/rate-detector-21d12567d0b5\">article<\/a> [3], we discussed the decay counters explicitly designed for this purpose. Another prerequisite for some form of decay is that despite the super-economical use of memory, even Morris&#8217;s counters once may overflow. Fortunately, it turns out Morris&#8217;s counters can also decay, moreover, it is very similar to EMA but much more efficient.<\/p>\n<p>The basic idea is quite simple: we periodically subtract from the counter. Recall the counter value of estimates the number of events as . <\/p>\n<p>Accordingly, after subtracting the estimate becomes . <\/p>\n<p>In other words, subtracting decreases the estimate by a factor of ! We can achieve exactly times decrease by updating the counter with probability immediately after subtracting :<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>6\u00a0Mantissa and exponent<\/h3>\n<p>Consider the following generalization of Morris&#8217;s counters: the probability of an update is halved not on every successful update, but on every\u00a0updates. One possible implementation of this approach would be splitting the counter bits into two parts:\u00a0<em>exponential<\/em>, responsible for the probability, and\u00a0<em>significand<\/em>, counting the number of successful updates within the current probability (the notions are taken from\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Floating-point_arithmetic\">floating point arithmetic<\/a>).<\/p>\n<figure class=\"\"><figcaption>Figure 2: splitting counter bits into E=3 exponential and  M=5 significand<\/figcaption><\/figure>\n<p>Let us introduce the notation for the exponential and significand parts:<\/p>\n<p>Now the update rule and the estimator of the number of events are written as follows:<\/p>\n<p>For example, on figure 2 counter value is\u00a0, the mantissa and exponent are equal to\u00a0and\u00a0correspondingly. So the event number estimate is\u00a0.  <\/p>\n<p>With such a partition, the bits of the exponent are responsible for the covered range of the number of events, and the bits of the mantissa determine the accuracy of the estimate (these claims are described in detail below). The best partition of the counter bits depends on the application; we suggest giving as many bits to the mantissa as possible, but with enough exponential bits to cover the target value range.<\/p>\n<p>The described variant of Morris&#8217;s counters has the following properties:<\/p>\n<ul>\n<li>\n<p>the covered range of values is<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>The covered range is easy to get by substituting the maximum possible values  and into the estimator :<\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>a new estimator\u00a0is also unbiased:\u00a0;<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>Proof<\/summary>\n<div class=\"spoiler__content\">\n<p>Similarly to the proof of the statement for the ordinary Morris&#8217;s counters, we calculate the conditional expectation . And we will do it separately for (the probability of the update changes on success) and .<\/p>\n<p>In both cases  <\/p>\n<\/div>\n<\/details>\n<ul>\n<li>\n<p>the coefficient of variation (and the relative error) gets even smaller! We do not know its exact value, but there is a bound:<\/p>\n<\/li>\n<\/ul>\n<p>\u00a0(recall the\u00a0analogous value\u00a0for classic Morris&#8217;s counter is\u00a0)  <\/p>\n<figure class=\"full-width\"><figcaption>Figure 3<\/figcaption><\/figure>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>Weighted update and decay algorithms will also change slightly.<\/p>\n<p>Similarly to the approach described in\u00a0Section 4, to perform a weighted update efficiently, we are to figure out how many update attempts are needed to change the probability. Formerly, without the mantissa, the probability changes on each successful update, but now it changes after every\u00a0updates. So we need the distribution of the number of Bernoulli trials (the required number of events), among which there are precisely\u00a0successful ones. This distribution is called\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Negative_binomial_distribution\">negative binomial<\/a>.<\/p>\n<p><strong><sub>Remark.<\/sub><\/strong><sub>\u00a0<\/sub><em><sub>Actually, there are several definitions of the negative binomial distribution. We will use the one that claims it as a distribution of the number of failures with a given number of successes. Then, to get the number of events, we need to add the number of successes to the random value:<\/sub><\/em>  <\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>In order to avoid generating the negative binomial distribution, one can simplify the algorithm by just using the mean of this distribution:  <\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>To perform the decay, we subtract\u00a0from the\u00a0<strong>exponential part<\/strong>\u00a0(in other words,\u00a0from the counter value). Then  <\/p>\n<p>Note that for 0&#8243; alt=&#187;{\\bf M}>0&#8243; src=&#187;https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c52\/67b\/c08\/c5267bc086a7aab144a9df69971e6a59.svg&#187;\/>(namely, this case is studied in this section) . Therefore, the estimate can be corrected by updating by :<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>7 Conclusion<\/h3>\n<p>Morris&#8217;s counters are awesome! Use them!<\/p>\n<p>The last section does not contain practical results, but &#8230;<\/p>\n<h3>8\u00a0Generalized Probabilistic Counters<\/h3>\n<p>In general, the rule for updating the counter is determined by the probability function\u00a0:<\/p>\n<p>In the case of Morris&#8217;s counters . When studying probabilistic counters, we tried to take a linear probability function: ( \u2014 some constant, the upper bound of the counter). <\/p>\n<p>This had led to some interesting results, which are summarized in this section.<\/p>\n<p>Before presenting these results, let us recall two mathematical entities. A\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Complete_homogeneous_symmetric_polynomial\"><em>complete symmetric homogeneous polynomial<\/em><\/a>\u00a0of degree\u00a0in\u00a0variables\u00a0is the sum of all possible monomials of degree\u00a0in these variables with coefficients\u00a0:<\/p>\n<\/p>\n<p>Another construction that we need is the\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Stirling_numbers_of_the_second_kind\"><em>Stirling number of the second kind<\/em><\/a>. One of the ways to define this number is combinatorial: the Stirling number of the second kind\u00a0is the number of partitions of the\u00a0nn-element set into\u00a0mm\u00a0non-empty subsets. Stirling numbers are associated with complete symmetric homogeneous polynomials:  <\/p>\n<p>With the help of , we can write in general form the probability of counter<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-389488","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/389488","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=389488"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/389488\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=389488"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=389488"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=389488"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}