Natural compare String zu Sortieralgorithmus???

Für allgemeine Fragen zur Programmierung mit PureBasic.
SMaag
Beiträge: 184
Registriert: 08.05.2022 12:58

Natural compare String zu Sortieralgorithmus???

Beitrag von SMaag »

Da in Purebasic zwar Sortieralgorithmen eingebaut sind, es aber keinen Natural Sort gibt, hab ich jetzt mal die Functionen für
Natural Compare String nach PureBasic convertiert. Das scheint soweit zu funktionieren. Jetzt muss man das aber in Sortieralgorithmen wie Quicksort, Bubbelsort ... einbauen. Leider hab ich das bisher nur angewendet, mich damit aber noch nicht wirklich beschäftigt.

1. Welchen Sortieralgorithmus für Listen, Arrays
a) für wenige Einträge: <10.0000
b) für viele Einträge >10.000 - 1.000.000

2. hat da jemand gute Implementierungen bereit, in die man den Naturalsort einbauen kann?

Code für NatrualCompareString

Code: Alles auswählen

;   #PB_String_Equal   ;  0  wenn String1 gleich String2 ist
;   #PB_String_Lower   ; -1  wenn String1 kleiner als String2 ist
;   #PB_String_Greater ;  1  wenn String1 größer als String2 ist

; based on C Code from
; https://github.com/sourcefrog/natsort/blob/master/strnatcmp.c

Structure pChar
  a.a[0]
  c.c[0]
EndStructure

Macro mac_IsDigit(C)
  Bool(C >= '0' And C <= '9')   
EndMacro

Macro mac_LCaseChar(Char, ReturnChar)
  #DeltaChar= 'a' - 'A'  ;  a[97]-A[65]=32
  If char >='A' And char <='Z'
    ReturnChar = Char + #DeltaChar   ; add 32 to LCase Char
  Else
    ReturnChar = Char
  EndIf
EndMacro

Procedure _NaturalCompare_Right(*a.pChar, *b.pChar)
; ============================================================================
; NAME: _NaturalCompare_Right
; DESC: 
; DESC: 
; VAR(*a.pChar) : Pointer to 1st String (interpreted as pointerChar)
; VAR(*a.pChar) : Pointer to 2nd String (interpreted as pointerChar)
; RET.i: #PB_String_Lower, #PB_String_Equal, #PB_String_Greater
; ============================================================================
  Protected I, bias  
  
  ; The longest run of digits wins.  That aside, the greatest
  ; value wins, but we can't know that it will until we've scanned
  ; both numbers To know that they have the same magnitude, so we
  ; remember it in BIAS.
  
  While *a\c[I] And *b\c
    
    If Not mac_IsDigit(*a\c[I]) And Not mac_IsDigit(*b\c[0])
      ProcedureReturn bias
      
    ElseIf Not mac_IsDigit(*a\c[I])
      ProcedureReturn #PB_String_Lower
      
    ElseIf Not mac_IsDigit(*b\c[0])
      ProcedureReturn #PB_String_Greater
      
    ElseIf *a\c[I] < *b\c[0]
      If Not bias
        bias = #PB_String_Lower
      EndIf
      
    ElseIf *a\c[I]  > *b\c[0]
      If Not bias
        bias = #PB_String_Greater
      EndIf
      
    ElseIf Not *a\c[I] And Not *b\c[0]
      ProcedureReturn bias
    EndIf
    I + 1  
  Wend
  
  ProcedureReturn #PB_String_Equal
EndProcedure

Procedure _NaturalCompare_Left(*a.pChar, *b.pChar)
; ============================================================================
; NAME: _NaturalCompare_Left
; DESC: 
; DESC: 
; VAR(*a.pChar) : Pointer to 1st String (interpreted as pointerChar)
; VAR(*a.pChar) : Pointer to 2nd String (interpreted as pointerChar)
; RET.i: #PB_String_Lower, #PB_String_Equal, #PB_String_Greater
; ============================================================================
  Protected I  
  
  ; Compare two left-aligned numbers: the first To have a
  ; different value wins.
  
  While *a\c[I] And *b\c[I]
    If Not mac_IsDigit(*a\c[I]) And Not mac_IsDigit(*b\c[0])
      ProcedureReturn #PB_String_Equal
      
    ElseIf Not mac_IsDigit(*a\c[I])
      ProcedureReturn #PB_String_Lower
      
    ElseIf Not mac_IsDigit(*b\c[I])
      ProcedureReturn #PB_String_Greater
      
    ElseIf *a\c[I]  < *b\c[I]
      ProcedureReturn #PB_String_Lower
      
    ElseIf *a\c[I]  > *b\c[I]
      ProcedureReturn #PB_String_Greater
    EndIf
    
    I + 1  
  Wend
  
  ProcedureReturn #PB_String_Equal
EndProcedure

Procedure NaturalCompareString(*a.pChar, *b.pChar, Mode=#PB_String_CaseSensitive )
; ============================================================================
; NAME: NaturalCompareString
; DESC: 
; DESC: 
; VAR(*a.pChar) : Pointer to 1st String (interpreted as pointerChar)
; VAR(*a.pChar) : Pointer to 2nd String (interpreted as pointerChar)
; RET.i: #PB_String_Lower, #PB_String_Equal, #PB_String_Greater
; ============================================================================
  Protected.i aI, bI, result
  Protected.c ca, cb  
 
  While *a\c[aI] And *b\c[bI]
    
    ; Skip Tabs and Spaces
    While (*a\c[aI] = 32) Or (*a\c[aI] = 9)
      aI + 1
    Wend
    
    ; Skip Tabs And Spaces
    While (*b\c[bI] = 32) Or (*b\c[bI] = 9)
      bI + 1
    Wend
    
    ca = *a\c[aI] 
    cb = *b\c[bI]
    
    If (Not ca) And (Not cb) ; C++ If (!ca && !cb) {
	    ProcedureReturn #PB_String_Equal    
	  EndIf
   
 	  ; process compare digits
	  If mac_IsDigit(ca)  And  mac_IsDigit(cb)
	    
	    If ca ='0' Or cb = '0'  ; if  (ca == '0' || cb == '0'); one of both is a '0'
	      
	      result = _NaturalCompare_Left((*a+aI), (*b+bI))	      
	      If result 
	        ProcedureReturn result  
	      EndIf
	      
	    Else
	      
	      result = _NaturalCompare_Right((*a+aI), (*b+bI))
	      If result
	        ProcedureReturn result  
	      EndIf
	      
	    EndIf
	  EndIf 
	  
	  If Mode <> #PB_String_CaseSensitive
      mac_LCaseChar(ca,ca)
      mac_LCaseChar(cb,cb)
	  EndIf
  
	  If ca < cb
	    ProcedureReturn #PB_String_Lower
	  ElseIf ca > cb
	    ProcedureReturn #PB_String_Greater
	  EndIf
	  
	  aI + 1
	  bI + 1
	Wend
	
	ProcedureReturn #PB_String_Equal
EndProcedure


Define.s str1, str2

str1 = "foo100bar99baz0.txt"
str2 = "foo100bar10baz0.txt"

;   #PB_String_Equal   ;  0  wenn String1 gleich String2 ist
;   #PB_String_Lower   ; -1  wenn String1 kleiner als String2 ist
;   #PB_String_Greater ;  1  wenn String1 größer als String2 ist

Debug NaturalCompareString(@str1, @str2)
Debug NaturalCompareString(@str2, @str1)
Benutzeravatar
hjbremer
Beiträge: 822
Registriert: 27.02.2006 22:30
Computerausstattung: von gestern
Wohnort: Neumünster

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von hjbremer »

für Windows und ListIcongadget gibt es LVM_SORTITEMSEX

diese Api Message arbeitet mit einem Callback in dem man für jeden Vergleich
eigene Routinen bereitstellen kann

Hier und im englischem Forum gibt es Beispiele wenn man nach LVM_SORTITEMS bzw. besser LVM_SORTITEMSEX sucht

siehe auch https://learn.microsoft.com/de-de/windo ... ortitemsex
Purebasic 5.70 x86 5.72 X 64 - Windows 10

Der Computer hat dem menschlichen Gehirn gegenüber nur einen Vorteil: Er wird benutzt
grüße hjbremer
SMaag
Beiträge: 184
Registriert: 08.05.2022 12:58

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von SMaag »

Das läuft alles auf QuickSort hinaus.
Quicksort ist immer die beste Wahl, solange die Daten in den Speicher passen. Nur bei ungünstig vorsortierten Listen,
wäre es besser, den Qiucksort mit einem InsertSort zu verbinden. Nen Quicksort-Code für Array und Zahlen hab ich bereits gefunden.
Damit kann ich mir das dann selbst entsprechend bauen.
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von Nino »

SMaag hat geschrieben: 02.09.2023 14:39 1. Welchen Sortieralgorithmus für Listen, Arrays
a) für wenige Einträge: <10.0000
b) für viele Einträge >10.000 - 1.000.000

2. hat da jemand gute Implementierungen bereit, in die man den Naturalsort einbauen kann?
Am flexibelsten ist es, wenn die Vergleichsfunktion überhaupt nicht in den Sortieralgorithmus „eingebaut“ ist. D.h. wenn der Sortieralgorithmus so geschrieben ist, dass er eine benutzerdefinierte Vergleichsfunktion aufruft. Dann kann man auch eine Vergleichsfunktion benutzen, die Natural Sort bewirkt, s. z.B. https://www.purebasic.fr/english/viewto ... 05#p528605
SMaag hat geschrieben: 05.09.2023 15:18 Quicksort ist immer die beste Wahl, solange die Daten in den Speicher passen.
Das würde ich nicht so allgemein sagen, v.a. weil QuickSort nicht stabil sortiert.
SMaag
Beiträge: 184
Registriert: 08.05.2022 12:58

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von SMaag »

