找到构成整体的所有可能重叠的片段?

时间:2014-11-28 05:08:57

标签: algorithm graph combinations combinatorics

让我们说space = [0, 100],并给出一些间隔。

这些间隔是空间的片段,可能会重叠。

[0, 30], [0, 20], [10, 40], [30, 50], [50, 90], [70, 100]

是一组间隔。

跨越从上述集合中选择的整个空间的一组间隔的示例是:

[0, 30], [10, 40], [30, 50], [50, 90], [70, 100]

另一个例子是

[0, 30], [30, 50], [50, 90], [70, 100]

这是上一个示例中没有[10, 40]的设置。

我想找到这些间隔集的所有组合来计算每个间隔的成本,并找到成本最低的那个。

from operator import itemgetter
import collections

tmp = [(0, 30), (0, 20), (10, 40), (30, 50), (50, 90), (70, 100), ]
aa = sorted(tmp, key=itemgetter(1)) # sort with respect to 1st elem
a = set(aa)
space = 100
d_conn = 15
RTT = d_conn*2
bandwidth = 10

def get_marginal_cost(fragment):
    return RTT + (fragment[1] - fragment[0])/bandwidth

def dfs(a, start, path=None):
    if path is None:
        path = [start, ]
    if start[1] == space:
        yield path
    for frgmt in a - set(path):
        l = frgmt[0]
        r = frgmt[1]
        if start[0] < l <= start[1] <= r:
#        if l <= start[1] <= r:
            yield dfs(a, frgmt, path + [frgmt, ])

for z in a:
    if z[0] == 0:
        for output in list(dfs(a, z)):
            for outpu in list(output):
                for outp in list(outpu):
                    for out in list(outp):
                        for ou in list(out):
                            print list(ou)

这是我迄今为止的尝试,但我无法完成。

特别是,我希望在Python中不使用yield功能来完成此操作,因为我不熟悉它,我可能想在C ++中实现它。

有人可以帮我写一个解决这个问题的工作程序吗?

谢谢。

3 个答案:

答案 0 :(得分:2)

  

为了找到最低成本,是否真的有必要建一棵树?

可能不是(假设您当前未指定的成本函数显示最佳子结构)。

对于线性成本函数,以下经典算法在时间O(n log n)中运行,其中n是间隔数。从中间初始化有序地图到覆盖[0,mid]的成本。最初,此地图有一个条目,0 - &gt; 0.按右端点对间隔进行排序,并按如下顺序处理它们。要处理[a,b],找到地图条目mid - &gt;成本使得mid> = a尽可能小。 (如果不存在这样的条目,那就继续。)让费用&#39; =成本+成本([a,b]),其中成本([a,b])未指定但始终为正。虽然地图中的最后一个条目的费用大于或等于费用,但请将其删除。插入b - &gt;成本&#39 ;.要完成,请查找end的后继,其中[0,end]是要覆盖的空间。

即使您的成本函数不是线性的,因为它是总间隔长度和间隔数的一个(可能是单调的)函数,我们可以得到一个O(n ^ 2)时间算法,而不是记住每个中点只需要一个成本,记住0到n之间的每个整数,使用指定间隔数的解决方案的成本。

答案 1 :(得分:1)

您不必显式构建树 - 您可以使用递归深度优先搜索来实现相同的效果。

在递归深度优先搜索中的每个点,您将构建一组覆盖[0,x]的间隔,并且您将要扩展它。为此,您需要找到与x相交并在x之后结束的所有间隔。当您递归树时,您将要对y&gt;执行相同的搜索。 x等等。

加快这种速度的一种方法是将间隔的起点和终点放入数组并对它们进行排序。你保留一个指向数组的指针,该数组标记位置x和一组覆盖x的区间,可能存储为哈希集。当您前进位置x时,您沿着列表移动指针,当您看到右手点时从组中删除间隔,并在看到左手点时向组中添加间隔。您可以以非常类似的方式备份。

这应该允许您跟踪可以使用哪些间隔来扩展每个点的覆盖[0,x]而不搜索每个可能的间隔。

这应该允许您合理有效地枚举所有可能的覆盖列表。为了找到最便宜的覆盖物而不列举所有可能的覆盖物,我们需要了解有关成本函数的更多信息。

答案 2 :(得分:1)

我很确定这可以优化,但下面是一个工作版本。将尝试优化它并再次更新:

from operator import itemgetter
import collections
import random

def generate_sample_input(space):
    # This method generates a set of tuples, each tuple consisting of 2 nos
    low, high = space
    init = (low, random.randint(low + 1, (low + high)/2))
    last = (random.randint((low + high)/2 + 1, high), high)
    mid = random.randint(init[1] + 1, last[0] - 1)
    ranges = [init, (init[1] - 1, mid + 1), (mid - 1, last[0] + 1), last]
    nums = {i for tup in ranges for i in tup}
    for _ in range(random.randint(0, 20)):
        low = random.randint(space[0], space[1] - 1)
        high = random.randint(low, space[1])
        if all(i not in nums for i in (low, high)):
            nums |= {low, high}
            ranges.append((low, high))
    return sorted(set(ranges), key = lambda x: x[0])

class Node(object):

    def __init__(self, tup):
        assert len(tup) == 2 and all(type(x) == int for x in tup)
        self.low, self.high = tup
        self.visitable_nodes = []
        self.piggybacker_nodes = []

    def __repr__(self):
        return "(%s, %s)" % (self.low, self.high)

    def set_visitable(self, node):
        assert type(node) == Node
        if self.low < node.low and node.high < self.high:
            self.piggybacker_nodes.append(node)
        elif self.low < node.low < self.high:
            self.visitable_nodes.append(node)

class Graph(object):

    def __init__(self):
        self.sources, self.sinks, self.nodes = [], [], []

    def add(self, node, is_sink=False, is_source=False):
        assert type(node) == Node and not (is_sink and is_source)
        for old_node in self.nodes:
            old_node.set_visitable(node)
            node.set_visitable(old_node)
        self.nodes.append(node)
        if is_sink:
            self.sinks.append(node)
        elif is_source:
            self.sources.append(node)

    def create_graph(self, ranges=[], space=[]):
        for tup in ranges:
            self.add(Node(tup), is_source=tup[0]==space[0], 
                     is_sink=tup[1]==space[1])

def dfs(stack=[], nodes=[], sinks=[], level=0):
    for node in nodes:
        if node in sinks:
            print stack + [node]
        dfs(stack + [node], node.visitable_nodes, sinks, level + 1)

def main():
    space = (0, 100)
    ranges = generate_sample_input(space)
    graph = Graph()
    graph.create_graph(space=space, ranges=ranges)
    print ranges
    dfs([], nodes=graph.sources, sinks=graph.sinks)

if __name__ == "__main__":
    main()