Autor |
Beitrag |
VampireSilence
      
Beiträge: 109
Erhaltene Danke: 5
C# (VS 2008 Express), PHP/MySQL, Windows XP
|
Verfasst: Sa 27.11.10 23:36
Ich sitze gerade dabei einen Zufallsgenerator zu schreiben. Dieser soll aber keine statistische oder chaotische Verteilung der Ergebnisse aufweisen, sondern eine systematisch gewichtete. Und eigentlich geht es dabei viel mehr um einen logischen Denkanstoß, als um C# selber.
Ich versuche das Ganze mal mit einem Beispiel zu konkretisieren.
Ausgangswert für den Generator ist ein Array, dass beispielsweise so aussehen könnte:
Grün = 10
Rot = 30
Blau = 60
Schwarz = 125
(realisiert über einen Hashtable)
Und wenn ich den Generator jetzt 50 Millionen mal laufen lassen, will ich dass die Ergebnisse später annähernd im Verhältnis 10:30:60:125 (Grün zu Rot zu Blau zu Schwarz) stehen. Ich erwarte auch keinen kompletten Code von euch, sondern eben nur einen kleinen Denkanstoß. Vielleicht lässt sich das Ganze ja auch irgendwie mathematisch lösen. Ich steh grad aufm Schlauch.
mfg
- VampireSilence Moderiert von Kha: Topic aus Basistechnologien verschoben am So 28.11.2010 um 13:11
|
|
huuuuuh
      
