sortieren Arrays über Pointer

Anfängerfragen zum Programmieren mit PureBasic.
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

sortieren Arrays über Pointer

Beitrag von PeterJ »

Ich habe ein kleines Verständnisproblem mit Arrays und deren Verwendung mittels Pointern.
Als Ausgangslage habe ich mehrere (viele) Arrays, die wie folgt definiert sind:

Code: Alles auswählen

Global Dim ArrayString00.s(1)
Global  Dim ArrayString01.s(1)
...
diese Arrays werden dynamische verwaltet und ggf. mit REDIM erweitert/verkürzt. Im Programm spreche ich sie mit Pointern und einer Struktur an:

Code: Alles auswählen

Structure StringArray 
  text.s[0] 
EndStructure 
Verwendet werden sie z.B. wie folgt Einlesen Datei in Array:

Code: Alles auswählen

*toArray.StringArray=ArrayDef(toArray)\ArrayAddress 
While Eof(0)=0   ; Read until EOF
      i+1 
     *toArray\text[i]=ReadString(0)  
Wend
Das funktioniert alles ganz perfekt. Nun will ich noch eine SORT Funktion einbauen. und habe keine Idee wie ich den Sortaufruf machen soll:
Syntaktisch muß es ja so sein:

Code: Alles auswählen

SortArray(ArrayString00(), Optionen [, Start, Ende]) 
Wie mache ich das aber wenn ich nur die Adresse kenne und nur über Pointer und Struktur zugreifen kann?
sowas wie:

Code: Alles auswählen

SortArray( *toArray\text[], Optionen [, Start, Ende]) 
geht leider nicht. Wie mache ich es richtig?
... und weil wir dabei sind, kann mir jemand zeigen wo die Dokumentation über die Array Adressierung mit (in *pointer\variable) steht. Ich verwende sie, weil ich sie mir jemand in einem früheren Posting gezeigt hat. Der eigentliche Unterschied zur "(i)" Notation ist mir aber nicht klar.

Peter
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

wenn du ReDim benutzt um sie zu verlängern/verkürzen, warum benutzt du dann nicht auch den klassischen Aufruf im Sort?

... mir ist sowieso nicht einleuchtend, warum du pointer für deinen zugriff benutzt...

wegen der unterschiedlichen namen?

warum benutzt du nicht ArrayString.s(0,1), ArrayString.s(1,1) statt ArrayString00.s(1), ArrayString01.s(1) ?
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag von cxAlex »

Wenn du es unbedingt mit Pointern machen willst musst du dir selbst einen Sortieralgorythmus basteln. Ich hab hier mal nen angepassten Counting Sort für dich.

Ist besonders schnell wen der Bereich der zu sortierenden Zahlen klein ist:

Du musst ihn nur noch so anpassen das er nach Characters sortiert.

Code: Alles auswählen

Structure IntArray
  i.i[0]
EndStructure

Procedure CountingSort(*Array.IntArray, ArraySize)
  Protected *sortedArray.IntArray
  Protected i, j, fillheight, cnt, MaxSize, MinSize
  MaxSize = *Array\i[0]
  MinSize = MaxSize
  For i = 1 To ArraySize-1
    If *Array\i[i]>MaxSize
      MaxSize = *Array\i[i]
    EndIf
    If *Array\i[i]<MinSize
      MinSize = *Array\i[i]
    EndIf
  Next
  *sortedArray = AllocateMemory(((MaxSize-MinSize) + 1)*SizeOf(Integer))
  For i = 0 To ArraySize-1
    *sortedArray\i[*Array\i[i]-MinSize] + 1
  Next
  For i = MinSize To MaxSize
    cnt = *sortedArray\i[i-MinSize]-1
    For j = 0 To cnt
      *Array\i[fillheight] = i
      fillheight + 1
    Next
  Next
  FreeMemory(*sortedArray)
EndProcedure

#AnzEintrys = 20000000
#Min = 100
#Max = 355

Define *tArray.IntArray = AllocateMemory(#AnzEintrys*SizeOf(Integer))

For i = 0 To #AnzEintrys-1
  *tArray\i[i] = Random(#Max-#Min) + #Min
  ;Debug *tArray\i[i]
Next

t = ElapsedMilliseconds()
CountingSort(*tArray, #AnzEintrys)
t1 = ElapsedMilliseconds()-t
MessageRequester("", Str(t1) + " ms")

For i = 0 To #AnzEintrys-1
  Debug *tArray\i[i]
Next
Gruß, Alex
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

Beitrag von PeterJ »

Kaeru Gaman hat geschrieben:wenn du ReDim benutzt um sie zu verlängern/verkürzen, warum benutzt du dann nicht auch den klassischen Aufruf im Sort?
... mir ist sowieso nicht einleuchtend, warum du pointer für deinen zugriff benutzt...
wegen der unterschiedlichen namen?
warum benutzt du nicht ArrayString.s(0,1), ArrayString.s(1,1) statt ArrayString00.s(1), ArrayString01.s(1) ?
Ist etwas kompliziert, zu erklären: Ich steuere von Außen die Operationen die mit dem Array ausgeführt werden sollen. Als Beispiel ich habe Float Arrays, mit denen ich Matrix Operationen ausführe. Ich sage also:
1. ReadFloat(array-nummer-1,File1)
2. ReadFloat(array-nummer-2,File2)
3. Multiply(array-nummer-1,array-nummer-2)
(Die 2. Dimension der Matrix simuliere ich)

Das heißt die Verarbeitungsanweisung wird über eine Eingabedatei angestoßen. Das bedeutet konkret, ich kenne nur die relative array-nummer und ordne intern die Array Addresse zu.

Für String- und Numeric Arrays verfahre ich entsprechend.

Die 2. Dimension geht deshalb nicht weil die Arrays unterschiedlich lang sind und ich dann nicht die Möglichkeit habe mit REDIM individuell zu dimensionieren.

@cxAlex: Danke, das wird dann wohl der Weg sein, den ich beschreiten muß.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag von cxAlex »

Ich hab den Algo nochmal schnell verbessert, wie man den noch schneller machen könnte seh ich im Moment nicht:

Code: Alles auswählen

Structure IntArray
  i.i[0]
EndStructure

Procedure CountingSort(*Array.IntArray, ArraySize, MaxSize = -1)
  Protected *sortedArray.IntArray
  Protected i, j, fillheight, cnt
  ArraySize-1
  If MaxSize = -1
    MaxSize = *Array\i[0]
    For i = 1 To ArraySize
      If *Array\i[i]>MaxSize
        MaxSize = *Array\i[i]
      EndIf
    Next
  EndIf
  *sortedArray = AllocateMemory((MaxSize + 1)*SizeOf(Integer))
  For i = 0 To ArraySize
    *sortedArray\i[*Array\i[i]] + 1
  Next
  For i = 0 To MaxSize
    cnt = *sortedArray\i[i]
    For j = 1 To cnt
      *Array\i[fillheight] = i
      fillheight + 1
    Next
  Next
  FreeMemory(*sortedArray)
EndProcedure

#AnzEintrys = 20000000
#Min = 100
#Max = 355

Define *tArray.IntArray = AllocateMemory(#AnzEintrys*SizeOf(Integer))

For i = 0 To #AnzEintrys-1
  *tArray\i[i] = Random(#Max-#Min) + #Min
  ;Debug *tArray\i[i]
Next

t = ElapsedMilliseconds()
CountingSort(*tArray, #AnzEintrys, #Max)
t1 = ElapsedMilliseconds()-t
MessageRequester("", Str(t1) + " ms")

For i = 0 To #AnzEintrys-1
  Debug *tArray\i[i]
Next
Gruß, Alex
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

PeterJ hat geschrieben: Die 2. Dimension geht deshalb nicht weil die Arrays unterschiedlich lang sind und ich dann nicht die Möglichkeit habe mit REDIM individuell zu dimensionieren.
Redim funktioniert doch mit Mehrdimensionalen Arrays, du kannst damit
halt nur die letzte Dimension ändern, aber genau das ist auch jetzt der
fall, wenn du das so umständlich machst. Die erste Dimension ist halt die
Anzahl deiner Variablen, die du von Hand geschrieben hast. Die kannst zur
laufzeit auch nicht löschen oder erstellen. :wink:

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

Beitrag von PeterJ »

PMV hat geschrieben:[Redim funktioniert doch mit Mehrdimensionalen Arrays, du kannst damit halt nur die letzte Dimension ändern, aber genau das ist auch jetzt der fall, wenn du das so umständlich machst. Die erste Dimension ist halt die Anzahl deiner Variablen, die du von Hand geschrieben hast. Die kannst zur laufzeit auch nicht löschen oder erstellen. :wink:
MFG PMV
nicht ganz, bei 2-dimensionalen Arrays hätte ich z.B. die Situation:
array(1,n1) n1=1,2, ... 10
array(2,n2) n2=1,2, ... 100
array(3,n3) n3=1,2,...1000

Nachdem ein REDIM immer nur die letzte Dimension ändern kann, kann ich nur REDIM ARRAY(3,1000) als den insgesamt maximalen Wert angeben.
Ich könnte nicht sagen
REDIM ARRAY(1,10)
REDIM ARRAY(2,100)
REDIM ARRAY(3,1000)
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

ReDim braucht du aber nur dann, wenn du den Inhalt buffern willst.
wenn der dir egal ist, kannst du einfach Dim benutzen, und du erhältst ein neues leeres Array mit dem selben Namen.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

PeterJ hat geschrieben:Nachdem ein REDIM immer nur die letzte Dimension ändern kann, kann ich nur REDIM ARRAY(3,1000) als den insgesamt maximalen Wert angeben.
Ich könnte nicht sagen
REDIM ARRAY(1,10)
REDIM ARRAY(2,100)
REDIM ARRAY(3,1000)
Stimmt, und du schreibst dein Programm für einen speziellen Rechner, der
nicht all so viel RAM hat? :mrgreen:

9.990 + 9.900 = 19.890 Byte zu viel ... naja gut, wenn du dafür lieber
Spagetticode produzierst, ist dir natürlich erlaubt. :lol: kleiner Spaß :D

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
PeterJ
Beiträge: 28
Registriert: 05.02.2009 21:15

Beitrag von PeterJ »

PMV hat geschrieben:Stimmt, und du schreibst dein Programm für einen speziellen Rechner, der nicht all so viel RAM hat? :mrgreen:

9.990 + 9.900 = 19.890 Byte zu viel ... naja gut, wenn du dafür lieber
Spagetticode produzierst, ist dir natürlich erlaubt. :lol: kleiner Spaß :D

MFG PMV
Ausgangslage war sortieren von String Arrays, insoweit ist mein REDIM natürlich nicht korrekt und was Dimensionen anbelangt nur beispielhaft. Real würde er in etwa so aussehen:
REDIM ARRAY.S(100,n), wobei n=1,...1.500.000 (derzeit größter Fall den ich hatte). Stringlänge im Schnitt 60 Bytes.
Das macht also 100*1.500.000*61 macht 8.5 GB, das könnte ein normal XP nicht addressieren. Die Applikation selbst braucht meist nur 1 ganz großen Array, und mehrere kleinere Kontrollarrays.
Dabei entsteht nicht notwendigerweise Spagetticode, das alles läßt sich wunderbar modularisieren.
Antworten