Rubrik: Verschiedenes / Sonstiges | VB-Versionen: VB2005, VB2008 | 12.02.09 |
DoubleComparer Ein Comparer für Array.Copy, der Double.Nan-Werte korrekt sortiert | ||
Autor: Manfred Bohn | Bewertung: | Views: 8.907 |
ohne Homepage | System: Win2k, WinXP, Win7, Win8, Win10, Win11 | kein Beispielprojekt |
Beim Sortieren der Daten in einem eindimensionalen Double-Array durch die Methode 'Array.Copy' tritt bei Verwendung des Standard-Comparers bekanntlich das Problem auf, dass Double.Nan-Werte nicht an den Anfang oder das Ende des Array gerückt werden, sondern nach dem Sortieren 'irgendwo' zwischen den Daten stehen. (Das liegt vermutlich daran, dass die Prüfung, ob ein Double-Wert 'Nan' enthält, sehr rechenzeitaufwendig.)
Allerdings: Der Sortiervorgang kann bei Verwendung des Standard-Comparers durcheinandergeraten.
Hier ein Comparer, der beim Sortieren ggf. die NaN-Werte an das Ende des Arays rückt. (Beim Vergleich eines gültigen Doublewertes mit einem NaN-Wert wird der Nan-Wert vom 'DoubleComparer' stets als der relativ größere Wert angegeben.)
Der Rechenzeitbedarf für das Sortieren großer Arrays liegt bei Anwendung des 'DoubleComparers' etwa 3x höher - im Vergleich zum Bedarf bei Verwendung des StandardComparers - und zwar unabhängig davon, ob und wie viele NaN.Werte im Array vorliegen.
Grundsätzlich ist bei der Programmierung eines 'Comparers' für Sortierfunktionen zu beachten, dass bei sukzessiver Anwendung keine widersprüchlichen Vergleichs-Ergebnisse auftreten dürfen (also etwa: a > b, b > c, c > a).
Als (langsame und etwas umständliche) Alternative bietet sich die OrderBy-Methode der Enumerable-Klasse an (Namespace: System.Linq.Enumerable). Dieses Verfahren sortiert NaN-Werte an den Anfang des Array, falls man als Parameter die sogenannte 'Identitätsfunktion' verwendet. Auch hier ist es möglich, eine selbsterstellte Comparer-Klasse für den Vergleich beim Sortieren anzugeben./p>
Aufrufbeispiele:
Randomize(1000.0#) ' Array erstellen Dim n As Integer = 25 Dim arr(n) As Double ' Array mit Zufallswerten füllen Fill(arr) ' einige Sonderwerte zuweisen arr(5) = Double.PositiveInfinity arr(8) = Double.MaxValue arr(10) = Double.NaN arr(20) = Double.NegativeInfinity arr(22) = Double.NaN arr(25) = Double.MinValue Array.Sort(arr, New DoubleComparer) arr = arr.AsEnumerable.OrderBy _ (Of Double)(Function(arg) arg).ToArray() arr = arr.AsEnumerable.OrderBy _ (Of Double)(Function(arg) arg, New DoubleComparer).ToArray()
Und hier die Comparer-Klasse:
Public Class DoubleComparer Implements IComparer(Of Double) Public Function Compare(ByVal x As Double, _ ByVal y As Double) As Integer _ Implements IComparer(Of Double).Compare ' NaN-Wert? Dim is_x_undef As Boolean = Double.IsNaN(x) Dim is_y_undef As Boolean = Double.IsNaN(y) ' Vergleich durchführen If is_x_undef And is_y_undef Then Return 0 ElseIf is_x_undef Then Return 1 ElseIf is_y_undef Then Return -1 ElseIf x = y Then Return 0 ElseIf x > y Then Return 1 Else Return -1 End If End Function End Class
Zwei Hilfsfunktionen:
Public Sub Fill(Of T)(ByRef D1arr As T()) For i As Integer = 0 To D1arr.GetUpperBound(0) D1arr(i) = CType(Convert.ChangeType(RandomNumber, GetType(T)), T) Next i End Sub
Public Function RandomNumber() As Double Dim r As Double = Microsoft.VisualBasic.Rnd If Microsoft.VisualBasic.Rnd < 0.5 Then r *= -1 Return r End Function