2016-05-30 8 views
5

文書ではData.Sequence.reverseはO(n)です。有限シーケンス型をフラグを設定することによって "反転"できないか? (事実上、から「カウントを開始」するために、エンドいる。)なぜData.Sequence.reverse O(n)ですか?

+3

それは可能ですが、そのフラグを運ぶために構造を拡張する必要があります。明らかに、Data.Sequenceの保守担当者は、それが価値がないと判断しました。 – Cubic

+1

私はそれが必要な場合、私はそのようなカスタムフラグを運ぶ 'Seq'変種を書くだろう。 – chi

+0

ほとんどのユースケースでは、追加のフラグの管理が導入するパフォーマンスのオーバーヘッドについては満足できません。 'Seq'をそれが何であるかにしましょう。あなたが必要とするものは、その上に単純なラッパーです。私たちは明確に分離可能な2つの抽象化を持っています。 –

答えて

3

フラグが1つだけの場合、問題が発生します。私はそれを計算することができる方法

xs <> reverse xs 

たいと?ただ1つのフラグで、追加する前に高価な逆を実行する必要があります。実際にこれを行うには、より多くの旗が必要です。 すべて木の周り。ツリー内のすべてのノードは、反転情報を運ぶ必要があります。これは間違いなく可能ですが、いくらか高価です。さらに悪いことに、すべての操作のすべてのステップで、反転フラグを適切に管理する必要があります。これは、誰でもコードを記述して維持する必要があります。このアイデアはどこかの紙の中で作られています(私はどちらが覚えていないのですか)が、実際には楽しいことではありません。

本質的に反転に依存するアルゴリズムを使用している場合は、多くのフラグを持つカスタムシーケンスタイプを使用し、必要な操作だけを含める必要があります(タイプをData.Sequenceに設定している場合は、各サイズフィールドから1ビット)。しかし、私はそのようなアルゴリズムはあまり一般的ではないと思う。

+0

'><'を意味しましたか?もしそうなら、連結上の逆転の費用を負担する必要性がなぜ単一の旗への反対であるのかわかりません。これは実際に必要になるまで経費を遅らせるだけだと思いますか? – Alan

+0

@Alan、 '<>'は '(<>):: Monoid a => a - > a - > a'の関数か' Semigroup'クラスのメソッドのいずれかです(ベースライブラリのバージョンに依存します)。同様の署名。 Haskellの一般的なコミュニティでは '><'より、 '(<>)=(><)'の場合はもっと広く知られています。費用が必要になるまで遅らせることは良いことですが、パフォーマンスを予測するのが難しく、多くの場合悪いこともあります。実際には、私は多くのシーケンス操作を自分の仕事よりも*熱心にこれに対処するように働いており、ベンチマークのパフォーマンスをかなり向上させてきました。 – dfeuer

1

さてあなたは

data S a = S { forward :: Seq a , backward :: Seq a } 

を定義し、これはそれぞれのコストを倍増

forall a :: Type, s :: S a : reverse (forward s) == backward s . 

という不変ですべての操作を実装することができ一定時間はreverseとなる。

4

はい、可能です。

しかし、これにより、すべてのルックアップに小さな値段が追加されます(最初にフラグをチェックする必要があります)。また、reverseを使用するユーザーにのみ支払いが行われます。この後者のグループはおそらく小さいと判断された。タイプに適切なフラグを投げることはクライアント側で行うことができるので、ではなく、すべての関数を使用するたびにライブラリでこの値段を支払うことになります。代わりに、reverseをたくさん呼びたいプログラマーは、望むのであればこのパフォーマンスのトレードオフの選択肢を作ることを余儀なくされます。これは私には完全に賢明です。

+1

"最初にフラグをチェックする必要があります!"各要素ではなく(関数呼び出しごとに1回だけ)、 '><' operationは構造全体を再構築する必要があります – josejuan

0

><連結がペナルティなしでxs >< reversed ysの連結を認めている場合は、O(1)で完了すると思います(フィンガーツリー構造に何らかのハックが必要です)。すべての操作は逆の対応(例えばtakeWhileLtakeWhileR)を持っているか、O(n)取るData.Sequenceに探し

; ><操作を除いて(または最悪を。例えばzipseq1ときn1<n2などを逆にし、O(min(n1,n2))を取ります)。

いずれにしても、xs >< ysは、最悪の場合(4つの可能なケースのうちの2つ)としてO(min(|xs|,|ys|))になります。これは実際にはO(n)より少し優れています(ユニークな方向への正規化と反転が必要です)。

その後、質問は次のとおりです。

O(min(|xs|,|ys|))より良いxs >< reversed ysを行うことができますか?

(理想的にO(log(min(|xs|,|ys|))))もちろん

、あなたが><操作を使用しない場合は、逆の操作を取るO(1)(例えば使用が左または右に対応するときチェックするためにフラグを使用)。

一方、逆順シーケンスのパフォーマンスを連結することが重要な場合は、おそらく他の最適な構造が存在するか、または@d8d0d65b3f7cf42 strategyと考えることができます。

+0

私は魔法のハックはありません追加します。追加シーケンスは、左側のシーケンスの左側と右側のシーケンスの右側を取り、残りのセグメントを再帰的にマッシュして中央を形成することで動作します。あなたがそれぞれの左側を取って1つを反転させると、再帰が進むにつれて、中間のマッシングが次第に高価になります。 – dfeuer

+0

これは、私が 'ハック'と言った理由です。連結の逆を避けるための1つの愚かなハックは、逆順のそれぞれの連結のためにシーケンスのシーケンス(実際にはツリー)かもしれません。逆の操作は償却されます( 'O(n)'操作が要求されるまで)、次の操作( 'index'など)は' O(log t * log n) 'の代わりに' (t * n)) 'tは保留中の連結で、' n'は平均トランクサイズです。ハックはマジックではありません... – josejuan

+0

私の愚かなハックはあなたのレスポンスに書かれています! xD xD:P – josejuan

関連する問題