【问题标题】:Segment tree implementation in PythonPython中的段树实现
【发布时间】:2016-12-11 11:18:45
【问题描述】:

我正在使用segment tree 解决this 问题,但出现时间限制错误。 下面是我的范围最小查询的原始代码,通过在我的代码中将min 更改为max,可以解决上述问题。我不知道如何提高代码的性能。你能帮我解决它的性能问题吗?

t = [None] * 2 * 7      # n is length of list


def build(a, v, start, end):
    '''
    A recursive function that constructs Segment Tree for list a.
    v is the starting node
    start and end are the index of array
    '''

    n = len(a)
    if start == end:
        t[v] = a[start]
    else:
        mid = (start + end) / 2
        build(a, v * 2, start, mid)     # v*2 is left child of parent v
        # v*2+1 is the right child of parent v
        build(a, v * 2 + 1, mid + 1, end)
        t[v] = min(t[2 * v], t[2 * v + 1])
    return t

print build([18, 17, 13, 19, 15, 11, 20], 1, 0, 6)

inf = 10**9 + 7


def range_minimum_query(node, segx, segy, qx, qy):
    '''
    returns the minimum number in range(qx,qy)
    segx and segy represent the segment index

    '''
    if qx > segy or qy < segx:      # query out of range
        return inf
    elif segx >= qx and segy <= qy:  # query range inside segment range
        return t[node]
    else:
        return min(range_minimum_query(node * 2, segx, (segx + segy) / 2, qx, qy), range_minimum_query(node * 2 + 1, ((segx + segy) / 2) + 1, segy, qx, qy))

print range_minimum_query(1, 1, 7, 1, 3)

# returns 13

这可以迭代实现吗?

【问题讨论】:

  • can you help me with [the code's] performance issues? 您想要自己解决问题的提示,还是想要分析和编码的解决方案? segments 在哪里出现? (你读过description of the segment tag 吗?)(赞成提供文档字符串 - 考虑重命名rmq 以反映 range minimum query 在这种情况下。)我的 2cents:你的问题是 not 递归与迭代。
  • @greybeard 我想要分析和编码的解决方案。在添加标签时,我写了段树,但它分成了树和段标签(对不起)。
  • sorry for that - 我认为你知道如何编辑标签。)有人听说过priority search tree吗?
  • 对于任何性能问题,我推荐Python“线程”模块。它允许同时运行多个东西。
  • 如果这被标记为segment-tree,您可能会怀疑build() 是否构建了一个段树:更常见的是原子区间的边界比任何有效的索引有明确的坐标,并且集合的片段重叠。您选择拥有end inclusive 似乎不合常规,而评论query range inside segment range 则倒退。 (我不知道为什么你的代码应该比最优值的两倍多。)

标签: python algorithm segment-tree


【解决方案1】:

语言选择

首先,如果你使用 python,你可能永远不会通过评分器。如果您在此处查看所有过去解决方案的状态 http://www.spoj.com/status/GSS1/start=0 ,您会发现几乎所有已接受的解决方案都是用 C++ 编写的。我认为您别无选择,只能使用 C++。注意时间限制是 0.115s-0.230s。这是“仅适用于 C/C++”的时间限制。对于将接受其他语言解决方案的问题,时间限制将是一个“整数”,例如 1 秒。在这种类型的环境中,Python 比 C++ 慢大约 2-4 倍。

Segment Tree 实现问题...?

其次,我不确定你的代码是否真的在构建一个段树。具体来说,我不明白为什么会有这条线:

t[v]=min(t[2*v],t[2*v+1]) 

我很确定段树中的一个节点存储了它的子节点的总和,所以如果你的实现接近正确,我认为它应该改为读取

t[v] = t[2*v] + t[2*v+1]

如果您的代码“正确”,那么如果您甚至不存储区间总和,我会质疑您如何在[x_i, y_i] 范围内找到最大区间总和。

迭代段树

第三,段树可以迭代实现。这是 C++ 教程:http://codeforces.com/blog/entry/18051

段树不应该足够快...

最后,我不明白分段树将如何帮助您解决这个问题。段树可让您查询log(n) 中范围的总和。这个问题要求任何范围的最大可能总和。我还没有听说过允许“范围最小查询”或“范围最大查询”的段树。

一个简单的解决方案将是 O(n^3)(尝试所有 n^2 个可能的起点和终点,并计算 O(n) 操作中的总和)1 个查询。而且,如果你使用分段树,你可以得到 O(log(n)) 而不是 O(n) 的总和。这只会让你加速到 O(n^2 log(n)),这对于 N = 50000 是行不通的。

替代算法

我认为你应该看看这个,它在 O(n) 每个查询中运行:http://www.geeksforgeeks.org/largest-sum-contiguous-subarray/。正如一位评论者所建议的那样,用 C/C++ 编写它并使用 IO 来提高效率。

【讨论】:

  • 我正在解决范围最小查询问题:“我们有一个数组 arr[0 . . . n-1]。我们应该能够有效地从索引 qs 中找到最小值(查询开始)到 qe(查询结束),其中 0 geeksforgeeks.org/segment-tree-set-1-range-minimum-query
  • 该算法为您提供区间内的最小值。它不会为您提供 SPOJ 问题所要求的区间内的最小/最大连续总和。
  • 我同意,python 很可能会超时,但是在不知道 M 的最大值的情况下,很难确定地说。但我确实认为,可以借助分段树来解决问题,但不是我们在这里讨论的版本 - 将 min 更改为 max 并不能解决问题。
  • @ead 现在我再想一想,似乎确实有一种方法可以将分段树与 Kadane 的算法相结合。简单的 Kadane 算法将采用O(M*n)。也许有一种方法可以构造一个段树并将这个想法应用到 Kadane 的算法中,这样我们就可以在log(n) 中查询最大连续子数组和。这将产生类似于O(n + M*log(n)) 的复杂性。
  • “我认为你别无选择,只能使用 C++”在我们拥有 Cython 和 PyPy 时仍然有效?
【解决方案2】:

您可以尝试使用生成器,因为您可以绕过很多限制。但是,您没有提供清楚显示您的性能问题的数据集 - 您能提供有问题的数据集吗?

你可以在这里试试:

t=[None]*2*7
inf=10**9+7

def build_generator(a, v, start, end):
    n = len(a)

    if start == end:
        t[v] = a[start]
    else:
        mid = (start + end) / 2
        next(build_generator(a, v * 2, start, mid))
        next(build_generator(a, v * 2 + 1, mid + 1, end))
        t[v] = min(t[2 * v], t[2 * v + 1])
    yield t



def range_minimum_query_generator(node,segx,segy,qx,qy):
    if qx > segy or qy < segx:
        yield inf
    elif segx >= qx and segy <= qy:
        yield t[node]
    else:
        min_ = min(
            next(range_minimum_query_generator(node*2,segx,(segx+segy)/2,qx,qy)),
            next(range_minimum_query_generator(node*2+1,((segx+segy)/2)+1,segy,qx,qy))
        )
        yield min_

next(build_generator([18,17,13,19,15,11,20],1,0,6))
value = next(range_minimum_query_generator(1, 1, 7, 1, 3))
print(value)

编辑

事实上,这可能无法解决您的问题。还有另一种解决任何递归限制的方法(如 D. Beazley 在其关于生成器的教程中所述 - https://www.youtube.com/watch?v=D1twn9kLmYg&t=9588s 在时间码 2h00 附近)

【讨论】:

  • 感谢您的视频参考。疯狂的东西!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-22
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 2012-02-18
  • 1970-01-01
相关资源
最近更新 更多