簡體   English   中英

如何從范圍列表中獲取所有最大范圍的最大不重疊范圍

[英]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)

現在,問題簡化為簡單地找到通過圖形的最長路徑,包括聯系。

給定問題的線性度,找到一個最大的解比較容易:在每個步驟中,選擇終止時間最快的后繼節點。 步驟:

  1. 首先,所有節點都可用。 結束時間最快的那個是(0,5)。
  2. 最早的(0,5)的后繼者是(5,8)。
  3. (5,8)...的后繼是(9,10)
  4. ...最后加(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM