Python 终止重叠指数的迭代解法

Python 终止重叠指数的迭代解法,python,list,loops,recursion,iteration,Python,List,Loops,Recursion,Iteration,我有一个列表,其中包含表示数字范围的元组。我的目标是返回该集合的所有可能子集(请参见下面的注释;真正寻找最长的子集),这些子集在每个元组中仅重叠第二个值,或者根本不重叠。我一直使用的函数是这个问题的递归解决方案 def get_all_end_overlapping_indices(lst, i, out): all_possibilities = [] def _get_all_end_overlapping_indices_helper(list_in, i, out):

我有一个列表,其中包含表示数字范围的元组。我的目标是返回该集合的所有可能子集(请参见下面的注释;真正寻找最长的子集),这些子集在每个元组中仅重叠第二个值,或者根本不重叠。我一直使用的函数是这个问题的递归解决方案

def get_all_end_overlapping_indices(lst, i, out):
    all_possibilities = []

    def _get_all_end_overlapping_indices_helper(list_in, i, out):
        r = -1
        if i == len(list_in):
            if out:
                if len(all_possibilities) == 0:
                    all_possibilities.append(out)
                else:                       
                    all_possibilities.append(out)

            return 

        n = i + 1

        while n < len(list_in) and r > list_in[n][0]:
            n += 1
        _get_all_end_overlapping_indices_helper(list_in, n, out)
        r = list_in[i][1]

        n = i + 1
        while n < len(list_in) and r > list_in[n][0]:
            n += 1
        _get_all_end_overlapping_indices_helper(list_in, n, out + [list_in[i]])

    _get_all_end_overlapping_indices_helper.count = 0
    lst.sort()
    _get_all_end_overlapping_indices_helper(list_in = lst, i = 0, out = [])
    
    return all_possibilities
由于我最终将处理更大的元组集合(而且运行速度非常慢),我想实现一个迭代解决方案;不幸的是,我被难倒了。此代码段最初来自:。虽然它能工作,但我发现破解它的工作原理很难。有人能提供一些关于如何构造这个问题的迭代解决方案的提示吗

注意:我实际上只希望获得最长的输出(见下文)。我总是可以在以后过滤掉那些较短的(也就是说,那些位于最长的内部的),但是如果这样做更容易的话,我可以很乐意地去掉它们

[(0.0, 2.0), (4.0, 5.5), (6.0, 7.25)]
[(0.0, 2.0), (2.5, 4.5), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 5.75), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 4.0), (4.0, 5.5), (6.0, 7.25)]
[(0.0, 4.0), (4.0, 5.5), (6.0, 7.25)]

编辑我以前的回答并没有真正回答这个问题,因此这里有一段代码可以真正回答这个问题

我们需要找到的是非重叠元组序列的帕累托最优前沿(包含意义上的帕累托最优)

  • 首先,我们需要找到“源”元组,也就是可以位于元组序列开头的元组
  • 然后,我们可以计算每个元组的“后继元组”列表
输入:
[(0.0,2.0)、(0.0,4.0)、(2.5,4.5)、(2.0,5.75)、(2.0,4.0)、(6.0,7.25)、(4.0,5.5)]

输出:

我认为这个问题在多项式时间内是不可解的,因为输出的大小可能是输入大小的指数。然而,它将运行得相当快,因为构建这4个字典需要
O(n²)
,然后每个路径都以线性时间w.r.t其长度输出

编辑结束,下面是旧解决方案

我们可以在多项式时间内解决这个问题,把它归结为DAG(有向无环图)中的最长路径问题


首先,我们需要将问题建模为DAG。每个元组代表一个顶点,我们从
(a,b)
(c,d)
当且仅当
b非常感谢,@m.raynal!这对我很有帮助。使用此框架,是否可以检索DAG中不重叠的其他路径?在我的帖子中,您可能会看到(在底部),我希望检索所有(最长的)非重叠路径,如[(0.0,2.0),(2.5,4.5),(6.0,7.25)]和[(0.0,2.0),(4.0,5.5),(6.0,7.25)]。您好,使用这个框架,可以找到最长路径的帕累托最优边界。但是今天早上我一直在思考你的问题,我在这个框架之外想出了一个更简单、更好的解决方案,今晚我会发布。@Lukeppeppel你可以看看新的解决方案,我想这是你这次的问题的答案:-)非常感谢你编辑的答案。这正是我想要的!:-)
[(0.0, 2.0), (4.0, 5.5), (6.0, 7.25)]
[(0.0, 2.0), (2.5, 4.5), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 5.75), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 4.0), (4.0, 5.5), (6.0, 7.25)]
[(0.0, 4.0), (4.0, 5.5), (6.0, 7.25)]
sources = {
    (a, b)
    for (a, b) in tup_lst
    if not any(d <= a for (c, d) in tup_lst)
}    
sinks = {
    (a, b)
    for (a, b) in tup_lst
    if not any(b <= c for (c, d) in tup_lst)
}
min_successor = {
    (a, b): min(d for c, d in tup_lst if c >= b)
    for (a, b) in set(tup_lst) - sinks
}
successors = {
    (a, b): [
        (c, d)
        for (c, d) in tup_lst
        if b <= c <= d and c < min_successor[(a, b)]
    ] for (a, b) in tup_lst
}
def print_path_rec(node, path):
    if node in sinks:
        print(path + [node])
    else:
        for successor in successors[node]:
            print_path_rec(successor, path + [node])
for source in sources:
    print_path_rec(source, [])
[(0.0, 2.0), (2.5, 4.5), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 5.75), (6.0, 7.25)]
[(0.0, 2.0), (2.0, 4.0), (4.0, 5.5), (6.0, 7.25)]
[(0.0, 4.0), (4.0, 5.5), (6.0, 7.25)]
def overlap_condition(tup1, tup2):
    if tup1 == tup2:
        return False
    a, b = tup1
    c, d = tup2
    return b <= c


def adj_mat_from_tup_list(tup_lst):
    return [
        [
            1 if overlap_condition(tup_lst[i], tup_lst[j]) else 0
            for j in range(len(tup_lst))
        ] for i in range(len(tup_lst))

    ]


def topological_sort(adj_mat):
    sorted_v = []
    sinks = {
        i for i in range(len(adj_mat))
        if not any(adj_mat[j][i] == 1 for j in range(len(adj_mat)))
    }

    while sinks:
        v = sinks.pop()
        sorted_v += [v]
        for j in range(len(adj_mat)):
            if adj_mat[v][j] == 1:
                adj_mat[v][j] = 0
                if not any(adj_mat[w][j] for w in range(len(adj_mat))):
                    sinks.add(j)
    return sorted_v


def get_longest_path(adj_mat, sorted_v):
    dists = {v: 0 for v in range(len(adj_mat))}
    preds = {v: None for v in range(len(adj_mat))}
    for v in sorted_v:
        for u in range(len(adj_mat)):
            if adj_mat[u][v]:
                dists[v] = max(dists[v], dists[u] + 1)
                preds[v] = u

    current_v = {
        v for v in range(len(adj_mat))
        if dists[v] == max(dists.values())
    }.pop()
    result = [current_v]
    while preds[current_v] is not None:
        current_v = preds[current_v]
        result += [current_v]
    return result[::-1]


def get_all_end_overlap_tups(tup_lst):
    sorted_v = topological_sort(adj_mat_from_tup_list(tup_lst))
    adj_mat = adj_mat_from_tup_list(tup_lst)
    return [tup_lst[i] for i in get_longest_path(adj_mat, sorted_v)]


lst = [
    (0.0, 2.0), (0.0, 4.0), (2.5, 4.5), (2.0, 5.75),
    (2.0, 4.0), (6.0, 7.25), (4.0, 5.5)
]

print(get_all_end_overlap_tups(lst))