Beiträge: 665
Erhaltene Danke: 19
win xp, (win vista), win 7
VS 2008 Express Edition, VS 2010 Express Edition, VS 2010 Professionell
|
Verfasst: Sa 27.11.10 23:58
hab ich selber letztens überlegt. bin zu diesem schluss gekommen:
du hast ja 4 möglichkeiten, nun generierst du eine zufallszahl zwischen 0 und 225 (10+30+60+125). alle ergebnisse zwischen 0 und 10 zählen für Grün, alle zwischen 10 und 40 für rot usw.
hoffe ich hab richtig gedacht, habs noch nie ausprobiert 
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 28.11.10 00:09
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: So 28.11.10 10:49
Michael,
es ist vielleicht nicht schlecht, wenn Du außer einem Link auch noch ein paar erklärende Worte zum Algorithmus findest. Es ist doch immer schön, wenn man die grundlegenden Infos direkt im Forum und nicht extern hat
Grüße
Christian
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 10:23
Hallo,
Mit Luckies Version kann ich nichts anfangen, wozu das hier gut sein soll, das der schon gezogene unwahrscheinlicher wird.( OK, isch hann kopp-ping aber wo sind die Gewichte für mehrfaches Vorkommen ? )
Es geht dort um etwas ganz anderes.
Zitat: |
Gewichteter Zufall - Readme
Copyright Michael Puff
www.michael-puff.de
mail@michael-puff.de
Bedienung:
Dem Programm wird eine Namensliste in einer Textdatei übergeben. Beim
Programmstart wird diese auf dem Bildschirm ausgegeben. Durch drücken
der Return-Taste wird die Namensliste erneut ausgegeben und der zu-
fällig gewählte Namen in der Liste markiert. In den folgenden Zie-
hungen wird der zuvor gezogene Name an den Anfang der Liste gesetzt
bevor erneut ein Name gezogen wird.
Algorithmus:
Damit schon mal gezogene Namen mit geringerer Wahrscheinlichkiet ge-
zogen werden, wird die Zufallszahl zum Bestimmen des Indexes in der
Liste erhöht und der in der letzten Runde gewählte Name an den An-
fang der Liste gesetzt. Da die Wahrscheinlichkeit höher ist, dass
Namen vom Ende der Liste gezogen werden, werden noch nicht gezogen-
en Namen "bevorzugt".
- Ermitteln einer Zufallszahl zwischen 0 und 1
- Ziehen der Wurzel aus der Zufallszahl
- Multplizieren der Zufallszahl mit der Anzahl der Namen
- Addieren von 1
- Abschneiden des Nachkommaanteiles
Durch den Wurzelexponenten kann man bestimmen, wie häufig schon mal
gezogenen Namen noch mals gezogen werden sollen. Je höher der Wurzel-
exponent, desto geringer ist die Wahrscheinlichkeit, da die zufällig
ermittelte Zahl mit höherer Wahrscheinlichkeit einen Namen am Ende+++
der Liste wählt. |
die Version von huuuuuh ist doch schon passend, wobei 0..9 10 Werte sind.
Das lässt sich noch einfacher umsetzen.
Man erzeugt ein Zählerfeld mit einer Länge der Anzahl verschiedenen Farben/Merkmale.
Ein Auswahlfeld mit einer Länge von hier 225.
In dieses Auswahlfeld wird dann der passende Index eingetragen also von 0..9 der Index auf Zählerfeld Grün= 0, von 10..40 ( linkse Seite+Anzahl -1 = 10 + 30 - 1) für Rot = 1.
Dann erzeugt man sich nur noch Zufallszahlen im Bereich von 0..225-1 und
erhöht dann ZaehlerFeld[AuswahlFeld[ganzzahlige Zufallszahl aus 0..224]].
Das Auswahlfeld kann vielleicht verkleinern, wenn man das alle Zahlen durch deren GGT 5 teilt.
statt 10/30/60/125 eben 2/6/12/25.
Aber wie macht man es geschickt, wenn es Werte wie sqrt(0.5) oder 2/pi() sind.
Auf die Schnelle fiele mir nur eine Sonderbeandlung ein, bei der im Auswahlfeld an an einer Grenzstelle zum Beispiel -1 steht und man entsprechend reagiert.
Sei Grün mit einer Wahrscheinlichkeit 2/pi() = 0,6366.. und Rot eben 1-2/pi().
Dann kann man mit Kettenbruch www.arndt-bruenner.d...s/bruchrechnung1.htm eine Näherung der Aufteilung finden:
2/pi()= 226/355.Mist ist schon auf 1e-8 genau.
Also wählte man eine Feld von Länge 355 und alle Werte bis 226-1 zählen für Grün, die anderen für Rot.
Gruß Horst
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 29.11.10 10:36
Der Zufall ist schon gewichtet. Wie du schon gesagt hast, wird eine schon mal gezogene Person mit einer geringeren Wahrscheinlichkeit wieder gezogen, als eine Person die noch nicht gezogen wurde.
@Christian: Es liegt eine Readme bei.
|
|
Christian S.
      
Beiträge: 20451
Erhaltene Danke: 2264
Win 10
C# (VS 2019)
|
Verfasst: Mo 29.11.10 10:42
_________________ Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 29.11.10 10:43
Aber jetzt wurde sie ja gepostet. 
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mo 29.11.10 11:27
 Das ist etwas zu kompliziert gedacht  .
Da jedes p aus P die Wahrscheinlichkeit p/sum(P) besitzt , einfach eine Zahl 0<=X<=sum(P) auswürfeln lassen und schauen, welche Partialsumme p1 + p2 + ... + pn sie übersteigt. n ist dann der gesuchte Index.
Das funktioniert komplett ohne zusätzlichen Speicher und problemlos auch mit reellwertigen Zahlen.
_________________ >λ=
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 11:29
Hallo,
wie bekomme ich denn sinnvoll die Aufteilung im Verhältnis 2/6/12/25 in Luckies Variante, das ist mir nicht offensichtlich.
Auserdem ist mir nicht klar, was das Programm eigentlich vollbringen soll.
Ich ziehe einen Namen und mache dessen Auswahl "und" seiner Nachbarn doch unwahrscheinlicher, weil ich diese nach vorne in der Liste verschiebe und die lineare Verteilung der Zufallszahlen in eine 1/kubische ( Exponent = 1/3 im Programm) umwandle, dass heisst werte von 0.79 .. 1 haben die Wahrscheinblichkeit von 0.5.
Dieses passiert bei jeder Ziehung.
Es erschliesst sich mir nicht, was man damit sinnvolles machen kann. Der erstgezogene Name wandert langsam bergauf, bis er wieder gezogen wird.
Man ändert ja ständig die Position, funktioniert das dan überhaupt noch mit der Wahrscheinlichkeit.
Letztendlich müssten bei dem Programm alle Namen gleichmaässig gezogen werden.
Gruß Horst
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 29.11.10 11:45
Horst_H hat folgendes geschrieben : | Auserdem ist mir nicht klar, was das Programm eigentlich vollbringen soll. |
Ok, das ist in der Berufsschule entstanden. Und zwar hatte der Lehrer eine Fragerunde gemacht um uns zu testen wie gut wir den Stoff beherrschen. Wenn man jetzt eine ungewichtete Wahrscheinlichkeit nimmt, kann es sein, dass zufällig ein paar Schüler immer wieder dran kommen und einige gar nicht.
Durch den Algorithmus wird die Wahrscheinlichkeit nach "oben" in der Liste verschoben. Und dadurch, dass schon gezogene Schüler ans Ende der Liste wandern, wird die Wahrscheinlichkeit für noch nicht gezogene Schüler höher, da sie nach "oben" wandern.
Zitat: | Letztendlich müssten bei dem Programm alle Namen gleichmaässig gezogen werden. |
Und genau das wird dadurch eben wahrscheinlicher.
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 12:55
Hallo,
@Kha:
Du hast ja recht, mir dem Vergleich.
Ein Feld mit den Namen von 0..n-1 // 0..3 = grün, rot, blau, schwarz
Ein Feld mit den aufsummierten Wahrscheinlichkeiten p[0..n-1] = 10/225,(10+30)/225,(10+30+60)/225, 1
Dann musst Du immer solange abfragen, bis das passende Feld gefunden ist und der gefundene Index gehört dann zum entsprechenden Namen.
Und dieses abfragen wollte ich mir sparen und hoffte, es wäre damit schneller, als ein Vergleich von mehreren Fliesskommazahlen, statt ein indirekter Zugriff.
Natürlich kann man auch mit binärer Suche im Feld p suchen., aber das braucht sicher keiner.
Gruß Horst
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Mo 29.11.10 14:50
www.delphi-forum.de/...arbeit_99225,20.html
In dem Programm kannst du Gewichtungen einstellen, der Source ist dabei. Die Technik ist die von huuuuuh geschilderte
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 15:26
Hallo,
nur um zu zeigen, dass es mit dem Feldzugriff zügiger ist, als die Summenwahrscheinlichkeit abzuklappern.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164:
| program Nichts;
{$IFDEF FPC} {$Mode delphi} {$R-} {$I-} {$V-} {$OPTIMIZATION RegVar} {$OPTIMIZATION Peephole} {$OPTIMIZATION CSE} {$OPTIMIZATION ASMCSE} {$ENDIF} {$AppType console}
uses sysutils;
const cZiehungen = 22500000; cAnzahlNamen = 4; cAnzahlNamen1 = cAnzahlNamen-1;
type tIndexNamen = 0..cAnzahlNamen1; tNamenFeld = array[tIndexNamen] of string[10]; tZaehlFeld = array[tIndexNamen] of longint; tIndexWahrschFeld = array[tIndexNamen] of double;
const cNamen : tNamenFeld = ('gruen','rot','blau','schwarz'); cAnteile : tZaehlFeld = (10,30,60,125); var IndexFeld : array of tIndexNamen; ZaehlFeld : tZaehlFeld; IndexWahrschFeld : tIndexWahrschFeld; T1,T0 : TDateTime; SumAnteile,i : longint;
procedure ZaehlfeldLoeschen; var i : longINt; begin For i in tIndexNamen do ZaehlFeld[i] := 0; end;
function GesamtAnteile: longInt; var i : longInt; begin result := 0; For i in tIndexNamen do result := result+cAnteile[i]; end; procedure IndexFeldAufbauen; var i,j,k : longint; begin setlength(IndexFeld,SumAnteile); k := 0; For i in tIndexNamen do For j := 1 to cAnteile[i] do begin IndexFeld[k] := i; k := k+1; end; end;
procedure IndexWahrschFeldAufbauen; var i,k : longint; begin k := 0; For i in tIndexNamen do begin k := k + cAnteile[i]; IndexWahrschFeld[i] := k / sumAnteile; end; end;
procedure ZaehlFeldAusgabe; var i : LongInt; begin For i in tIndexNamen do writeln(cNamen[i]:10,ZaehlFeld[i]:11,ZaehlFeld[i]/cZiehungen*sumAnteile:10:3); end;
procedure IndexZiehungen; var i : longint; begin ZaehlfeldLoeschen; For i := 1 to cZiehungen do inc(Zaehlfeld[IndexFeld[random(SumAnteile)]]);
ZaehlFeldAusgabe; end;
procedure IndexWahrschFeldZiehungen; var i,j: longint; p : double; begin ZaehlfeldLoeschen; For i := 1 to cZiehungen do begin p := random; For j in tIndexNamen do if p < IndexWahrschFeld[j] then break; inc(ZaehlFeld[j]); end; ZaehlFeldAusgabe; end;
begin sumAnteile := GesamtAnteile;
for i := 0 to 1000000 do begin IndexFeldAufbauen; IndexWahrschFeldAufbauen; end;
randseed := 47110815;writeln('IndexFeld'); T0 := time; IndexFeldAufbauen; IndexZiehungen; T1 := time; writeln; writeln(FormatDateTime('hh:mm:ss.zzz',T1-T0));
randseed := 47110815; writeln('Wahrscheinlichkeitsfeld'); T0 := time; IndexWahrschFeldAufbauen; IndexWahrschFeldZiehungen; T1 := time; writeln; writeln(FormatDateTime('hh:mm:ss.zzz',T1-T0));
end. |
ist bei mir also fast doppelt so schnell
Gruß Horst
Anbei ein Bild von Xion's Version zu der Ausgangsfrage
Einloggen, um Attachments anzusehen!
|
|
Delphi-Laie
      
