Sliding window for positive sums in Python
Grow the right end until the sum reaches s, then shrink the left end; runs in linear time because values are positive.
- File
sliding_window_positive_sum.py, 78 lines- Level
- I0 not checked yet
- Toolchain
- Python 3.14, pytest 9
- Test framework
- pytest
- Run the tests
python3 -m pytest
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/sliding-window-positive-sum/. The authors are listed in prov/7FDZS3.jsonl.
from typing import Optional, Union
Number = Union[int, float]
def min_window_length(a: list[Number], s: Number) -> int:
"""Return the length of the shortest subarray of a with sum >= s, or 0 if there is none.
Precondition: every item of a is >= 0 and s > 0 (raises ValueError otherwise).
Cost: O(n) time, O(1) extra space.
"""
if s <= 0:
raise ValueError("the threshold s must be positive")
if any(x < 0 for x in a):
raise ValueError("the items must not be negative")
left = 0 # @line L1
total: Number = 0
best: Optional[int] = None
for right in range(len(a)): # @line L2
total += a[right] # @line L3
while total >= s: # @line L4
length = right - left + 1 # @line L5
if best is None or length < best:
best = length
total -= a[left] # @line L6
left += 1
if best is None: # @line L7
return 0
return best # @line L8
def min_window(a: list[Number], s: Number) -> Optional[tuple[int, int]]:
"""Return (l, r), 0-based and inclusive, of a shortest subarray with sum >= s.
Among the shortest windows, the one with the earliest end is returned.
Returns None if there is no such window. Same precondition and cost as
min_window_length.
"""
if s <= 0:
raise ValueError("the threshold s must be positive")
if any(x < 0 for x in a):
raise ValueError("the items must not be negative")
left = 0
total: Number = 0
found: Optional[tuple[int, int]] = None
for right in range(len(a)):
total += a[right]
while total >= s:
if found is None or right - left < found[1] - found[0]:
found = (left, right)
total -= a[left]
left += 1
return found
def count_windows_below(a: list[Number], s: Number) -> int:
"""Return the number of non-empty subarrays of a with sum < s.
Precondition: every item of a is >= 0 and s > 0.
Cost: O(n) time, O(1) extra space.
"""
if s <= 0:
raise ValueError("the threshold s must be positive")
if any(x < 0 for x in a):
raise ValueError("the items must not be negative")
left = 0
total: Number = 0
count = 0
for right in range(len(a)):
total += a[right]
while total >= s:
total -= a[left]
left += 1
count += right - left + 1
return count
test_sliding_window_positive_sum.py 60 lines
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/sliding-window-positive-sum/. The authors are listed in prov/7FDZS3.jsonl.
import random
import pytest
from sliding_window_positive_sum import count_windows_below, min_window, min_window_length
def brute(a: list[int], s: int) -> int:
best = 0
for i in range(len(a)):
for j in range(i, len(a)):
if sum(a[i : j + 1]) >= s and (best == 0 or j - i + 1 < best):
best = j - i + 1
return best
def test_example() -> None:
assert min_window_length([2, 3, 1, 2, 4, 3], 7) == 2
assert min_window([2, 3, 1, 2, 4, 3], 7) == (4, 5)
def test_edges() -> None:
assert min_window_length([], 5) == 0
assert min_window([], 5) is None
assert min_window_length([1, 1, 1], 4) == 0
assert min_window_length([1, 1, 1], 3) == 3
assert min_window_length([7], 7) == 1
assert min_window_length([1, 4, 4], 4) == 1
assert min_window([1, 4, 4], 4) == (1, 1)
assert min_window_length([0, 0, 5, 0], 5) == 1
assert min_window_length([0.5, 0.25, 0.25], 1.0) == 3
assert count_windows_below([10, 5, 2, 6], 100) == 10
with pytest.raises(ValueError):
min_window_length([1, 2], 0)
with pytest.raises(ValueError):
min_window_length([5, -10, 4, 4], 8)
def test_against_brute_force() -> None:
rng = random.Random(20261105)
for _ in range(2000):
n = rng.randrange(0, 20)
a = [rng.randrange(0, 10) for _ in range(n)]
s = rng.randrange(1, 40)
expected = brute(a, s)
assert min_window_length(a, s) == expected
w = min_window(a, s)
if expected == 0:
assert w is None
else:
assert w is not None
assert w[1] - w[0] + 1 == expected
assert sum(a[w[0] : w[1] + 1]) >= s
below = sum(
1 for i in range(n) for j in range(i, n) if sum(a[i : j + 1]) < s
)
assert count_windows_below(a, s) == below
What the next level needs
- the tests did not run in this build