web-dev-qa-db-ja.com

Python)でのセグメントツリーの実装

セグメントツリー を使用して this 問題を解決していますが、制限時間エラーが発生します。以下は、範囲最小クエリの生のコードです。コードでminmaxに変更することで、上記の問題を解決できます。コードのパフォーマンスを改善する方法がわかりません。パフォーマンスの問題について教えてください。

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

これを繰り返し実装できますか?

7
john smith

言語の選択

まず、Pythonを使用している場合、おそらく採点者に合格することはありません。ここで過去のすべてのソリューションのステータスを見ると、 http://www.spoj.com/status/GSS1/start= 、受け入れられたほとんどすべてのソリューションが次のように記述されていることがわかります。 C++。 C++を使用する以外に選択肢はないと思います。制限時間が0.115秒から0.230秒であることに注意してください。これは「C/C++専用」の時間制限です。他の言語で解決策を受け入れる問題の場合、制限時間は1秒のような「ラウンド」数になります。 Pythonは、このタイプの環境ではC++よりも約2〜4倍遅くなります。

セグメントツリーの実装の問題...?

次に、コードが実際にセグメントツリーを構築しているかどうかはわかりません。具体的には、なぜこの行があるのか​​わかりません。

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

セグメントツリーのノードには子の合計が格納されていると確信しているので、実装がほぼ正しい場合は、代わりに次のように読み取る必要があると思います。

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

コードが「正しい」場合、間隔の合計を保存していなくても、範囲_[x_i, y_i]_内の最大間隔の合計をどのように見つけているのか疑問に思います。

反復セグメントツリー

第3に、セグメントツリーを繰り返し実装できます。これがC++のチュートリアルです: http://codeforces.com/blog/entry/18051

セグメントツリーは、これに十分な速度ではないはずです...

最後に、セグメントツリーがこの問題にどのように役立つのかわかりません。セグメントツリーを使用すると、log(n)の範囲の合計をクエリできます。この問題は、任意の範囲の可能な最大合計を要求します。 「範囲最小クエリ」または「範囲最大クエリ」を可能にするセグメントツリーについて聞いたことがありません。

単純な解決策は、1つのクエリに対してO(n ^ 3)(n ^ 2の可能なすべての開始点と終了点を試し、O(n)操作)で合計を計算する)です。セグメントツリーを使用すると、O(n)の代わりにO(log(n))で合計を取得できます)。 、N = 50000では機能しません。

代替アルゴリズム

代わりに、クエリごとにO(n)で実行されるこれを確認する必要があると思います: http://www.geeksforgeeks.org/largest-sum-contiguous-subarray / 。C/ C++で記述し、1人のコメント提供者が提案したようにIOで効率的にします。

14
user2570465

多くの制限を回避できるので、ジェネレーターを試してみることができます。ただし、パフォーマンスの問題を明確に示すデータセットを提供していません。問題のあるデータセットを提供できますか?

ここで試すことができます:

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前後)

2
Pierre Alex