Frage

Jetzt habe ich immer gehört, binäre Suchbäume sind schneller zu bauen aus zufällig ausgewählten Daten als geordnete Daten, einfach weil geordnete Daten expliziten Rebalancing erfordern mindestens die Baumhöhe zu halten.

Vor kurzem implementiert ich ein unveränderliches Treap , eine besondere Art von binärer Suchbaum, der Randomisierung verwendet, um halten sich relativ ausgewogen. Im Gegensatz zu dem, was ich erwartet habe, fand ich, dass ich eine Treap über 2x schneller konsequent aufbauen kann und in der Regel besser ausgeglichen aus geordneten Daten als ungeordnete Daten -. Und ich habe keine Ahnung, warum

Hier ist meine Treap Implementierung:

Und hier ist ein Testprogramm:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace ConsoleApplication1
{

    class Program
    {
        static Random rnd = new Random();
        const int ITERATION_COUNT = 20;

        static void Main(string[] args)
        {
            List<double> rndTimes = new List<double>();
            List<double> orderedTimes = new List<double>();

            rndTimes.Add(TimeIt(50, RandomInsert));
            rndTimes.Add(TimeIt(100, RandomInsert));
            rndTimes.Add(TimeIt(200, RandomInsert));
            rndTimes.Add(TimeIt(400, RandomInsert));
            rndTimes.Add(TimeIt(800, RandomInsert));
            rndTimes.Add(TimeIt(1000, RandomInsert));
            rndTimes.Add(TimeIt(2000, RandomInsert));
            rndTimes.Add(TimeIt(4000, RandomInsert));
            rndTimes.Add(TimeIt(8000, RandomInsert));
            rndTimes.Add(TimeIt(16000, RandomInsert));
            rndTimes.Add(TimeIt(32000, RandomInsert));
            rndTimes.Add(TimeIt(64000, RandomInsert));
            rndTimes.Add(TimeIt(128000, RandomInsert));
            string rndTimesAsString = string.Join("\n", rndTimes.Select(x => x.ToString()).ToArray());

            orderedTimes.Add(TimeIt(50, OrderedInsert));
            orderedTimes.Add(TimeIt(100, OrderedInsert));
            orderedTimes.Add(TimeIt(200, OrderedInsert));
            orderedTimes.Add(TimeIt(400, OrderedInsert));
            orderedTimes.Add(TimeIt(800, OrderedInsert));
            orderedTimes.Add(TimeIt(1000, OrderedInsert));
            orderedTimes.Add(TimeIt(2000, OrderedInsert));
            orderedTimes.Add(TimeIt(4000, OrderedInsert));
            orderedTimes.Add(TimeIt(8000, OrderedInsert));
            orderedTimes.Add(TimeIt(16000, OrderedInsert));
            orderedTimes.Add(TimeIt(32000, OrderedInsert));
            orderedTimes.Add(TimeIt(64000, OrderedInsert));
            orderedTimes.Add(TimeIt(128000, OrderedInsert));
            string orderedTimesAsString = string.Join("\n", orderedTimes.Select(x => x.ToString()).ToArray());

            Console.WriteLine("Done");
        }

        static double TimeIt(int insertCount, Action<int> f)
        {
            Console.WriteLine("TimeIt({0}, {1})", insertCount, f.Method.Name);

            List<double> times = new List<double>();
            for (int i = 0; i < ITERATION_COUNT; i++)
            {
                Stopwatch sw = Stopwatch.StartNew();
                f(insertCount);
                sw.Stop();
                times.Add(sw.Elapsed.TotalMilliseconds);
            }

            return times.Average();
        }

        static void RandomInsert(int insertCount)
        {
            Treap<double> tree = new Treap<double>((x, y) => x.CompareTo(y));
            for (int i = 0; i < insertCount; i++)
            {
                tree = tree.Insert(rnd.NextDouble());
            }
        }

        static void OrderedInsert(int insertCount)
        {
            Treap<double> tree = new Treap<double>((x, y) => x.CompareTo(y));
            for(int i = 0; i < insertCount; i++)
            {
                tree = tree.Insert(i + rnd.NextDouble());
            }
        }
    }
}

Und hier ist ein Diagramm vergleicht zufällige und geordnete Einsetzzeiten in Millisekunden:

Insertions         Random          Ordered         RandomTime / OrderedTime
50                 1.031665        0.261585        3.94
100                0.544345        1.377155        0.4
200                1.268320        0.734570        1.73
400                2.765555        1.639150        1.69
800                6.089700        3.558350        1.71
1000               7.855150        4.704190        1.67
2000               17.852000       12.554065       1.42
4000               40.157340       22.474445       1.79
8000               88.375430       48.364265       1.83
16000              197.524000      109.082200      1.81
32000              459.277050      238.154405      1.93
64000              1055.508875     512.020310      2.06
128000             2481.694230     1107.980425     2.24

