Frage

überspringen Listen (Pugh, 1990) liefern sortierte Wörterbücher mit logarithmischer -Zeit Operationen wie Suchbäume aber überspringen Listen sind viel zugänglicher gleichzeitige Aktualisierungen .

Ist es möglich, eine effiziente rein funktional gleichzeitige Sprungliste zu erstellen? Wenn nicht, ist es möglich, jede Art von effizienten rein funktional gleichzeitig sortierten Wörterbuch erstellen?

War es hilfreich?

Lösung

Die Eigenschaft der Sprunglisten, die sie gut für die gleichzeitige Aktualisierungen macht (nämlich, dass die meisten Additionen und Subtraktionen lokal sind) macht sie auch schlecht für die Unveränderlichkeit (nämlich, dass viele frühere Elemente in der Liste Punkt schließlich zu den späteren Artikel, und müßte geändert werden).

Insbesondere überspringen Listen bestehen aus Strukturen, die aussehen wie folgt:

NODE1 ---------------------> NODE2 ---------...
  |                           |
  V                           V
NODE1a --> NODE1b ---------> NODE2a --> NODE2b --> NODE2c --- ...

Nun, wenn Sie ein Update haben, die, sagen wir, NODE2b oder NODE1b löscht, können Sie sich darum kümmern sehr lokal: Sie gerade Punkt 2a zu 2c oder 1a zu 2a jeweils und du bist fertig. Leider, denn das Blatt alle einem zum anderen Punkt Knoten, es ist keine gute Struktur für eine funktionale (unveränderlich) zu aktualisieren.

So Baumstrukturen sind besser für die Unveränderlichkeit (wie der Schaden immer lokal begrenzt ist - nur der Knoten, den Sie kümmern uns um und ihre direkt Eltern nach oben durch die Wurzel des Baumes)

.

Concurrent Updates funktionieren nicht gut mit unveränderlichen Datenstrukturen. Wenn man darüber nachdenkt, hat jede funktionelle Lösung, die ein Update von A als f(A). Wenn Sie zwei Updates wollen, da ein von f und einem von g gegeben, Sie haben ziemlich viel f(g(A)) oder g(f(A)) zu tun, oder müssen Sie die Anfragen abfangen und eine neue Operation h = f,g schaffen, dass Sie alle in einem Rutsch anwenden können (oder Sie haben verschiedene andere sehr kluge Sachen zu tun).

Doch gleichzeitig liest Arbeit fantastisch gut mit unveränderlichen Datenstrukturen, da Sie garantiert keine Statusänderung haben. Wenn Sie nicht davon ausgehen, dass Sie eine Lese- / Schreib-Schleife haben kann, dass Entschlüsse vor allen anderen Schreibunterbrechen kann, dann müssen Sie sich niemals auf Lese sperren.

So Schreib schwere Datenstrukturen sind wahrscheinlich besser mutably umgesetzt (und mit so etwas wie eine Sprungliste, in der Sie nur lokal müssen sperren), während Leselastige Datenstrukturen wahrscheinlich besser unwandelbar umgesetzt werden (wo ein Baum ist ein mehr natürliche Datenstruktur).

Andere Tipps

Andrew McKinlay-Lösung ist die eigentliche „true“ funktionelle Lösung für eine echte Sprung-Liste hier, aber es hat einen Nachteil. Sie zahlen logarithmische Zeit ein Element zuzugreifen, aber jetzt Mutation über das Kopfelement wird hoffnungslos. Die Antwort, die Sie wollen, ist in unzähligen Pfad-Kopien begraben unten!

können wir besser machen?

Ein Teil des Problems ist, dass es mehrere Wege von -unendlich zu Ihrem Artikel.

Aber wenn man die Algorithmen durchdenken einen skiplist für die Suche, die Sie nie diese Tatsache nutzen.

Wir können in den Baum jedes Knotens denken als eine bevorzugte Verbindung, wobei die meisten Top-Link, um es von der linken Seite, die in gewissem Sinne gedacht werden kann als ‚Besitz‘ dieser Eintrag.

