Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Zufallszahlen erzeugen?


jackie05 - Mo 08.08.11 19:37
Titel: Zufallszahlen erzeugen?
Hallo,
gibt es eine möglichkeit 3 Zufallszahlen zu erzeugen die zusammen 50 ergeben?
Die Zufallszahlen sollen zwischen 5 und 50 liegen, z.B:
Zufallszahl 1: 7
Zufallszahl 2: 26
Zufallszahl 3: 17

oder so

Zufallszahl 1: 5
Zufallszahl 2: 37
Zufallszahl 3: 8

Die 3 Zufallszahlen sollen halt immer Zusammengerechnet 50 ergeben.

Wie könnte man das realisieren?

Ich bedanke mich schonmal im Voraus.

MfG


Narses - Mo 08.08.11 19:38

Moin!

Erzeuge zwei und bilde die Differenz. :idea: ;)

cu
Narses


jackie05 - Mo 08.08.11 19:42

Danke Dir.
Ich denke nicht das es so gehen könnte, weil wenn die 2 Zufallszahlen 25 oder mehr haben, dann sind es ja schon 50 oder mehr.

Wie soll ich die ambesten erzeugen?

MfG


Kha - Mo 08.08.11 20:08

Dann überlege einmal, wie du die Erzeugung der Zahlen anhand der bereits erzeugten einschränken musst, damit so etwas nicht auftritt. Die Tripel sind dann nicht mehr perfekt gleichverteilt, aber ich nehme mal an, das ist nicht ganz so wichtig?


Delphi-Laie - Mo 08.08.11 20:38

Wenn man Zufallszahlen einzuschränken versucht, dann schränkt man auch den Zufall ein.

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Die Tripel sind dann nicht mehr perfekt gleichverteilt, aber ich nehme mal an, das ist nicht ganz so wichtig?


Nicht nur "nicht perfekt", sondern gar nicht mehr. Bereits die Summe zweier Gleichverteilungen führt zu einer Dreiecksverteilung. Addiert man noch eine gleichverteilte Zufallsgröße dazu, wird die Verteilung noch schmaler, man entfernt sich vom gleichverteilten Zufall noch mehr.


gehstock - Mo 08.08.11 20:42


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
a := Random(40);// die Max 50 - 2x min 5
 b := Random(45-a); //die Max 50 - 1x min 5 - a
 c := 50 -(a+b);// der rest von 50
 edt1.Text := IntToStr(a);
 edt2.Text := IntToStr(b);
 edt3.Text := IntToStr(c);
end;


Edit: ups Fehler so sind zu kleine Werte möglich


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Repeat
    a := Random(40);
  until a >= 5;
  Repeat
    b := Random(45 - a);
  until b >= 5;
  c := 50 - (a + b);
  edt1.Text := IntToStr(a);
  edt2.Text := IntToStr(b);
  edt3.Text := IntToStr(c);


Delphi-Laie - Mo 08.08.11 21:09

user profile icongehstock hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
Repeat
    a := Random(40);
  until a >= 5;


Und so sind überflüssige Schleifendurchläufe möglich! Nimm doch gleich besser: a:=5+random(35) und b:=5+random(40-a).

c wird keine gleichverteilte, sondern eine dreiecksverteilte Zufallszahl sein (s.o.).


jackie05 - Mo 08.08.11 21:17

Genau das hatte ich versucht gehabt und irgendwie hat es nicht so richtig funktioniert.

Vielen Dank für die Hilfe.

MfG


Delphi-Laie - Mo 08.08.11 21:20

Was soll daran "irgendwie nicht richtig funktionieren"? Natürlich funktioniert so etwas! Mit der Addition wird die (gleichverteilte) Zufallsvariable auf dem Zahlenstrahl um eben 5 nach oben verschoben, also erhöht. Damit die andere, die obere Spitze auch korrekt ist, muß man den Zufallszahlenbereich eben um genau den gleichen Betag reduzieren.

Ohne Quellcode ist nicht ersichtlich, wo Dein Problem steckt.


jackie05 - Mo 08.08.11 22:28

Danke Dir.

Ich bin dabei ein Spiel zu schreiben mit jeweils 3 Runden, jede Runde wird eine Zahl generiert und alle 3 Runden sollen dann dementsprechend mit den generierten Zahlen die Zahl 50 ergeben.