Ich sehe nichts in dem Code, der asymptotisch schneller als ungeordneter Eingang bestellt Eingang macht, so dass ich mit einem Verlust bin, den Unterschied zu erklären.

Warum ist es so viel schneller ein Treap bestellt Eingang als Zufall Eingang zu bauen?

War es hilfreich?

Lösung

Self-Balancing-Bäume existieren fix die damit verbundenen Probleme nicht zufällig verteilten Daten. Per Definition handeln sie ein wenig von der Best-Case-Leistung weg erheblich die Worst-Case-Leistung mit nicht ausgeglichenen BSTs assoziiert zu verbessern, insbesondere, dass der sortierten Eingangs.

Sie dieses Problem tatsächlich Grübeln, weil langsames Einfügen von Zufallsdaten vs. Daten bestellt ist eine Eigenschaft von jeder ausgewogen Baum. Versuchen Sie es auf einem AVL und Sie werden die gleichen Ergebnisse sehen.

Cameron hat die richtige Idee, die Vorrangprüfung Entfernen den schlimmsten Fall zu erzwingen. Wenn Sie das tun und Instrument Ihrem Baum so können Sie sehen, wie viele wieder ins Gleichgewicht bringt für jeden Einsatz sind passiert, es ist eigentlich sehr offensichtlich wird, was los ist. Wenn sortiert Einfügen von Daten, der Baum immer links dreht und das Recht des Kindes Wurzel ist immer leer. Insertion führt immer in genau einer Neuverteilung, weil der Einführungs Knoten keine Kinder hat und keine Rekursion auftritt. Auf der anderen Seite, wenn Sie es auf den Zufallsdaten auszuführen, starten Sie fast sofort mehrere Gleicht geschieht bei jedem Einsatz, so viele wie 5 oder 6 von ihnen im kleinsten Gehäuse (50 Einsätze), denn es passiert auf Teilbäume zu sehen, wie gut.

Mit Kontrolle Priorität wieder eingeschaltet, nicht nur in der Regel wieder ins Gleichgewicht weniger teuer durch mehr Knoten in den linken Unterbaum geschoben werden (wo sie nie kommen, weil keine Einfügungen passieren dort), aber sie auch sind weniger wahrscheinlich auftreten. Warum? Denn in der Treap, schweben mit hohen Priorität Knoten nach oben, und die konstanten Linksdrehungen (nicht durch Rechtsdrehungen begleitet) beginnen alle die mit hohen Priorität Knoten in den linken Unterbaum als auch zu schieben. Das Ergebnis ist, dass wieder ins Gleichgewicht kommt weniger häufig aufgrund der ungleichen Verteilung der Wahrscheinlichkeit.

Wenn Sie Instrument der Neugewichtung Code Sie sehen, dass dies wahr ist; sowohl für die sortierten und zufälligen Eingang, am Ende mit fast identischer Anzahl von Linksdrehungen, aber die zufällige Eingabe gibt auch die gleiche Anzahl von Rechtsdrehungen, die doppelt so viele in aller Fabrikate. Dies sollte nicht überraschen - Gauß-Eingang in einer Gaußschen Verteilung der Drehungen führen soll. Sie sehen auch, dass es nur etwa 60-70% sind so viele Top-Level für den sortierten Eingang wieder ins Gleichgewicht bringt, die vielleicht is überraschend, und wieder, dass die aufgrund der sortierten Eingang Messing mit dem natürlichen Verteilung der Prioritäten.

Sie können auch dies überprüfen, indem Sie den vollständigen Baum am Ende einer Insertionsschleife Inspektion. Mit dem Zufall Eingang neigen Prioritäten ziemlich linear von Ebene zu verringern; mit dem sortierten Eingang, neigen dazu, Prioritäten sehr hoch zu bleiben, bis Sie zu einer oder zwei Ebenen von unten zu bekommen.

Hoffentlich ich einen anständigen Job zu erklären dies getan haben ... lassen Sie mich wissen, ob irgendetwas davon zu vage ist.

Andere Tipps

lief ich den Code, und ich denke, es hat mit der Anzahl der Umdrehungen zu tun. Während bestellt Eingangs, ist die Anzahl der Umdrehungen optimal, und der Baum wird nie zu drehen zurück haben.

Während der Zufalls Eingang wird der Baum muss mehr Drehungen ausführen, weil es zu drehen zurück haben und her.

Um wirklich aus, würde ich die Zähler für die Anzahl der linken und rechten Umdrehungen für jeden Durchlauf hinzufügen. Sie können sich wahrscheinlich dies selbst tun.

UPDATE:

Ich habe Stützpunkte auf rotateLeft und rotateRight. Während bestellt Eingang rotateRight wird nie verwendet. Während der Zufalls Eingang werden beide getroffen, und es scheint mir, dass sie häufiger verwendet werden.

UPDATE 2:

Ich habe eine Ausgabe an die 50 Stück laufen geordnet (mit ganzen Zahlen aus Gründen der Klarheit ersetzt), um mehr zu erfahren:

TimeIt(50, OrderedInsert)
LastValue = 0, Top.Value = 0, Right.Count = 0, Left.Count = 0
RotateLeft @value=0
LastValue = 1, Top.Value = 1, Right.Count = 0, Left.Count = 1
LastValue = 2, Top.Value = 1, Right.Count = 1, Left.Count = 1
LastValue = 3, Top.Value = 1, Right.Count = 2, Left.Count = 1
RotateLeft @value=3
RotateLeft @value=2
RotateLeft @value=1
LastValue = 4, Top.Value = 4, Right.Count = 0, Left.Count = 4
LastValue = 5, Top.Value = 4, Right.Count = 1, Left.Count = 4
LastValue = 6, Top.Value = 4, Right.Count = 2, Left.Count = 4
RotateLeft @value=6
LastValue = 7, Top.Value = 4, Right.Count = 3, Left.Count = 4
LastValue = 8, Top.Value = 4, Right.Count = 4, Left.Count = 4
RotateLeft @value=8
RotateLeft @value=7
LastValue = 9, Top.Value = 4, Right.Count = 5, Left.Count = 4
LastValue = 10, Top.Value = 4, Right.Count = 6, Left.Count = 4
RotateLeft @value=10
RotateLeft @value=9
RotateLeft @value=5
RotateLeft @value=4
LastValue = 11, Top.Value = 11, Right.Count = 0, Left.Count = 11
LastValue = 12, Top.Value = 11, Right.Count = 1, Left.Count = 11
RotateLeft @value=12
LastValue = 13, Top.Value = 11, Right.Count = 2, Left.Count = 11
RotateLeft @value=13
LastValue = 14, Top.Value = 11, Right.Count = 3, Left.Count = 11
LastValue = 15, Top.Value = 11, Right.Count = 4, Left.Count = 11
RotateLeft @value=15
RotateLeft @value=14
LastValue = 16, Top.Value = 11, Right.Count = 5, Left.Count = 11
LastValue = 17, Top.Value = 11, Right.Count = 6, Left.Count = 11
RotateLeft @value=17
LastValue = 18, Top.Value = 11, Right.Count = 7, Left.Count = 11
LastValue = 19, Top.Value = 11, Right.Count = 8, Left.Count = 11
RotateLeft @value=19
LastValue = 20, Top.Value = 11, Right.Count = 9, Left.Count = 11
LastValue = 21, Top.Value = 11, Right.Count = 10, Left.Count = 11
RotateLeft @value=21
LastValue = 22, Top.Value = 11, Right.Count = 11, Left.Count = 11
RotateLeft @value=22
RotateLeft @value=20
RotateLeft @value=18
LastValue = 23, Top.Value = 11, Right.Count = 12, Left.Count = 11
LastValue = 24, Top.Value = 11, Right.Count = 13, Left.Count = 11
LastValue = 25, Top.Value = 11, Right.Count = 14, Left.Count = 11
RotateLeft @value=25
RotateLeft @value=24
LastValue = 26, Top.Value = 11, Right.Count = 15, Left.Count = 11
LastValue = 27, Top.Value = 11, Right.Count = 16, Left.Count = 11
RotateLeft @value=27
LastValue = 28, Top.Value = 11, Right.Count = 17, Left.Count = 11
RotateLeft @value=28
RotateLeft @value=26
RotateLeft @value=23
RotateLeft @value=16
RotateLeft @value=11
LastValue = 29, Top.Value = 29, Right.Count = 0, Left.Count = 29
LastValue = 30, Top.Value = 29, Right.Count = 1, Left.Count = 29
LastValue = 31, Top.Value = 29, Right.Count = 2, Left.Count = 29
LastValue = 32, Top.Value = 29, Right.Count = 3, Left.Count = 29
RotateLeft @value=32
RotateLeft @value=31
LastValue = 33, Top.Value = 29, Right.Count = 4, Left.Count = 29
RotateLeft @value=33
RotateLeft @value=30
LastValue = 34, Top.Value = 29, Right.Count = 5, Left.Count = 29
RotateLeft @value=34
LastValue = 35, Top.Value = 29, Right.Count = 6, Left.Count = 29
LastValue = 36, Top.Value = 29, Right.Count = 7, Left.Count = 29
LastValue = 37, Top.Value = 29, Right.Count = 8, Left.Count = 29
RotateLeft @value=37
LastValue = 38, Top.Value = 29, Right.Count = 9, Left.Count = 29
LastValue = 39, Top.Value = 29, Right.Count = 10, Left.Count = 29
RotateLeft @value=39
LastValue = 40, Top.Value = 29, Right.Count = 11, Left.Count = 29
RotateLeft @value=40
RotateLeft @value=38
RotateLeft @value=36
LastValue = 41, Top.Value = 29, Right.Count = 12, Left.Count = 29
LastValue = 42, Top.Value = 29, Right.Count = 13, Left.Count = 29
RotateLeft @value=42
LastValue = 43, Top.Value = 29, Right.Count = 14, Left.Count = 29
LastValue = 44, Top.Value = 29, Right.Count = 15, Left.Count = 29
RotateLeft @value=44
LastValue = 45, Top.Value = 29, Right.Count = 16, Left.Count = 29
LastValue = 46, Top.Value = 29, Right.Count = 17, Left.Count = 29
RotateLeft @value=46
RotateLeft @value=45
LastValue = 47, Top.Value = 29, Right.Count = 18, Left.Count = 29
LastValue = 48, Top.Value = 29, Right.Count = 19, Left.Count = 29
LastValue = 49, Top.Value = 29, Right.Count = 20, Left.Count = 29

Die immer bestellten Artikel wird auf die rechte Seite des Baumes hinzugefügt, natürlich. Wenn die rechte Seite größer als die linke bekommt, geschieht ein rotateLeft. RotateRight nie passiert. Ein neuer Top-Knoten wird in etwa jedes Mal, wenn der Baum verdoppelt ausgewählt. Die Zufälligkeit der Prioritätswert Bammel es ein wenig, so geht es 0, 1, 4, 11, 29 in diesem Lauf.

Ein zufälliger Lauf zeigt etwas Interessantes:

TimeIt(50, RandomInsert)
LastValue = 0,748661640914465, Top.Value = 0,748661640914465, Right.Count = 0, Left.Count = 0
LastValue = 0,669427539533669, Top.Value = 0,748661640914465, Right.Count = 0, Left.Count = 1
RotateRight @value=0,669427539533669
LastValue = 0,318363281115127, Top.Value = 0,748661640914465, Right.Count = 0, Left.Count = 2
RotateRight @value=0,669427539533669
LastValue = 0,33133987678743, Top.Value = 0,748661640914465, Right.Count = 0, Left.Count = 3
RotateLeft @value=0,748661640914465
LastValue = 0,955126694382693, Top.Value = 0,955126694382693, Right.Count = 0, Left.Count = 4
RotateRight @value=0,669427539533669
RotateLeft @value=0,33133987678743
RotateLeft @value=0,318363281115127
RotateRight @value=0,748661640914465
RotateRight @value=0,955126694382693
LastValue = 0,641024029180884, Top.Value = 0,641024029180884, Right.Count = 3, Left.Count = 2
LastValue = 0,20709771951991, Top.Value = 0,641024029180884, Right.Count = 3, Left.Count = 3
LastValue = 0,830862050331599, Top.Value = 0,641024029180884, Right.Count = 4, Left.Count = 3
RotateRight @value=0,20709771951991
RotateRight @value=0,318363281115127
LastValue = 0,203250563798123, Top.Value = 0,641024029180884, Right.Count = 4, Left.Count = 4
RotateLeft @value=0,669427539533669
RotateRight @value=0,748661640914465
RotateRight @value=0,955126694382693
LastValue = 0,701743399585478, Top.Value = 0,641024029180884, Right.Count = 5, Left.Count = 4
RotateLeft @value=0,669427539533669
RotateRight @value=0,701743399585478
RotateLeft @value=0,641024029180884
LastValue = 0,675667521858433, Top.Value = 0,675667521858433, Right.Count = 4, Left.Count = 6
RotateLeft @value=0,33133987678743
RotateLeft @value=0,318363281115127
RotateLeft @value=0,203250563798123
LastValue = 0,531275219531392, Top.Value = 0,675667521858433, Right.Count = 4, Left.Count = 7
RotateRight @value=0,748661640914465
RotateRight @value=0,955126694382693
RotateLeft @value=0,701743399585478
LastValue = 0,704049674190604, Top.Value = 0,675667521858433, Right.Count = 5, Left.Count = 7
RotateRight @value=0,203250563798123
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
LastValue = 0,161392807104342, Top.Value = 0,161392807104342, Right.Count = 13, Left.Count = 0
RotateRight @value=0,203250563798123
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
RotateLeft @value=0,161392807104342
LastValue = 0,167598206162266, Top.Value = 0,167598206162266, Right.Count = 13, Left.Count = 1
LastValue = 0,154996359793002, Top.Value = 0,167598206162266, Right.Count = 13, Left.Count = 2
RotateLeft @value=0,33133987678743
LastValue = 0,431767346538495, Top.Value = 0,167598206162266, Right.Count = 14, Left.Count = 2
RotateRight @value=0,203250563798123
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
RotateLeft @value=0,167598206162266
LastValue = 0,173774613614089, Top.Value = 0,173774613614089, Right.Count = 14, Left.Count = 3
RotateRight @value=0,830862050331599
LastValue = 0,76559642412029, Top.Value = 0,173774613614089, Right.Count = 15, Left.Count = 3
RotateRight @value=0,76559642412029
RotateLeft @value=0,748661640914465
RotateRight @value=0,955126694382693
RotateLeft @value=0,704049674190604
RotateLeft @value=0,675667521858433
LastValue = 0,75742144871383, Top.Value = 0,173774613614089, Right.Count = 16, Left.Count = 3
LastValue = 0,346844367844446, Top.Value = 0,173774613614089, Right.Count = 17, Left.Count = 3
RotateRight @value=0,830862050331599
LastValue = 0,787565814232251, Top.Value = 0,173774613614089, Right.Count = 18, Left.Count = 3
LastValue = 0,734950566540915, Top.Value = 0,173774613614089, Right.Count = 19, Left.Count = 3
RotateLeft @value=0,20709771951991
RotateRight @value=0,318363281115127
RotateLeft @value=0,203250563798123
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
RotateRight @value=0,75742144871383
RotateLeft @value=0,173774613614089
LastValue = 0,236504829598826, Top.Value = 0,236504829598826, Right.Count = 17, Left.Count = 6
RotateLeft @value=0,830862050331599
RotateLeft @value=0,787565814232251
RotateLeft @value=0,76559642412029
RotateRight @value=0,955126694382693
LastValue = 0,895606500048007, Top.Value = 0,236504829598826, Right.Count = 18, Left.Count = 6
LastValue = 0,599106418713511, Top.Value = 0,236504829598826, Right.Count = 19, Left.Count = 6
LastValue = 0,8182332901369, Top.Value = 0,236504829598826, Right.Count = 20, Left.Count = 6
RotateRight @value=0,734950566540915
LastValue = 0,704216948572647, Top.Value = 0,236504829598826, Right.Count = 21, Left.Count = 6
RotateLeft @value=0,346844367844446
RotateLeft @value=0,33133987678743
RotateRight @value=0,431767346538495
RotateLeft @value=0,318363281115127
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
RotateRight @value=0,75742144871383
LastValue = 0,379157059536854, Top.Value = 0,236504829598826, Right.Count = 22, Left.Count = 6
RotateLeft @value=0,431767346538495
LastValue = 0,46832062046431, Top.Value = 0,236504829598826, Right.Count = 23, Left.Count = 6
RotateRight @value=0,154996359793002
LastValue = 0,0999000217299443, Top.Value = 0,236504829598826, Right.Count = 23, Left.Count = 7
RotateLeft @value=0,20709771951991
LastValue = 0,229543754006524, Top.Value = 0,236504829598826, Right.Count = 23, Left.Count = 8
RotateRight @value=0,8182332901369
LastValue = 0,80358425984326, Top.Value = 0,236504829598826, Right.Count = 24, Left.Count = 8
RotateRight @value=0,318363281115127
LastValue = 0,259324726769386, Top.Value = 0,236504829598826, Right.Count = 25, Left.Count = 8
RotateRight @value=0,318363281115127
LastValue = 0,307835293145774, Top.Value = 0,236504829598826, Right.Count = 26, Left.Count = 8
RotateLeft @value=0,431767346538495
LastValue = 0,453910283024381, Top.Value = 0,236504829598826, Right.Count = 27, Left.Count = 8
RotateLeft @value=0,830862050331599
LastValue = 0,868997387527021, Top.Value = 0,236504829598826, Right.Count = 28, Left.Count = 8
RotateLeft @value=0,20709771951991
RotateRight @value=0,229543754006524
RotateLeft @value=0,203250563798123
LastValue = 0,218358597354199, Top.Value = 0,236504829598826, Right.Count = 28, Left.Count = 9
RotateRight @value=0,0999000217299443
RotateRight @value=0,161392807104342
LastValue = 0,0642934488431986, Top.Value = 0,236504829598826, Right.Count = 28, Left.Count = 10
RotateRight @value=0,154996359793002
RotateLeft @value=0,0999000217299443
LastValue = 0,148295871982489, Top.Value = 0,236504829598826, Right.Count = 28, Left.Count = 11
LastValue = 0,217621828065078, Top.Value = 0,236504829598826, Right.Count = 28, Left.Count = 12
RotateRight @value=0,599106418713511
LastValue = 0,553135806020878, Top.Value = 0,236504829598826, Right.Count = 29, Left.Count = 12
LastValue = 0,982277666210326, Top.Value = 0,236504829598826, Right.Count = 30, Left.Count = 12
RotateRight @value=0,8182332901369
LastValue = 0,803671114520948, Top.Value = 0,236504829598826, Right.Count = 31, Left.Count = 12
RotateRight @value=0,203250563798123
RotateRight @value=0,218358597354199
LastValue = 0,19310415405459, Top.Value = 0,236504829598826, Right.Count = 31, Left.Count = 13
LastValue = 0,0133136604043253, Top.Value = 0,236504829598826, Right.Count = 31, Left.Count = 14
RotateLeft @value=0,46832062046431
RotateRight @value=0,531275219531392
RotateRight @value=0,641024029180884
RotateRight @value=0,675667521858433
RotateRight @value=0,75742144871383
LastValue = 0,483394719419719, Top.Value = 0,236504829598826, Right.Count = 32, Left.Count = 14
RotateLeft @value=0,431767346538495
RotateRight @value=0,453910283024381
LastValue = 0,453370328738061, Top.Value = 0,236504829598826, Right.Count = 33, Left.Count = 14
LastValue = 0,762330518459124, Top.Value = 0,236504829598826, Right.Count = 34, Left.Count = 14
LastValue = 0,699010426969738, Top.Value = 0,236504829598826, Right.Count = 35, Left.Count = 14

Rotationen passiert nicht so sehr, weil der Baum unausgeglichen ist, sondern auch wegen der Prioritäten, die zufällig ausgewählt werden. Zum Beispiel haben wir 4 Drehungen am 13. Einsetzen bekommen. Wir haben einen Baum am 07.05 ausgeglichen (was in Ordnung ist), aber bekommen 13/0! Es scheint, dass die Verwendung von Zufall Prioritäten weitere Untersuchung verdient. Wie auch immer, es ist klar zu sehen, dass der Zufall Einsätze viel mehr Drehungen verursachen, als die bestellten Einsätze.

Ich habe die Berechnung der Standardabweichung und verändern den Test mit der höchsten Priorität ausgeführt (Rauschen so weit wie möglich zu reduzieren). Dies sind die Ergebnisse:

Random                                   Ordered
0,2835 (stddev 0,9946)                   0,0891 (stddev 0,2372)
0,1230 (stddev 0,0086)                   0,0780 (stddev 0,0031)
0,2498 (stddev 0,0662)                   0,1694 (stddev 0,0145)
0,5136 (stddev 0,0441)                   0,3550 (stddev 0,0658)
1,1704 (stddev 0,1072)                   0,6632 (stddev 0,0856)
1,4672 (stddev 0,1090)                   0,8343 (stddev 0,1047)
3,3330 (stddev 0,2041)                   1,9272 (stddev 0,3456)
7,9822 (stddev 0,3906)                   3,7871 (stddev 0,1459)
18,4300 (stddev 0,6112)                  10,3233 (stddev 2,0247)
44,9500 (stddev 2,2935)                  22,3870 (stddev 1,7157)
110,5275 (stddev 3,7129)                 49,4085 (stddev 2,9595)
275,4345 (stddev 10,7154)                107,8442 (stddev 8,6200)
667,7310 (stddev 20,0729)                242,9779 (stddev 14,4033)

Ich habe einen Sampling-Profiler läuft und hier sind die Ergebnisse (Höhe der Zeit das Programm in diesem Verfahren war):

Method           Random        Ordered
HeapifyRight()   1.95          5.33
get_IsEmpty()    3.16          5.49
Make()           3.28          4.92
Insert()         16.01         14.45
HeapifyLeft()    2.20          0.00

. Fazit: der Zufall hat eine ziemlich vernünftige Verteilung zwischen linker und rechten Rotation, während die nie bestellt dreht links

Hier ist meine verbesserte "Benchmark" Programm:

    static void Main(string[] args)
    {
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

        List<String> rndTimes = new List<String>();
        List<String> orderedTimes = new List<String>();

        rndTimes.Add(TimeIt(50, RandomInsert));
        rndTimes.Add(TimeIt(100, RandomInsert));
        rndTimes.Add(TimeIt(200, RandomInsert));
        rndTimes.Add(TimeIt(400, RandomInsert));
        rndTimes.Add(TimeIt(800, RandomInsert));
        rndTimes.Add(TimeIt(1000, RandomInsert));
        rndTimes.Add(TimeIt(2000, RandomInsert));
        rndTimes.Add(TimeIt(4000, RandomInsert));
        rndTimes.Add(TimeIt(8000, RandomInsert));
        rndTimes.Add(TimeIt(16000, RandomInsert));
        rndTimes.Add(TimeIt(32000, RandomInsert));
        rndTimes.Add(TimeIt(64000, RandomInsert));
        rndTimes.Add(TimeIt(128000, RandomInsert));
        orderedTimes.Add(TimeIt(50, OrderedInsert));
        orderedTimes.Add(TimeIt(100, OrderedInsert));
        orderedTimes.Add(TimeIt(200, OrderedInsert));
        orderedTimes.Add(TimeIt(400, OrderedInsert));
        orderedTimes.Add(TimeIt(800, OrderedInsert));
        orderedTimes.Add(TimeIt(1000, OrderedInsert));
        orderedTimes.Add(TimeIt(2000, OrderedInsert));
        orderedTimes.Add(TimeIt(4000, OrderedInsert));
        orderedTimes.Add(TimeIt(8000, OrderedInsert));
        orderedTimes.Add(TimeIt(16000, OrderedInsert));
        orderedTimes.Add(TimeIt(32000, OrderedInsert));
        orderedTimes.Add(TimeIt(64000, OrderedInsert));
        orderedTimes.Add(TimeIt(128000, OrderedInsert));
        var result = string.Join("\n", (from s in rndTimes
                        join s2 in orderedTimes
                            on rndTimes.IndexOf(s) equals orderedTimes.IndexOf(s2)
                        select String.Format("{0} \t\t {1}", s, s2)).ToArray());
        Console.WriteLine(result);
        Console.WriteLine("Done");
        Console.ReadLine();
    }

    static double StandardDeviation(List<double> doubleList)
    {
        double average = doubleList.Average();
        double sumOfDerivation = 0;
        foreach (double value in doubleList)
        {
            sumOfDerivation += (value) * (value);
        }
        double sumOfDerivationAverage = sumOfDerivation / doubleList.Count;
        return Math.Sqrt(sumOfDerivationAverage - (average * average));
    }
    static String TimeIt(int insertCount, Action<int> f)
    {
        Console.WriteLine("TimeIt({0}, {1})", insertCount, f.Method.Name);

        List<double> times = new List<double>();
        for (int i = 0; i < ITERATION_COUNT; i++)
        {
            Stopwatch sw = Stopwatch.StartNew();
            f(insertCount);
            sw.Stop();
            times.Add(sw.Elapsed.TotalMilliseconds);
        }

        return String.Format("{0:f4} (stddev {1:f4})", times.Average(), StandardDeviation(times));
    }