Jetzt können wir den Begriff der ‚Finger‘ in eine Datenstruktur betrachten, die eine funktionelle Technik ist, die Sie auf ein bestimmtes Element konzentrieren können, und einen Weg zurück zur Wurzel liefern.

Jetzt können wir mit einer einfachen Sprung-Liste starten

-inf-------------------> 16
-inf ------> 8 --------> 16
-inf -> 4 -> 8 -> 12 --> 16

es von Ebene erweitern:

-inf-------------------> 16
  |                       |
  v                       v
-inf ------> 8 --------> 16
  |          |            |
  v          v            v
-inf -> 4 -> 8 -> 12 --> 16

Streifen aus alle, aber die bevorzugten Zeiger:

-inf-------------------> 16
  |                       |
  v                       v
-inf ------> 8           16
  |          |            |
  v          v            v
-inf -> 4    8 -> 12     16

Dann könnte man einen ‚Finger‘ bewegen zu Position 8 durch alle Zeiger Tracking würde man dort drehen muß, um zu.

-inf ------------------> 16
   ^                      |
   |                      v
-inf <------ 8           16
   |         |            |
   v         v            v
-inf -> 4    8 -> 12     16

Von dort aus ist es möglich, 8 zu löschen, mit dem Finger irgendwo anders schieben, und Sie können weiterhin durch die Struktur mit dem Finger navigieren.

auf diese Weise betrachtet, kann man sehen, dass die privilegierten Pfade in einer Sprungliste ein Spanning Tree bilden!

Bewegen 1 Schritt mit dem Finger ist ein O (1) -Operation, wenn Sie nur privilegierte Zeiger in dem Baum und die Verwendung „skinny Knoten“ wie diese haben. Wenn Sie Fett Knoten verwendet, dann Fingerbewegung nach links / rechts wäre möglicherweise teurer.

Alle Operationen bleiben O (log n) und Sie können eine Sprunglistenstruktur verwenden randomisierte oder deterministisch wie gewohnt.

Das heißt, wenn wir den skiplist nach unten in eine Vorstellung von einem bevorzugten Weg brechen dann bekommen wir, dass ein skiplist ist nur ein Baum mit einigen redundanten nicht bevorzugten Verbindungen, die wir nicht für Einsatz benötigen / search / löschen, so dass die Länge jedes dieser Pfade von der oberen rechten Ecke ist O (log n) entweder mit hoher Wahrscheinlichkeit oder garantiert je nach Aktualisierungsstrategie.

Auch ohne den Finger können Sie pflegen O (log n) erwartet Zeit pro Einfügen / Löschen / Update in einem Baum mit dieser Form.

Nun, das Schlüsselwort in Ihrer Frage, die nicht sinnvoll ist „concurrent“. Eine rein funktionale Datenstruktur hat keinen Begriff von in-Place-Mutation. Sie produzieren immer eine neue Sache. In gewissem Sinne ist gleichzeitig funktionales Updates einfach. Jeder bekommt ihre eigene Antwort! Sie können einfach nicht gegenseitig sehen.

Nicht eine Sprungliste, scheint aber die Problembeschreibung entspricht: Clojure beharrliche rot-schwarz Bäume (siehe PersistentTreeMap.java ). Die Quelle enthält die folgenden Hinweis:

/**
 * 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
 */

halten diese Bäume die Reihenfolge der Elemente und sind „persistent“ in dem Sinne, in der Rich-Hickey verwendet das Wort (unveränderlich und in der Lage, um ihre Leistungsgarantien als aktualisierte Versionen beibehalten werden aufgebaut).

Falls Sie mit ihnen spielen, um, Sie Instanzen in Clojure Code mit der Funktion sorted-map konstruieren können.

Wenn Sie nur auf der Vorderseite der Sprungliste Nachteile müssen, dann sollte es möglich sein, eine persistente unveränderliche Version zu machen.

Der Vorteil dieser Art von Sprungliste wäre „random“ Zugang. z.B. Sie könnten das n-te Element zugreifen schneller, als Sie in einer regelmäßigen einzelnen verknüpften Liste könnten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top