2009-06-18 2 views
1

popping Ich habe mich gefragt, welche Implementierung würde eine bessere Leistung haben:Performance: die schneller Rückwärts auf einem Stapel Aufruf oder Elemente in einem Array

Ich brauche alle Elemente aus einem Stapel löschen mit Ausnahme der ersten 10 an der Spitze .

Diese 10 müssen dann in ihrer ursprünglichen Reihenfolge in den Stapel gelegt werden.

Ich dachte, von 2 nähert sich

die erste:

FilterRecord[] records = new FilterRecord[10]; 

     for (int i = 0; i < records.Length; i++) 
     { 
      records[i] = _InternalCollection.Pop(); 
     } 

     _InternalCollection.Clear(); 

     for (int i = records.Length - 1; i >= 0; i--) 
     { 
      _InternalCollection.Push(records[i]); 
     } 

Die zweite:

int count = _InternalCollection.Count - 10; 
       _InternalCollection.Reverse(); 


       for (int i = 0; i < count; i++) 
       { 
        _InternalCollection.Pop(); 
       } 

       _InternalCollection.Reverse(); 

Jede Hilfe oder Richtlinien oder andere impemenations wäre willkommen.

+0

Klingt wie eine Gelegenheit für Sie, ein Benchmarking zu machen. Für Ihr gegebenes Beispiel ist der Unterschied wahrscheinlich in der realen Welt sowieso vernachlässigbar. –

+0

Ja, ich war faul, dachte, ich könnte sofort eine Antwort bekommen, vielleicht durch irgendjemanden, der etwas offensichtlich offensichtlich falsch in beiden Ansatz aufzeigt. – AndyMM

+0

Die zweite Methode funktioniert nicht wie vorgesehen. Die Klasse Stack hat keine Reverse-Methode. Es ist eine Erweiterungsmethode für IEnumerable , so dass es den Stack überhaupt nicht beeinflussen würde. Ein Leistungstest würde zeigen, dass es sehr schnell ist, aber das liegt daran, dass die Aufrufe von Reverse keine wirkliche Arbeit machen. – Guffa

Antwort

0

Ich denke, dass von dem Stapel knallen Gegenstände zu vermeiden wird Ihr Code die Leistung mehr als die Alternative zwischen den beiden verbessern Ansätze, die Sie vorschlagen. Ich würde empfehlen, auf dem Stapel einen neuen Stapels mit dem Inhalt des letzten Elements Zuweisung Linq mit etwa so:

var newStack = new Stack<FilterRecord>( 
    _InternalCollection.Take(10) 
         .Reverse()); 

_InternalCollection = newStack; 

Wenn Sie nicht der Verwendung von Generika, um Sie mit dem eingebauten Iterator das gleiche tun:

var iterator = _InternalCollection.GetEnumerator(); 
int i = 9; 
while(iterator.MoveNext() && i-- >= 0) 
{ 
    newStack.Push(iterator.Current); 
} 
_InternalCollection = newStack; 
+0

Siehe Kommentar zu Guffas Vorschlag – AndyMM

+0

Ein paar Gedanken. Vermeiden Sie es, die statische Sammlung auf diese Weise zu teilen - verwenden Sie eine Eigenschaft, um den Zugriff zu verbergen und zuzulassen, dass Objektinstanzen nach Bedarf ersetzt werden. Zweitens, da Sie bereits ein solches Szenario haben, vermeiden Sie es, Pop() auf dem Stapel überhaupt aufzurufen. Verwenden Sie einen Enumerator, kopieren Sie die Werte in ein temporäres Array. Verwende Clear(), um den Stapel zu löschen (das ist ziemlich schnell). Und dann drücken() die Werte zurück in den Stapel in umgekehrter Reihenfolge. Dies ist wahrscheinlich der schnellste Ansatz. – LBushkin

+0

Dank LBushkin Ich verstehe, was Sie über den Einsatz von statischen Klassen meinen, diskutierte ich dies in meinem Kopf für einige Zeit. Am Ende musste ich sicherstellen, dass ich genau ein Objekt in Erinnerung hatte, das war der Fator, der die Entscheidung getroffen hat. – AndyMM

0

Das hängt davon ab, dass Sie davon ausgehen, dass die Reverse() -Methode nicht genau das tut. Nur zwei Möglichkeiten zu wissen: 1.) Führen Sie Ihren Code und sagen Sie uns 2.) Nachschlagen der Umsetzung von Reverse().

2

Ihr erster Algorithmus hat zwei For-Schleifen, also O (2N). Ihr zweiter Algorithmus hat eine for-Schleife, aber wir müssen annehmen, dass intern Reverse() eine O (N) -Operation ist, also sein O (3N).

Ich bin nicht sicher, ob der Aufruf Clear() in Ihrem ersten Algorithmus O (N) oder O (C) ist.

+0

Die for-Schleifen im ersten Algorithmus sind O (1), daher ist nur der Aufruf von Clear für die große O-Notation relevant, die eine O (n) -Operation ist. Der zweite Algorithmus ist O (n-10), aber für jede große Zahl wäre die 10 vernachlässigbar, also sind beide Alorithmen O (n). – Guffa

+0

Wie ist eine for-Schleife, die über jedes Element in der Sammlung eine O (1) -Operation geht? –

0

Es ist oft schwer zu sagen auf diese Art von Dingen. Mach einfach einen wahnsinnig großen Stack, ruf beide an und Zeit sie. Dann wirst du es wissen.

2

Kommt drauf an. Es gibt zwei Dinge, die Sie tun können:

  • Test durch sie es für ein paar hundert Iterationen ausgeführt wird (weniger oder mehr je nach Größe)
  • Blick auf ihre Implementierungen

die Methode analysieren in dem ein Stapel (konzeptionell) implementiert wird, mit umgekehrten wäre die langsamste, da es alles des Stapels pop und dann zurück in, in umgekehrter Reihenfolge. Wenn es intern ausgewählt wird, wählt es einfach einen anderen Startindex für den Beginn des Popups aus, es könnte schneller sein.

In beiden Fällen, einfach gesagt, mit Reverse() scheint ineffizient, wie Sie diese Operation zweimal durchführen.

0

Ich würde vorschlagen, werfen Sie einen Blick auf die tatsächliche Umsetzung von Reverse() - Sie können die .Net Reflector nützlich dafür finden.

1

Wenn sich nur wenige Elemente im Stapel befinden, ist der Unterschied so gering, dass eine Optimierung nicht sinnvoll ist.

Wenn es viele Elemente in dem Stapel sind, wäre dies der schnellste sein:

FilterRecord[] records = new FilterRecord[10]; 

for (int i = 0; i < records.Length; i++) { 
    records[i] = _InternalCollection.Pop(); 
} 

_InternalCollection = new Stack<FilterRecord>(); 

for (int i = records.Length - 1; i >= 0; i--) { 
    _InternalCollection.Push(records[i]); 
} 

D.h. Werfen Sie einfach den aktuellen Stapel weg, nachdem Sie die gewünschten Artikel erhalten haben, und lassen Sie den Müllsammler sich darum kümmern. Wenn Sie die Clear-Methode verwenden, werden die Referenzen aus dem Stapel entfernt, aber die Kapazität bleibt erhalten.

Da Sie nur zehn der Elemente im Stapel berühren, unabhängig davon, wie viele davon vorhanden sind, handelt es sich um eine O (1) -Operation anstelle einer O (n) -Operation.

(Beachten Sie, dass Sie die Größe des Arrays verwenden, wenn Sie es erklären, nicht die hoechste Index.)

+0

Dies kann noch schneller sein, wenn Sie vermeiden, die Elemente aus dem ursprünglichen Stapel zu puffen und verwenden Sie nur einen Enumerator, um jedes Element zu besuchen. – LBushkin

+0

Schau nochmal ... Es sind nicht alle Gegenstände, nur zehn Stück. – Guffa

+0

das Problem ist, dass die _InternalCollection statisch ist, wird es eine Anzahl von Objekt mit einem Verweis auf diese Sammlung – AndyMM

0

Wie wäre es, wenn Sie keinen Stapel verwenden würden? Wie wäre es mit einer LinkedList und dann mit der RemoveLast-Methode, um das zu tun, was Sie brauchen?

Einige grobe Code, braucht mehr Arbeit, aber Sie bekommen die Grundidee:

public class CustomStack<T> 
    { 
     private LinkedList<T> list; 

     public CustomStack() 
     { 
      list = new LinkedList<T>(); 
     } 

     public void Push(T item) 
     { 
      list.AddFirst(item); 
     } 

     public T Pop() 
     { 

      var result = list.First.Value; 
      list.RemoveFirst(); 
      return result; 
     } 

     public void TrimLast(int amountToLeave) 
     { 
      while (list.Count > amountToLeave) 
      { 
       list.RemoveLast(); 
      } 
     } 
    } 
0

Ich denke, dass Sie ziemlich viel Zählung auf der Double-Reverse kann langsamer sein. Ihre Laufzeit für 1) ist konstant (O (1)) in Bezug auf _InternalCollection.Length (unter der Annahme, Clear ist konstante Zeit) und O (n) in Bezug auf die Anzahl der Elemente, die Sie behalten möchten. 2) wird langsamer, linear desto größer _InternalCollection ist (O (n))

Sie könnten auch in Betracht ziehen, Ihre _InternalCollection in eine Deque (Double ended queue) zu ändern. Ein Deque unterstützt O (1) amortisierte Zeit, um Elemente von jedem Ende hinzuzufügen/zu entfernen. Natürlich (AFAIK) gibt es keine Standard-Implementierung, also müssten Sie Ihre eigene oder rollen.

+0

Danke ich werde mir die Implementierung anschauen – AndyMM

0

memcpy die letzten 10 Elemente auf dem Stapel, wo Sie sie haben möchten.

Dann deklarieren Sie den Stapel zu 10 Stück lang.

Verwandte Themen