Ja, es ist die Anzahl der Umdrehungen, die die zusätzliche Zeit verursacht. Hier ist, was ich getan habe:

  • Nehmen Sie die Linien Priorität in HeapifyLeft Überprüfung und HeapifyRight so Drehungen immer getan werden.
  • ein Console.WriteLine nach dem, ob in RotateLeft und RotateRight hinzugefügt.
  • a Console.WriteLine im IsEmpty Teil der Insert Methode hinzugefügt, um zu sehen, was eingesetzt wurde.
  • Ran der Test einmal mit 5 Werte je.

Ausgabe:

TimeIt(5, RandomInsert)
Inserting 0.593302943554382
Inserting 0.348900582338171
RotateRight
Inserting 0.75496212381635
RotateLeft
RotateLeft
Inserting 0.438848891499848
RotateRight
RotateLeft
RotateRight
Inserting 0.357057290783644
RotateLeft
RotateRight

TimeIt(5, OrderedInsert)
Inserting 0.150707998383189
Inserting 1.58281302712057
RotateLeft
Inserting 2.23192588297274
RotateLeft
Inserting 3.30518679009061
RotateLeft
Inserting 4.32788012657682
RotateLeft

. Ergebnis: 2 mal so viele Drehungen auf Zufallsdaten

Sie sind nur einen Unterschied von etwa 2x sehen. Sofern Sie die daylights aus diesem Code abgestimmt haben, das ist im Grunde in dem Lärm. Die meisten Programme gut geschrieben, vor allem solche Datenstruktur beteiligt, kann leicht mehr Raum für Verbesserungen haben als das. Hier ist ein Beispiel.

