|
Registriert seit: 15. Nov 2004 2.647 Beiträge |
#1
Hi,
ich habe eine Funktion geschrieben, welche eine Liste von Zufallszahlen erstellt. Jede Zahl gehört einer Zahlenkategorie an. Pro Zufallsdurchlauf darf jeweils nur eine Zahl aus jeder Kategorie in der Liste am Ende auftauchen. Das heißt: 1=Kategorie 1; 2,3=Kategorie 2; 4,5=Kategorie 3. Nun kriege ich eine 2 als Zufallszahl. Also schreibe ich sie in die Liste und merke mir, dass sowohl 2 als auch 3 (da gleiche Kategorie) verworfen werden müssen, falls sie nochmal vorkommen sollten. Das geht solange weiter, bis keine Zahl mehr übrig bleibt. Man hat am Ende also eine Teilmenge aller Zufallszahlen, jede Zahlenkategorie nur einmal. Angenommen man hat 5 Kategorien, dann hätte man auch nur 5 Zufallszahlen obwohl es sagen wir mal 14 mögliche Zufallszahlen gibt. Das zu programmieren klingt ja einfach. Eine Prozedur erzeugt die besagte Liste und eine Funktion holt von Anfang bis Ende alle Zufallszahlen nach der Reihe raus und wenn die Funktion am Ende der Liste ist, lässt es über die Prozedur eine neue erstellen. So könnte man gleichmäßig nach Kategorien verteilte Zufallszahlen erhalten. Das geht sogar gut, aber irgendwann verabscheidet sich mein Programm mit dem CPU-Fenster vom Debugger. Die Prozedur und Funktion sind Teilbestandteil einer Prozedur, daher habe ich das so zerstückelt. Es hat aber seinen strukturellen Sinn. Die Zahlen 12,13,14 sollen ausgeschlossen werden, also nicht wundern, dass ich das tue.
Delphi-Quellcode:
Ich hoffe, es ist alles verständlich. Ich weiß nicht, was ich noch genauer erklären könnte, habe aber das dumme Gefühl, dass da keiner durchsteigt. Falls es jemand überhaupt nicht versteht:
procedure ...;
// Zufallsarray var ArrRnd : TDynIntArr; RndAkt : Integer; ArrRndU : Array of Integer; procedure FillRndArr; // Zahlen in Zufallsarray einfügen procedure AddArr(Zahlen : Array of Integer); var i : Integer; begin for i := 0 to High(Zahlen) do begin SetLength(ArrRndU, High(ArrRndU)+2); ArrRnd[High(ArrRndU)] := Zahlen[i]; end; end; var i, j, Zahl : Integer; Flag : Boolean; begin // Alles zurücksetzen RndAkt := 0; SetLength(ArrRndU, 0); SetLength(ArrRnd, 0); i := 0; while i < 4 do // 5 Zahlenkategorien gibt es begin // Zahl ermitteln und prüfen ob zulässig Zahl := Random(12); Flag := True; for j := 0 to High(ArrRndU) do if ArrRndU[j] = Zahl then begin Flag := False; Break; end; // Zahl ist zulässig if Flag then begin SetLength(ArrRnd, High(ArrRnd)+2); ArrRnd[High(ArrRnd)] := Zahl; // geht performanter, gerade weil ich ja im Voraus weiß, wie lang mein Array ist - nutzt aber nichts, ich will alles so einfach wie möglich halten erstmal, damit der Fehler beseitigt wird. // Kategorisierung case Zahl of 0, 1 : AddArr([0, 1]); 2, 3, 4, 5 : AddArr([2, 3, 4, 5]); 6, 7, 8, 9 : AddArr([6, 7, 8, 9]); 10 : AddArr([10]); 11 : AddArr([11]); end; Inc(i); end; end; end; // Zufallszahl ermitteln. Jede Zahlenkategorie darf pro Zufallsdurchlauf nur einmal vorkommen. function RandomExclusive : Integer; begin Result := ArrRnd[RndAkt]; Inc(RndAkt); if RndAkt = 4 then FillRndArr; end; var i : Integer; begin FillRndArr; RndAkt := 0; for i := 0 to 49 do RandomExclusive; end; 1,2,3,4,5 ist der Zahlentopf (einfaches Beispiel). 1 ist eine Kategorie, 2 und 3 sind eine Kategorie, 4 und 5 sind ebenfalls eine Kategorie (per Definition!). Es ist ein Array voller Zahlen gesucht, welches insgesamt von jeder Kategorie nur eine Zahl enthält. Beim jetzigen Beispiel also: 1, 2 oder 3, 4 oder 5. Beispielarray: 1, 3, 4 Es geht mir halt darum, dass die entsprechenden Zahlenkategorien relativ gleichmäßig verteilt werden. Vielleicht fällt jemanden ja auch eine andere einfacherere Variante ein. Das Problem sind halt die Kategorien. Sonst musste ich immer nur dafür sorgen, dass jeweils nur eine Zufallszall pro Durchlauf vorkam, aber es gab da keine Kategorien - das war nichts weiter als eine kleine Schleife die das erledigt hat, hier sieht es komplizierter aus leider. Nochmal zu meinem Code: Angenomen im case läuft das Programm den Weg über 2,3,4,5. Dann beendet die Prozedur erfolgreich und sie wird erneut aufgerufen (50 mal - siehe Schleife unten). Es ist egal welchen Weg das Programm im case dann geht, außer wenn es WIEDER 2,3,4,5 ist - dann kommt die ungültige Zeigeroperation welche sogar teilweise den Debugger zum Absturz brachte. Das finde ich schon sehr merkwürdig. EDIT: Nochmal weiter rumgetestet. Der Fehler tritt auch auf, wenn case-Option 1 und 3 auftreten, scheint also nichts mit mehrfachem Aufruf zu tun zu haben. Weiterer Nachtrag: Habe eine einfacherere Lösung gefunden und PROBIERT. Ganz einfach:
Delphi-Quellcode:
Dann setzt man ArrRndKat noch voll auf False und das MÜSSTE laufen. Beim xten Durchlauf kracht es plötzlich (Ungültige Zeigeroperation) - nach erfolgreicher Ausführung - beim end; der Aufrufprozedur. Da platzte mir der Kragen und ich nahm den Code in ein mageres Testprojekt auf und hatte...Trommelwirbel...den gleichen Fehler. Noch wütender öffnete ich Lazarus und...Trommelwirbel...KEIN Fehler. Das ist dann das Ende von Delphi, der Compiler von Delphi7 macht generell Fehler die nicht mehr tragbar sind langsam. Dann kompilier ich lieber direkt mit dem FPC, die VCL von Delphi benutze ich eh schon lange nicht mehr. Schade, abgesehen vom fehlerhaften Compiler war D7 deutlich besser als Lazarus. Aber Compiler geht vor. Natürlich kann es sein, dass ich einen dummen Fehler gemacht habe, den bloß keiner sieht. Eventuell hängt es auch mit einer unvorstellbaren(!) 32- auf 64-bit Inkompatibilität zusammen (kann ich mir aber echt nicht vorstellen), Fakt ist, dass der Compiler auch einige andere Fehler hat und das jetzt leider sein Ende bedeutet, zumindest für große Projekte.
var ArrRndKat : Array[0..4] of Boolean;
function RandomExclusive : Integer; var Flag, SFlag : Boolean; Zahl, i : Integer; begin Zahl := 0; Flag := True; while Flag do begin // Prüfen ob ArrRndKat wieder komplett auf False gesetzt werden müssen, also ob alle Zahlenkategorien verbraucht sind. SFlag := True; for i := 0 to 4 do if not ArrRndKat[i] then begin SFlag := False; Break; end; if SFlag then for i := 0 to 4 do ArrRndKat[i] := False; // Zahl bestimmen und falls die Zahl okay ist, direkt zurückgeben und Kategorie sperren. Zahl := Random(12); case Zahl of 0, 1 : if not ArrRndKat[0] then begin Flag := False; ArrRndKat[0] := True; end; 2, 3, 4, 5 : if not ArrRndKat[1] then begin Flag := False; ArrRndKat[1] := True; end; 6, 7, 8, 9 : if not ArrRndKat[2] then begin Flag := False; ArrRndKat[2] := True; end; 10 : if not ArrRndKat[3] then begin Flag := False; ArrRndKat[3] := True; end; 11 : if not ArrRndKat[4] then begin Flag := False; ArrRndKat[4] := True; end; end; end; Result := Zahl; end; Gruß, Nils Geändert von Nils_13 ( 7. Jan 2011 um 18:10 Uhr) |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |