純粋に機能的な同時スキップリスト
-
28-09-2019 - |
質問
リストをスキップします (Pugh、1990)検索ツリーのような対数時間操作を並べ替えた辞書を提供しますが スキップリストは、同時の更新をより適しています.
効率的に純粋に機能的な同時スキップリストを作成することは可能ですか?そうでない場合は、純粋に機能的に同時並べ替えられた辞書を作成することは可能ですか?
解決
同時の更新に適しているスキップリストのプロパティ(つまり、ほとんどの追加と減算はローカル)にも不変性が悪くなります(つまり、リスト内の以前のアイテムの多くは最終的に後のアイテムを指し、必要になる必要があります。変更されます)。
具体的には、スキップリストは次のように見える構造で構成されています。
NODE1 ---------------------> NODE2 ---------...
| |
V V
NODE1a --> NODE1b ---------> NODE2a --> NODE2b --> NODE2c --- ...
今、あなたがそれを削除するアップデートがある場合 NODE2b
また NODE1b
, 、あなたはそれを非常にローカルで世話することができます:あなたはただ指し示します 2a
に 2c
また 1a
に 2a
それぞれ完了です。残念ながら、リーフノードはすべてポイントを1つずつ互いに指し示すため、機能的な(不変の)アップデートの良い構造ではありません。
したがって、ツリー構造は不変性に適しています(損傷は常に局所的に制限されているためです。あなたが気にするノードと、その直接の親が木の根を通っているだけです)。
同時の更新は、不変のデータ構造ではうまく機能しません。考えてみると、機能的なソリューションには更新があります A
なので f(A)
. 。 2つの更新が必要な場合は、1つは提供されます f
そして、それによって与えられたもの g
, 、あなたはほとんどしなければなりません f(g(A))
また g(f(A))
, 、またはリクエストを傍受して新しい操作を作成する必要があります h = f,g
すべてを一度に適用できること(または、他のさまざまな非常に賢いことをする必要があります)。
ただし、同時は、状態の変更がないことが保証されているため、不変のデータ構造で巧みに機能します。他の書き込みが中断される前に解決する読み取り/書き込みループを持つことができると仮定しない場合、読み取りをロックする必要はありません。
したがって、書き込みが多いデータ構造は、おそらくよりよく実装されています(そして、ローカルでのみロックする必要があるスキップリストのようなものを使用)、読み取りが多いデータ構造はおそらくより適切に実装されています(ツリーはより自然なデータ構造です。 )。
他のヒント
Andrew McKinlayのソリューションは、ここの実際のスキップリストのための本当の「真の」機能的ソリューションですが、マイナス面があります。 3つの要素にアクセスするために対数時間を支払いますが、ヘッド要素を超えた突然変異は絶望的になります。あなたが望む答えは、無数のパスコピーに埋もれています!
もっと良くできますか?
問題の一部は、 - インフィニティからアイテムへの複数のパスがあることです。
しかし、スカップリストを検索するためのアルゴリズムを介して考える場合、その事実を決して使用しません。
ツリー内の各ノードは、左から最上位リンクである優先リンクを持っていると考えることができます。
これで、「指」の概念をデータ構造に考慮することができます。これは、特定の要素に焦点を合わせてルートに戻るパスを供給できる機能的手法です。
これで、簡単なスキップリストから始めることができます
-inf-------------------> 16
-inf ------> 8 --------> 16
-inf -> 4 -> 8 -> 12 --> 16
レベルごとに拡張します:
-inf-------------------> 16
| |
v v
-inf ------> 8 --------> 16
| | |
v v v
-inf -> 4 -> 8 -> 12 --> 16
好みのポインターを除くすべてを取り除きます:
-inf-------------------> 16
| |
v v
-inf ------> 8 16
| | |
v v v
-inf -> 4 8 -> 12 16
その後、「指」を移動して8位置に移動できます。
-inf ------------------> 16
^ |
| v
-inf <------ 8 16
| | |
v v v
-inf -> 4 8 -> 12 16
そこから8を削除して、他のどこかに指を押して、指で構造をナビゲートし続けることができます。
このように見て、スキップリストの特権的なパスがスパニングツリーを形成することがわかります!
指で1つのステップを移動することは、木に特権的なポインターのみがあり、このような「スキニーノード」を使用する場合のO(1)操作です。脂肪ノードを使用した場合、左/右の指の動きはより高価になります。
すべての操作はO(log n)のままであり、ランダム化されたスキップリスト構造または通常どおり決定論的構造を使用できます。
そうは言っても、スカップリストを優先パスの概念に分解すると、スキップリストは、挿入/検索/削除には必要ない冗長な非適切なリンクを備えた単なるツリーであるとわかります。右上からそれらの各パスの長さは、高い確率でO(log n)であるか、更新戦略に応じて保証されます。
指がなくても、このフォームのツリーで挿入/削除/更新ごとにO(log n)の予想時間を維持できます。
さて、あなたの質問の意味を理解していないキーワードは「同時」です。純粋に機能的なデータ構造には、インプレース変異の概念はありません。あなたは常に新しいものを生み出します。ある意味で、同時に機能的な更新は簡単です。誰もが自分の答えを得る!彼らはお互いを見ることができません」。
スキップリストではありませんが、問題に一致するようです。説明:Clojureの永続的な赤黒木( persistenttreemap.java)。ソースにはこの通知が含まれています。
/**
* Persistent Red Black Tree
* Note that instances of this class are constant values
* i.e. add/remove etc return new values
* <p/>
* See Okasaki, Kahrs, Larsen et al
*/
これらの木は、要素の順序を維持し、リッチなヒッキーが単語を使用するという意味で「永続的」です(更新されたバージョンが構築されるにつれてパフォーマンス保証を維持することができます)。
あなたが彼らと一緒に遊びたい場合に、あなたは関数を使用してClojureコードのインスタンスを構築することができます sorted-map
.
スキップリストの前面でのみConsが必要な場合は、永続的な不変バージョンを作成することができるはずです。
この種のスキップリストの利点は、「ランダム」アクセスです。たとえば、通常のシングルリンクリストよりも速くn'th要素にアクセスできます。