Ich lief Code und nahm ein paar stackshots. Hier ist, was ich sah:

Random Insert:

1 Insert:64 -> HeapifyLeft:81 -> RotateRight:150
1 Insert:64 -> Make:43 ->Treap:35
1 Insert:68 -> Make:43

Bestellen einfügen:

1 Insert:61
1 OrderedInsert:224
1 Insert:68 -> Make:43
1 Insert:68 -> HeapifyRight:90 -> RotateLeft:107
1 Insert:68
1 Insert:68 -> Insert:55 -> IsEmpty.get:51

Dies ist eine ziemlich kleine Anzahl von Proben, aber es schlägt vor, im Fall von zufälliger Eingabe dass Fabrikat (Linie 43) einen höheren Anteil an Zeit verbraucht. Das ist dieser Code:

    private Treap<T> Make(Treap<T> left, T value, Treap<T> right, int priority)
    {
        return new Treap<T>(Comparer, left, value, right, priority);
    }

Ich habe dann 20 stackshots des Zufalls Insert Code eine bessere Vorstellung davon zu bekommen, was er tat:

1 Insert:61
4 Insert:64
3 Insert:68
2 Insert:68 -> Make:43
1 Insert:64 -> Make:43
1 Insert:68 -> Insert:57 -> Make:48 -> Make:43
2 Insert:68 -> Insert:55
1 Insert:64 -> Insert:55
1 Insert:64 -> HeapifyLeft:81 -> RotateRight:150
1 Insert:64 -> Make:43 -> Treap:35
1 Insert:68 -> HeapifyRight:90 -> RotateLeft:107 -> IsEmpty.get:51
1 Insert:68 -> HeapifyRight:88
1 Insert:61 -> AnonymousMethod:214

Dies zeigt einige Informationen.
43 oder seine Aufgerufenen
: 25% der Zeit in der Leitung Make ausgegeben. 15% der Zeit ist in dieser Zeile ausgegeben, nicht in einer anerkannten Routine, mit anderen Worten, in new einen neuen Knoten zu machen.
90% der Zeit wird in den Zeilen einfügen ausgegeben. 64 und 68 (der Anruf und heapify
10% der Zeit ist in rotateLeft und rechts ausgegeben.
15% der Zeit ist in Heapify oder seine Aufgerufenen verbracht.

Ich habe auch eine angemessene Menge von Einzelschritt (auf Quellebene), und kam zu dem Verdacht, dass, da der Baum unveränderlich ist, ist es viel Zeit damit verbringt, neue Knoten zu machen, weil es nicht zu ändern will alte. Dann werden die alten sind Müll gesammelt, weil niemand mehr auf sie Bezug nimmt.

Dies hat zu ineffizient.

Ich antworte immer noch nicht Ihre Frage, warum das Einfügen bestellte Zahlen schneller als zufällig generierten Zahlen, aber es hat mich nicht wirklich überrascht, weil der Baum unveränderlich ist.

Ich glaube nicht, dass Sie jede Leistung Argumentation über Baum-Algorithmen erwarten können leicht auf unveränderliche Bäume zu übertragen, weil die geringste Veränderung tief im Baum verursacht es auf dem Weg wieder aufgebaut wird wieder aus, zu einem hohen Preis in new -ing und Garbage collection.

@Guge ist richtig. Allerdings gibt es ein kleines bisschen mehr zu bieten. Ich sage nicht, dass es der größte Faktor ist in diesem Fall -. Aber es gibt es, und es ist schwer, etwas dagegen zu tun

Für einen sortierte Eingang, Lookups wahrscheinlich die Knoten berühren, die im Cache heiß sind. (Dies gilt im Allgemeinen für ausgeglichene Bäume wie AVL-Bäume, rot-schwarze Bäume, B-Bäume, usw.)

Da Einsätze mit einem Nachschlag zu starten, hat dies Auswirkungen auf Insert / als auch Löschleistung.

Auch ich behaupte nicht, dass es der wichtigste Faktor in jedem und allen Fällen. Es ist da, aber, und führt sehr wahrscheinlich in sortierten Eingaben immer schneller als zufällige diejenigen in diesen Datenstrukturen zu sein.

Aaronaught eine wirklich gute Arbeit geleistet hat erklärt diese.

Für diese beiden besonderen Fällen finde ich es leichter ist es im Hinblick auf die Einführung Weglängen zu erreichen.

Für zufällige Eingabe Ihrer Einsteckweges geht zu einem der Blätter nach unten und der Länge des Weges - also die Anzahl der Umdrehungen -. Werden durch die Höhe des Baumes begrenzt

In dem sortierten Fall Sie zu Fuß auf dem

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