{"id":26361460,"url":"https://github.com/pgdr/slidemin","last_synced_at":"2026-01-02T20:59:12.016Z","repository":{"id":281171839,"uuid":"944378637","full_name":"pgdr/slidemin","owner":"pgdr","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-07T10:54:45.000Z","size":2,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-07T11:34:10.650Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TeX","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pgdr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-03-07T08:45:08.000Z","updated_at":"2025-03-07T10:54:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"32bd10c0-dbae-4518-8387-3cc5951fd317","html_url":"https://github.com/pgdr/slidemin","commit_stats":null,"previous_names":["pgdr/slidemin"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pgdr%2Fslidemin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pgdr%2Fslidemin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pgdr%2Fslidemin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pgdr%2Fslidemin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pgdr","download_url":"https://codeload.github.com/pgdr/slidemin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243909201,"owners_count":20367533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-16T17:37:48.713Z","updated_at":"2026-01-02T20:59:11.987Z","avatar_url":"https://github.com/pgdr.png","language":"TeX","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sliding window for minimum\n\nComputing the minimum values of a sliding window is not as easy as\ncomputing, e.g., the sum. When computing the sum, we simply need a\nsingle accumulated variable\n`acc = acc - data[win] + data[win + k]`.\n\nHowever, when we want to compute the minimum value, we need to know\nwhich next minimum value to keep when a small value disappears out of\nthe window. Recomputing this value every iteration is more expensive\nthan necessary; It would take $\\Omega(n \\cdot k)$, where $n$ is the\nnumber of elements and $k$ the size of the window. There is, however, a\nway of computing all min-values in $O(n)$ *amortized* time (left as an\nexercise to the student).\n\nThe trick is to use a *double-ended queue*. In this queue, we keep a\n(necessarily increasing) list of potential minimum values. To understand\nthe algorithm, consider the following list:\n`data = [10, 5, 3, 5, 7, 4, 2]`, with $k = 3$.\n\nLet `Q = []` be an empty double-ended queue [^1] , and let us\nstart reading values from `data`. First we read 10, which we\nwill put into the queue. Second, we read 5. Notice that 10 is now not\nrelevant anymore, since 10 will never be the minimum value. We will pop\n*from the right* until the queue is empty, or until the right-most value\nis higher than what we are currently looking at. The queue, after having\nread $10, 5$ is `Q = [5]`.\n\nWe still haven't read $k=3$ values yet, so we read another one, and see\n3. Again, we have that $5$ is not relevant, so we remove it from the\nqueue. After having read $10, 5, 3$, we have `Q = [3]`.\n\nSince we have seen $k=3$ values, we can output $3$. The window is now at\n$$[\\underbracket[0.1pt][0.1pt]{ 10, 5, 3}, 5, 7, 4, 2].$$ At this point,\nwe are ready to move the window one step to the right, i.e., we want to\ndiscard 10 and introduce 5. Since the left-most value in `data`\nis 10, and 10 is not in the queue, we do not pop anything from the\nqueue. However, the value we include in the window might at some point\nin the future become the minimum value (after 3 has been discarded some\ntime in the future), so we include 5 in the queue.\n\nAt $[10, \\underbracket[0.1pt][0.1pt]{ 5, 3, 5 }, 7, 4, 2]$ with\n`Q = [3, 5]`, we again output the minimum value; it is the\nfirst value in the queue. Moving the window one step to the right means\nwe discard a value 5. However, the leftmost value in the queue is 3,\nwhich means that the value we discard is irrelevant. On the right hand\nside, we include 7. This might in the future become the minimum value,\nso we need to include it in the queue.\n\nThe state is now $[10, 5, \\underbracket[0.1pt][0.1pt]{ 3, 5, 7}, 4, 2]$\nwith `Q = [3, 5, 7]`, and we again output a value: the leftmost\nvalue in the queue, 3. Thus far, our output has been\n`output = [3, 3, 3]`.\n\nFinally, something interesting happens, we are ready to discard the\nvalue 3. Moving the window one step to the right means we discard a\nvalue that is the leftmost value in the queue, which means we need to\npop it off. The queue is then `Q = [5, 7]`, but we also need to\ninclude the new value, which happens to be 4.\n\nWhen the queue is `Q = [5, 7]`, and the new value is 4, we can\nnotice that neither 5 nor 7 will ever be the minimum value, because (a)\n4 is smaller than them both and (b) 4 will outlive them. The procedure\nis to look at the rightmost value, and since $7 \u003e 4$, we pop off 7, and\nrepeat. The rightmost value is $5 \u003e 4$ and we will pop off 5, and we are\nleft with an empty queue, and therefore the state is:\n\n$$[10, 5 ,3, \\underbracket[0.1pt][0.1pt]{ 5, 7, 4 },  2],$$ with\n`Q = [4]` and we output the leftmost value in the queue: 4.\n\nThe final state is\n$[10, 5 ,3 ,5, \\underbracket[0.1pt][0.1pt]{ 7, 4 , 2 }]$, with\n`Q = [2]` at which point we output 2. The total output is\n`output = [3, 3, 3, 4, 2]`.\n\n# Code\n\n``` python\ndef slidemin(data, k):\n    \"\"\"Linear time algorithm for computing sliding\n       window minimum values.\"\"\"\n    minq = deque()              # from collections\n    for i in range(len(data)):\n        if minq and minq[0] == i - k:\n            minq.popleft()\n        while minq and data[minq[-1]] \u003e= data[i]:\n            minq.pop()\n        minq.append(i)\n        if i \u003e= k - 1:\n            yield data[minq[0]]\n```\n\n[^1]: A double-ended queue is one we can read and write, push and pop,\n    from both left and right side in $O(1)$ time.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpgdr%2Fslidemin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpgdr%2Fslidemin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpgdr%2Fslidemin/lists"}