So hatte ich es testhalber versucht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
  if (RundeNr = 1then
  begin
    Zahl := random(40-5)+(5);
    tmpZahl := (50-Zahl);
  end;
  if (RundeNr = 2then
  begin
    Zahl := random((tmpZahl div 2)-5)+(5);
    tmpZahl := (tmpZahl-Zahl);
  end;
  if (RundeNr = 3then
  begin
    Zahl := tmpZahl;
    tmpZahl := (tmpZahl-Zahl);
  end;


leider ohne erfolg.

Jetzt habe ich den vorschlag von @gehstock eingebaut und nun funktioniert es.

Vielen Dank.

MfG


Delphi-Laie - Mo 08.08.11 22:46

Implementiere statt der drei Abfragen bezüglich der Rundennummer besser nur eine (1) case-Anweisung.

Daß gestocks Code funktioniert, mag schon sein, doch das widerlegt nicht, daß Zufallszahlen in einem bestimmten Zahlenbereich mit der Methode (nicht im objektorientierten Sinne gemeint), wie ich Sie zeigte, generierbar sind. Der Fehler muß also woanders stecken. Vielleicht zeigst Du auch noch Deinen nunmehr erfolgreichen Code, damit man vergleichen kann.

Postscriptum: Das @-Zeichen (at = an (... gerichtet)) vor gehstock ist wahrlich überflüssig, da Du ihn offensichtlich nicht ansprichst.


Blup - Fr 12.08.11 14:55

a:=5+random(35) liefert nur Zufallszahlen im Bereich 5..39, die 40 wird niemals geworfen.

Delphi-Quelltext
1:
2:
3:
4:
5:
a := random(36);       // a = 0..35
b := random(36 - a);   // b = 0..35
a := a + 5;            // a = 5..40
b := b + 5;            // b = 5..40
c := 50 - (a + b);     // c = 5..40

Dieser Ansatz kann aber keine gleichverteilte Zufallszahlen liefern:

Quelltext
1:
2:
3:
4:
Wahrscheinlichkeit für die Zahl 40
a -> (1/36)            // erster Wurf 40
b -> (1/36) * (1/36)   // erster Wurf 5  + zweiter Wurf 40
c -> (1/36) * (1/36)   // erster Wurf 5  + zweiter Wurf 5


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
Wahrscheinlichkeit für die Zahl 5
a -> (1/36)            // erster Wurf 5
b -> (36/36) * (1/36)  // erster Wurf beliebig + zweiter Wurf 5
c -> (1/36) * (1/36) + // erster Wurf 5  + zweiter Wurf 40
     (1/36) * (1/35) + // erster Wurf 6  + zweiter Wurf 39
     (1/36) * (1/34) + // erster Wurf 7  + zweiter Wurf 38
     ...
     (1/36) * (1/1)    // erster Wurf 40 + zweiter Wurf 5
.
     oder (1/36) * 36-te harmonische Zahl // "Harmonische Reihe"
.
                  n
     (1/36) * ( summe  1/k )
                k = 1


---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Reihe der möglichen Kombinationen

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
 a    b    c
-------------
 5    5   40 --  36 x 5
 5    6   39  |
 5    7   38  |
 5    8   37  |
 5    9   36  |
...           |
 5   40    5 -            
 6    5   39 --  35 x 6
...           |
 6   39    5 -
 7    5   38 --  34 x 7
...           |
 7   38    5 -
...
38    5    7 --   3 x 38
38    6    6  |
38    7    5 -
39    5    6 --   2 x 39
39    6    5 -
40    5    5 --   1 x 40


Betrachtet für die Werte der Variable a jeweils die Anzahl der Kombinationen, können diese so zu Pärchen zusammenfasst werden, daß die Summe der Kombinationen für jedes Pärchen konstant ist:

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
 5 (36)   40 (1)
 6 (35)   39 (2)
 7 (34)   38 (3)
...
22 (19)   23 (18)
.
Gesamtanzahl der Kombinationen
(36 / 2) * (36 + 1) = 666

Damit ergibt sich bei einer gleichmäßigen Verteilung die Wahrscheinlichkeit
 5 -> 36/666
40 ->  1/666


Die einfachste Lösung des Problems ist, jeweils eine Kombination aus der Menge aller Kombinationen auszuwählen.

Delphi-Quelltext
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:
const
  ERROR_PARAMETER_RANGE =
    'Paramter außerhalb des gültigen Bereichs';
.
procedure GetDreierKomination50(n: Integer; out a, b, c: Integer);
var
  x1, x2: Integer;
begin
  if (n < 0or (n > 665then
    raise Exception.Create(ERROR_PARAMETER_RANGE);
.
  x1 := (n div 37);      // x1 = 0..17
  x2 := (n mod 37);      // x2 = 0..36
.
  if x2 < (36 - x1) then
  begin
    a := x1;             // a = 0..17
    b := x2;             // b = 0..35
  end
  else
  begin
    a := 35 - x1;        // a = 35..18
    b := x2 - (36 - x1); // b =  0..17
  end;
.
  a := a + 5;            // a = 5..40
  b := b + 5;            // b = 5..40
  c := 50 - (a + b);     // c = 5..40
end;  
.
procedure TForm1.ButtonTestClick(Sender: TObject);
var
  n, a, b, c: Integer;
begin
  // Test: alle Kombinationen ausgeben
  Memo1.Lines.Clear;
  for n := 0 to 665 do
  begin
    GetDreierKomination50(n, a, b, c);
    Memo1.Lines.Add(Format('%2d %2d %2d', [a, b, c]));
  end;
  // zufällige Kombination:
  // GetDreierKomination50(Random(666), a, b, c);
end;