Beiträge: 1600
Erhaltene Danke: 232
Delphi 2 - RAD-Studio 10.1 Berlin
|
Verfasst: Mo 29.11.10 16:46
huuuuuh hat folgendes geschrieben : | du hast ja 4 möglichkeiten, nun generierst du eine zufallszahl zwischen 0 und 225 (10+30+60+125). alle ergebnisse zwischen 0 und 10 zählen für Grün, alle zwischen 10 und 40 für rot usw. |
Das ließe sich kürzen. Das kleinstmögliche Quadrupel, bei dem alle Zahlen noch ganzzahlig sind, wäre: 2-6-12-25, als Summe: 45.
|
|
VampireSilence 
      
Beiträge: 109
Erhaltene Danke: 5
C# (VS 2008 Express), PHP/MySQL, Windows XP
|
Verfasst: Mo 29.11.10 20:16
Also die Variante von huuuuh kommt am Nächsten an mein Problem heran. Das Ganze liefe also quasi wie bei einem Roulette. Als vereinfachende Randbedingung sei allerdings gesagt, dass ich die Gewichtung später manuell erstellen werden und es sich dabei nur um Ganzzahlen handeln wird. Einen Verglich zwischen 1 : Pi würde ich dann einfach mit 100 zu 314 Nähern, das wäre exakt genug. Was mir nur ein bisschen Sorgen macht, ist die Performance, denn es werden nachher eine Menge "Farben" (um mein Beispiel mal aufzugreifen) sein und ich müsste dementsprechend viele Indizes überprüfen, um zu einem Ergebnis zu kommen. Und das sollte am besten keine Minute dauern, wenns geht. Vielleicht ist es auch eine Überlegung wert, das Ganze zu assemblieren, aber derweil bastle ich jetzt erstmal noch dran rum, vielleicht finde noch ne bessere Lösung.
mfg
- VampireSilence
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 21:31
Hallo,
liest hier jemand mal etwas komplett bis zum Ende durch ??  ( Tue ich auch nicht immer )
Wenn Du meinen ObjektPascal-Quelltext des "progam Nichts" mal zu Ende scrollst, steht dort am Ende die Laufzeit für beide Versionen.( AMD Athlon 245 X2 2.9 Ghz )
cZiehungen = 22500000;// -> 22,5 Millionen
Kha-Version 1,1 Sekunden -> 117 CPU-Takte
hu..uh-Version 0,61 Sekunden -> 78 CPU-Takte // fast alles nur random ( > 70 CPU-Takte arbeitet mit Division )
Da der Zufallszahlengenerator von Delphi2006ff erheblich! schneller ist, könnte der von C# vielleicht auch derart gut sein.
Um wieviele Farben soll es den gehen?
Gruß Horst
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Mo 29.11.10 22:25
Angenommen du hast 6 Farben.
Delphi-Quelltext 1: 2: 3: 4:
| Cls: array of integer; ... Cls[3]:= 17; ... |
R: array of integer; //BinSuche array
// Aufsummierung berechnen (1x)
Delphi-Quelltext 1: 2: 3: 4:
| R[0]:=Cls[0]; R[1]:=R[0]+Cls[1]; R[2]:=R[1]+Cls[2]; ... |
Die Arrays sehen dann so aus:
Delphi-Quelltext 1: 2:
| Cls: 1 4 30 17 101 401 R : 1 5 35 52 153 554 |
Als Zufallszahl ziehen wir dann z.B:
x = 400
Binäre Suche:
Delphi-Quelltext 1: 2: 3:
| R[2] = 35 < 400 R[4] = 153 < 400 R[5] = 554 > 400 -> Farbe Nummer 4 ist die gesucht. |
Das ganze nennt sich Binäre Suche und ist O(log(n)) schnell.
Hast du 1.000.000 Farben, braucht das log(1.000.000) = log(2^20) => 20 Vergleiche . Hübsch, gell?
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
Horst_H
      