Danke! das müsste genau das sein, was mit noch fehlte!
SMaag hat geschrieben: 05.09.2023 15:18 Quicksort ist immer die beste Wahl, solange die Daten in den Speicher passen.
Das würde ich nicht so allgemein sagen, v.a. weil QuickSort nicht stabil sortiert.
Nicht stabil sortieren ist aber relativ unerheblich, da es sich dabei nur im identische Elemente handelt, die dann in anderer Reihenfolge auftauchen können. In dem Wikipedia Artikel ist das auch etwas verzerrt dargestellt. Dort wird nur nach den vorgestellten Zahlen sortiert und das stimmt. Die nachgestellten Texte (auf die nicht sortiert wurde) können bei verschiedenen Sortierläufen dann in unterschiedlichen Reihenfolgen auftauchen.

Ansonsten scheint, nach allem was ich bisher gelesen habe, der Quicksort wirklich immer die Nase vorn zu haben, solange die Daten in den Speicher passen. Wenn das nicht der Fall ist, dann MergeSort! Beide, QucikSort und MergeSort kombiniert mit einem InsertSort für vorsortierte Listen steigert die Effektivität! Ich hab vielleicht 10.000 Einträge und im worts case evtl. 100.000 und bleib im MByte-Bereich! Damit sollte ich mit QickSort gut aufgehoben sein!
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von Nino »

SMaag hat geschrieben: 19.09.2023 10:21 Nicht stabil sortieren ist aber relativ unerheblich,
Für dich vielleicht, für mich eben nicht!
Es kommt halt immer auf den Einsatzzweck an, deshalb halte ich nichts von stark vereinfachten Aussagen.
Eine „Vielzweck-Sortierroutine für den Alltag“ muss für mich stabil sortieren. Dabei ist es mir egal, ob eine andere Routine 100 ms o.Ä. schneller ist. Damit kann ich mich beschäftigen, wenn ich eine Riesenmenge an Datensätzen sortieren möchte (was wahrscheinlich nie passieren wird).
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von DarkDragon »

Merge Sort braucht mehr Speicher als Quick Sort. Aber jeder Sortieralgorithmus kann stabil gemacht werden ohne die asymptotische worst case Laufzeit zu verändern, indem man jedem Element noch eine Index-Nummer anhängt, was in O(n) funktioniert.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
SMaag
Beiträge: 184
Registriert: 08.05.2022 12:58

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von SMaag »

Evtl. hab ich das mit dem stabil sortieren falsch verstanden.

Hier die Version wie ich es verstanden hab.

Vorname / Name
Peter Meier
Peter Mayer

Wenn ich nur nach Vorname sortiere, dann kann je nach Sortierlauf mal
Meier oder Mayer oben stehen.
Das ist für mich korrekt, da ich ja nur nach Vorname sortiert habe und mich Name in der Sortierung nicht interessiert.

Bei einer stabilen Sortierung würde immer der gleich oben stehen.
Also Meier oder Mayer, ohne das sich das bei einem weiteren Sortierlauf ändert.
Solange ich aber nicht nach Nachname sortiere ist bei einem stabilen Algorithmus aber auch nicht klar welcher oben steht
das kann auch Meier sein, was alphabetisch nicht korrekt ist, bleibt dann aber immer so!

Grundsätzlich muss ich, wenn ich das korrekt alphabetisch sortiert haben möchte, Name mit in die Sortierung aufnehmen.

Stimmt das so oder liege ich da falsch?
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Re: Natural compare String zu Sortieralgorithmus???

Beitrag von DarkDragon »

SMaag hat geschrieben: 22.09.2023 08:47 Evtl. hab ich das mit dem stabil sortieren falsch verstanden.

Hier die Version wie ich es verstanden hab.

Vorname / Name
Peter Meier
Peter Mayer

Wenn ich nur nach Vorname sortiere, dann kann je nach Sortierlauf mal
Meier oder Mayer oben stehen.
Das ist für mich korrekt, da ich ja nur nach Vorname sortiert habe und mich Name in der Sortierung nicht interessiert.

Bei einer stabilen Sortierung würde immer der gleich oben stehen.
Also Meier oder Mayer, ohne das sich das bei einem weiteren Sortierlauf ändert.
Korrekt, bei gleichen Sortierwerten bleibt die Ursprungsreihenfolge erhalten.
SMaag hat geschrieben: 22.09.2023 08:47Grundsätzlich muss ich, wenn ich das korrekt alphabetisch sortiert haben möchte, Name mit in die Sortierung aufnehmen.
Kennst du noch Excel oder alte Programme wo man Tabellen sieht die man über den Spaltenheader sortieren kann? Angenommen du willst nach Spalte Nachname sortieren und bei gleichen Nachnamen willst du nach Vornamen sortieren, dann würdest du zuerst nach Vornamen sortieren und dann nach Nachnamen, also zweimal mit einem stabilen Sortieralgorithmus drüber gehen, weil die Reihenfolge des ersten Sortierdurchlaufs erhalten bleibt für alle gleichen Werte im zweiten Sortierdurchlauf.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Antworten