[英]How to get all maximal non-overlapping sets of spans from a list of spans
我似乎找不到一種無需編寫某種結果就能在標題中編寫算法的方法。
為了說明我想要的:
all_spans = [(0, 5), (2, 7), (5, 8), (6, 10), (9, 10), (11, 15)]
possible_sets = [
{(0, 5), (5, 8), (9, 10), (11, 15)},
{(2, 7), (9, 10), (11, 15)},
{(0, 5), (6, 10), (11, 15)}
]
not_possible = [
{(0, 5), (5, 8), (6, 10), (11, 15)}, # has overlaps
{(5, 8), (9, 10), (11, 15)} # not maximal w.r.t possible_sets[0]
]
我當前的實現或多或少是這樣的:
def has_overlap(a, b):
return a[1] > b[0] and b[1] > a[0]
def combine(spans, current, idx=0):
for i in range(idx, len(spans)):
overlaps = {e for e in current if has_overlap(e, spans[i])}
if overlaps:
yield from combine(spans, current-overlaps, i)
else:
current.add(spans[i])
yield current
但是它會產生非最大跨度,而我首先不想創建。
>>> for s in combine(all_spans, set()):
... print(sorted(s))
[(9, 10), (11, 15)]
[(6, 10), (11, 15)]
[(5, 8), (9, 10), (11, 15)]
[(9, 10), (11, 15)]
[(6, 10), (11, 15)]
[(2, 7), (9, 10), (11, 15)]
[(0, 5), (9, 10), (11, 15)]
[(0, 5), (6, 10), (11, 15)]
[(0, 5), (5, 8), (9, 10), (11, 15)]
是否有其他方法可以避免這種行為? 我在關鍵字“時間間隔重疊”和“活動計划”下發現了類似的問題,但是似乎沒有一個問題涉及到此特定問題。
這取決於您不想表達結果的意思。
將生成器與以下各項配合使用后,您可以濾除非最大結果:
all_results = [s for s in combine(all_spans, set())]
for first_result in list(all_results):
for second_result in list(all_results):
if first_result.issubset(second_result) and first_result != second_result:
all_results.remove(first_result)
break
如果不首先生成它們,可以在屈服之前檢查一下答案是否最大。 就像是:
def combine(spans, current, idx=0):
for i in range(idx, len(spans)):
overlaps = {e for e in current if has_overlap(e, spans[i])}
if overlaps:
yield from combine(spans, current-overlaps, i)
else:
current.add(spans[i])
# Check whether the current set is maximal.
possible_additions = set(spans)
for item_to_consider in set(possible_additions):
if any([has_overlap(item_in_current, item_to_consider) for item_in_current in current]):
possible_additions.remove(item_to_consider)
if len(possible_additions) == 0:
yield current
這是一個簡單的(?)圖問題。 制作一個有向圖,其中每個跨度都是一個節點。 假說,如果跨度B在跨度A完成之前沒有開始,那么存在A邊(從節點A到節點B)iff A [1] <= B [0]。 您的圖看起來像
Node => Successors
(0, 5) => (5, 8), (6, 10), (9, 10), (11, 15)
(2, 7) => (9, 10), (11, 15)
(5, 8) => (9, 10), (11, 15)
(6, 10) => (11, 15)
(9, 10) => (11, 15)
現在,問題簡化為簡單地找到通過圖形的最長路徑,包括聯系。
給定問題的線性度,找到一個最大的解比較容易:在每個步驟中,選擇終止時間最快的后繼節點。 步驟:
注意,這不需要圖。 只是您願意通過第一個或第二個子元素引用的結構。
如您所知,解決方案長度為4。
你能在這里把它做成表格嗎?
假設范圍是按下限排序的,我們想將當前范圍附加到可以附加的最長路徑上,或創建一個新路徑(附加到空路徑上)。 如果需要,我們可以考慮使搜索最長前綴的效率更高。 (下面的代碼僅以稍微優化的線性方法更新搜索。)
(我不確定如何使用yield功能,也許您可以使此代碼更加優雅。)
# Assumes spans are sorted by lower bound
# and each tuple is a valid range
def f(spans):
# Append the current span to the longest
# paths it can be appended to.
paths = [[spans.pop(0)]]
for l,r in spans:
to_extend = []
longest = 0
print "\nCandidate: %s" % ((l,r),)
for path in paths:
lp, rp = path[-1]
print "Testing on %s" % ((lp,rp),)
if lp <= l < rp:
prefix = path[:-1]
if len(prefix) >= longest:
to_extend.append(prefix + [(l,r)])
longest = len(prefix)
# Otherwise, it's after so append it.
else:
print "Appending to path: %s" % path
path.append((l, r))
longest = len(path)
for path in to_extend:
print "Candidate extensions: %s" % to_extend
if len(path) == longest + 1:
print "Adding to total paths: %s" % path
paths.append(path)
print "\nResult: %s" % paths
return paths
all_spans = [(0, 5), (2, 7), (5, 8), (6, 10), (9, 10), (11, 15)]
f(all_spans)
輸出:
"""
Candidate: (2, 7)
Testing on (0, 5)
Candidate extensions: [[(2, 7)]]
Adding to total paths: [(2, 7)]
Candidate: (5, 8)
Testing on (0, 5)
Appending to path: [(0, 5)]
Testing on (2, 7)
Candidate: (6, 10)
Testing on (5, 8)
Testing on (2, 7)
Candidate extensions: [[(0, 5), (6, 10)]]
Adding to total paths: [(0, 5), (6, 10)]
Candidate: (9, 10)
Testing on (5, 8)
Appending to path: [(0, 5), (5, 8)]
Testing on (2, 7)
Appending to path: [(2, 7)]
Testing on (6, 10)
Candidate: (11, 15)
Testing on (9, 10)
Appending to path: [(0, 5), (5, 8), (9, 10)]
Testing on (9, 10)
Appending to path: [(2, 7), (9, 10)]
Testing on (6, 10)
Appending to path: [(0, 5), (6, 10)]
Result: [[(0, 5), (5, 8), (9, 10), (11, 15)],
[(2, 7), (9, 10), (11, 15)],
[(0, 5), (6, 10), (11, 15)]]
"""
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.