beam: add min_interval_cover greedy with brute-force-verified tests
This commit is contained in:
1 parent
b79f2a6a60
commit
d8d8e3292d
2 files changed
+86
No files matched your search
@@ -0,0 +1,34 @@
|
||||
"""Minimum number of intervals to cover [0, target).
|
||||
|
||||
Greedy: at each step, among intervals with start <= current coverage end,
|
||||
pick the one extending furthest right. Returns list of chosen intervals
|
||||
or None if full coverage impossible.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def min_interval_cover(intervals, target):
|
||||
if target <= 0:
|
||||
return []
|
||||
ivs = sorted(intervals, key=lambda x: x[0])
|
||||
chosen = []
|
||||
cur_end = 0
|
||||
i = 0
|
||||
n = len(ivs)
|
||||
while cur_end < target:
|
||||
best_end = cur_end
|
||||
best = None
|
||||
# consider all intervals starting <= cur_end
|
||||
j = 0
|
||||
# find first not considered; use scanning from 0 each iteration is fine for correctness
|
||||
for iv in ivs:
|
||||
if iv[0] <= cur_end and iv[1] > best_end:
|
||||
best_end = iv[1]
|
||||
best = iv
|
||||
if best is None or best_end == cur_end:
|
||||
return None
|
||||
chosen.append(best)
|
||||
cur_end = best_end
|
||||
# remove used interval to avoid infinite loop? If best still best, next iteration best_end would equal cur_end -> None. Remove it.
|
||||
ivs = [x for x in ivs if x is not best]
|
||||
return chosen
|
||||
@@ -0,0 +1,52 @@
|
||||
import sys; sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent))
|
||||
from interval_cover import min_interval_cover
|
||||
|
||||
# basic
|
||||
assert min_interval_cover([(0,5),(5,10)], 10) is not None
|
||||
r = min_interval_cover([(0,5),(5,10)], 10)
|
||||
assert len(r)==2
|
||||
# impossible
|
||||
assert min_interval_cover([(0,4),(5,10)], 10) is None
|
||||
# single
|
||||
assert min_interval_cover([(0,10)], 10) == [(0,10)]
|
||||
# empty target
|
||||
assert min_interval_cover([(0,5)], 0) == []
|
||||
# greedy optimal check vs brute for small
|
||||
import itertools
|
||||
def brute(intervals,target):
|
||||
# min subset covering [0,target)
|
||||
best=None
|
||||
for r in range(1,len(intervals)+1):
|
||||
for combo in itertools.combinations(intervals,r):
|
||||
# check coverage
|
||||
cur=0
|
||||
# sort
|
||||
s=sorted(combo)
|
||||
ok=True
|
||||
for a,b in s:
|
||||
if a>cur: ok=False;break
|
||||
cur=max(cur,b)
|
||||
if ok and cur>=target:
|
||||
best=r; break
|
||||
if best: break
|
||||
return best
|
||||
import random
|
||||
random.seed(1)
|
||||
for _ in range(2000):
|
||||
n=random.randint(1,6)
|
||||
ivs=[]
|
||||
for _ in range(n):
|
||||
a=random.randint(0,6); b=a+random.randint(1,4)
|
||||
ivs.append((a,b))
|
||||
t=random.randint(1,8)
|
||||
res=min_interval_cover(ivs,t)
|
||||
got = len(res) if res is not None else None
|
||||
# feasibility
|
||||
feas = any(True for _ in [0])
|
||||
# compute brute min if feasible
|
||||
b=brute(ivs,t)
|
||||
if b is None:
|
||||
assert res is None, (ivs,t,res)
|
||||
else:
|
||||
assert res is not None and len(res)==b, (ivs,t,res,b)
|
||||
print("all ok")
|
||||
Reference in new issue
Block a user