Beiträge: 1654
Erhaltene Danke: 244
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Mo 29.11.10 22:35
Hallo,
O.K hier dann array of integer statt array of double ( da ging es ja um 2/pi() als Grenze) macht Sinn,.
Werde ich mal testen.
Der Vorschlag binäre Suche gab es schon um 11:35 Uhr ...
Gruß Horst
Edit:
Ausgabe mit p : array of longint statt double und 6 Farben immer noch 22,5 Mio Ziehungen.
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| IndexFeld gruen 616907 10.008 rot 1850633 30.021 blau 3697190 59.977 schwarz 7702419 124.950 gelb 801646 13.004 weisz 7831205 127.040
Laufzeit 00:00:00.605 Wahrscheinlichkeitsfeld gruen 616907 10.008 rot 1850633 30.021 blau 3697190 59.977 schwarz 7702419 124.950 gelb 801646 13.004 weisz 7831205 127.040
Laufzeit 00:00:00.877 Drücken Sie eine beliebige Taste . . . |
Zuletzt bearbeitet von Horst_H am Mo 29.11.10 22:42, insgesamt 1-mal bearbeitet
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Mo 29.11.10 22:41
Horst_H hat folgendes geschrieben : | Der Vorschlag binäre Suche gab es schon um 11:35 Uhr ... |
Garnicht gesehen so klein und versteckt wie das da stand  (11:55 war das)
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
|