Forum: Compiler & IDEs Zufallszahlen: Frage und Bemerkung


von Michael P. (praderbz)


Lesenswert?

Hallo Forenmitglieder,

ich bastle für die Modellbahn an einem Heißläuferortungsgerät. Da im 
Modell überhitzte Achslager (hoffentlich) nie auftreten, möchte ich eine 
Möglichkeit finden, pseudo-zufällige Zahlen in der Auswertung zu nutzen.

Ich habe mir das momentan so überlegt:
1
#define PERCENTAGE 200  // Gesamtzahl an Achsen, bis Ereignis wieder auftritt
2
#define MAX_ANZAHL 3 // maximale Anzahl an Fehlern pro einzelnen Zug
3
4
unsigned int axle_count; //zählt die Achsen eines einzelnen vorbeifahrenden Zuges
5
unsigned int global_axles; //zählt Achsen aller Züge
6
7
unsigned char fehleranzahl;  // Anzahl an Fehlern pro untersuchten Zug
8
unsigned int fehler[MAX_ANZAHL];  //Array, wo Fehler gespeichert werden
9
10
...
11
12
if (global_axles >= PERCENTAGE)       // Ereignis tritt ein
13
{
14
    /* Erzeuge Fehleranzahl so lange, bis kleiner der Maximalanzahl */
15
    while( fehleranzahl > MAX_ANZAHL)
16
        fehleranzahl = rand() / rand();  // Bruch, um Zahl zu verkleinern
17
18
    /* generiere Fehler innerhalb Zuglänge; 0 ist nicht zugelassen */
19
    for (uint8_t j=0; j< fehleranzahl; j++)
20
    {
21
       while( (j > axle_count) || (fehler[j] == 0))
22
          fehler[j] = rand();
23
    }
24
    global_axles = 0;  // Setze Zähler wieder auf null
25
26
} else fehleranzahl = 0;
27
28
...

Diese Methode erlaubt es mir nicht, eine prozentuelle Vorgabe zu machen, 
wieviel Fehler produziert werden. Vorteilhaft ist aber die Zufälligkeit 
der Anzahl, als auch der Position im Zug.
Gibt es eine Möglichkeit, die Gesamtzahl abzuschätzen (außer Probieren)? 
Oder habt ihr andere Ideen?

Alternativ würde ich nur alle jede bei global_axles == PERCENTAGE 
liegende Achse als fehlerhaft markieren, womit die Prozentualität 
eindeutig festgelegt ist. Wäre halt nicht so schön.

Grüße
Michael Prader

von Michael P. (praderbz)


Lesenswert?

Errata corrige:

Die while-Schleifen sind natürlich mit do-while-Schleifen zu ersetzen. 
kopfschüttel

Eine weitere Möglichkeit ist mir eingefallen, und zwar folgende:
Falls global_axles >= PERCENTAGE ist, wird ein einzelner Fehler 
generiert mit
1
do {
2
   fehler = rand();
3
} while (fehler > axle_count)

von Maximilian K. (laplace)


Lesenswert?

Ich verstehe nur Bahnhof...:]

Aber nur so am Rande solltest du

while( fehleranzahl > MAX_ANZAHL)
   fehleranzahl = rand() / rand();  // Bruch, um Zahl zu verkleinern

mit

fehleranzahl = rand() % (MAX_ANZAHL + 1);

ersetzen, zumal du bei deiner version irgendwann eine Null im Nenner 
stehen hast...crash.

von Michael P. (praderbz)


Lesenswert?

Autsch, vielen Dank für den Hinweis, Maximilian.

Lg
Michael

von Michael P. (praderbz)


Lesenswert?

Maximilian K. wrote:
> Ich verstehe nur Bahnhof...:]

Ich versuche, das Problem klarer darzustellen:

Ich habe ein Gerät gebaut, welches auf der Modellbahn die Achsen eines 
vorbeifahrenden Zuges zählt.

Nun kommt es manchmal vor, dass Achsen wegen ungenügender Schmierung 
heiß werden. Das kann schwere Folgen haben, sodass dies erkannt werden 
muss. Das macht ein Heißläufer-Ortungsgerät.
Im Modell ist das natürlich nicht möglich, sodass hier per Zufall 
gewisse Achsen als "überhitzt" deklariert werden sollen. Es ist 
wünschenswert, die Prozentualität einzustellen, bezogen auf die 
Gesamtmenge aller vorbeifahrenden Achsen (also n Züge mit jeweils i(n) 
Achsen).

Mit der im ersten Posting genannten Methode wird zwar nur bei jeder 
PERCENTAGE-Achse die Fehlerprozedur aufgerufen, es werden dabei aber von 
0 bis MAX_ANZAHL viele Fehler generiert. Damit ist die Prozentualität 
nicht mehr gegeben. Warum möchte ich mehrere Fehler in einem Zug? Weil 
das in Echt schon mal vorkommen kann.

Vielleicht hat jemand eine Idee, wie ich die Prozentualität genauer 
einhalten kann.

Ansonst generiere ich nur einen Fehler pro Zug. Wäre nicht ideal, aber 
auch nicht so schlimm.

Lg
Danke
Michael Prader

von Sven K. (skasko)


Lesenswert?

Hallo,

nur eine kurze Überlegung:

Wenn Du 20% der Achsen Fehlerhaft haben willst, darf es bei der 
Berechnung der fehlerhaften Achsen pro Zug keine 0 geben.

Eine fehlerhafte Achse wird immer zugelassen.
Zwei fehlerhafte Achsen immer nur in der hälfte der Fälle (An dieser 
Stelle hättest Du dann Deine 0)
Drei fehlerhafte Achsen nur in 1/3 der Fälle

Gruß
Sven

von Michael P. (praderbz)


Lesenswert?

Hallo Sven,

ich probiere mal folgendes, in der Hoffnung, dass es dem Sinn deiner 
Überlegung entspricht:

1
#define PERCENTAGE 200  // Gesamtzahl an Achsen, bis Ereignis wieder auftritt: Prozentualität 0.5 %
2
#define MAX_ANZAHL 3 // maximale Anzahl an Fehlern pro einzelnen Zug
3
4
unsigned int axle_count; //zählt die Achsen eines einzelnen vorbeifahrenden Zuges
5
unsigned int global_axles; //zählt Achsen aller Züge
6
7
unsigned char fehleranzahl;  // Anzahl an Fehlern pro untersuchten Zug
8
unsigned int fehler[MAX_ANZAHL];  //Array, wo Fehler gespeichert werden
9
10
unsigned char total_events, double_event, triple_event;  // Zähler für alle Ereignisse, für solche mit 2 Fehlern und 3 Fehlern
11
12
...
13
14
if (global_axles >= PERCENTAGE)       // Ereignis tritt ein
15
{
16
    if (total_events >= 0xFF)
17
    {
18
      total_events = 0; double_event = 0; triple_event = 0;
19
    }
20
21
    total_events++;  // erhöhe Gesamtzahl der Ereignisse
22
23
    /* Erzeuge Fehleranzahl so lange, bis kleiner der Maximalanzahl */
24
    do {
25
        fehleranzahl = rand() % (MAX_ANZAHL +1);  // Bruch, um Zahl zu verkleinern
26
    } while( (fehleranzahl > MAX_ANZAHL) || (fehleranzahl == 0))  // Fehleranzahl 0 wird nicht zugelassen
27
    
28
    switch (fehleranzahl)
29
    {
30
        case 1: break;  // 1 wird immer zugelassen
31
32
        case 2: 
33
           if (double_event >= (total_events / 2)) fehleranzahl = 0;
34
           double_event++;
35
           break;  // nur in Hälfte der Fälle zugelassen
36
37
        case 3: if (triple_event >= (total_events / 3)) fehleranzahl = 0;
38
           triple_event++;
39
           break;  // nur in einem Drittel der Fälle zugelassen
40
        
41
        default: fehleranzahl = 0; break;  // zur Sicherheit
42
    }
43
44
45
    /* generiere Fehler innerhalb Zuglänge; Achse 0 ist nicht zugelassen 
46
       Die Wahrscheinlichkeit, dass zwei Fehler hintereinander gleich sind (an der selben Achse) wird nicht berücksichtigt
47
    */
48
    for (uint8_t j=0; j< fehleranzahl; j++)
49
    {
50
        do {
51
          fehler[j] = rand();
52
        } while( (fehler[j] > axle_count) || (fehler[j] == 0))
53
    }
54
55
    global_axles = 0;  // Setze Zähler wieder auf null
56
    
57
    
58
59
} else fehleranzahl = 0;
60
61
...

Verstehe ich das richtig, dass die Anzahl der doppelten und dreifachen 
Ereignisse auch dann hochgezählt werden muss, wenn die Zahl selbst nicht 
zugelassen wird?

von Karl H. (kbuchegg)


Lesenswert?

Du musst folgendes machen:

rand() liefert dir eine Zufallszahl im Bereich 0 bis RAND_MAX
die alle gleich wahrscheinlich sind.

Du möchtest haben, dass eine fiktive Zahl mit einer gewissen
Wahrscheinlichkeit entweder 0 oder 1 ist (0 = kein Fehler,
1 = Fehler).

D.h. du rechnest dir eine Grenze aus. Liegt die Zahl die du
von rand() bekommst über dieser Grenze, dann ist deine
fiktive Zahl 0, liegt sie darunter oder ist sie gleich,
dann ist sie 1.

Diese Grenze liegt bei (RAND_MAX + 1 )* Fehlerwahrscheinlichkeit / 100

Mach mal ein Beispiel mit einer kleiner Zahl für RAND_MAX.
Sagen wir mal deine maximale Zufallszahl sei 20.
Du möchtest eine Fehlerwahrscheinlichkeit von 60%.
Die Grenze sei dann bei  21 * 60 / 100 = 12
In diesem hypotetischen Beispiel, kann rand() liefern
0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20

Für jede dieser Zahlen bedeutet das

 Zahl von rand()   Zahl <= 12   -> daher fikitve Zahl
-------------------------------------------
  0                   ja         1
  1                   ja         1
  2                   ja         1
  3                   ja         1
  4                   ja         1
  5                   ja         1
  6                   ja         1
  7                   ja         1
  8                   ja         1
  9                   ja         1
 10                   ja         1
 11                   ja         1
 12                   ja         1
 13                  nein        0
 14                  nein        0
 15                  nein        0
 16                  nein        0
 17                  nein        0
 18                  nein        0
 19                  nein        0
 20                  nein        0

Von den 21 möglichen Ergebnissen, die du von rand() erhalten
kannst, führen 8 zu einem Ergebnis von 0, und 12 zu einem
Ergebnis von 1. Du hast daher eine Chance von 12 * 100 / 21
oder 57.1% dass das Ergebnis dieser Berechnung eine 1
(und du daher einen Fehler erzeugen willst) ist, da ja
rand() seine Zahlen nicht der Reihe nach, sondern zufällig
in irgendeiner Reihenfolge liefern wird.

int Border = Percent * ( (long)RAND_MAX + 1 ) / 100;

  if( rand() <= Border ) {
    generate_Error;
  }

von Sven K. (skasko)


Lesenswert?

Ich habe jetzt nicht überprüft, wie es sich bei der obigen Lösung mit 
der Wahrscheinlichkeit verhält.
Ich meinte es etwas anders, als ich es beschrieben habe.
1
    switch (fehleranzahl)
2
    {
3
        case 1: break;  // 1 wird immer zugelassen
4
5
        case 2: 
6
           if (double_event < 2))
7
           {           
8
             fehleranzahl = 0;
9
             double_event++;
10
           }
11
           else
12
           {
13
             double_event = 0;
14
           }
15
           break;  // nur in Hälfte der eintretenden Fälle zugelassen
16
17
        case 3:           
18
           if (triple_event < 3))
19
           {           
20
             fehleranzahl = 0;
21
             triple_event++;
22
           }
23
           else
24
           {
25
             triple_event = 0;
26
           }
27
           break;  // nur in einem Drittel der auftretenden Fälle zugelassen
28
        
29
        default: fehleranzahl = 0; break;  // zur Sicherheit
30
    }

Wenn man von der Wahrscheinlichkeit ausgeht, daß alle Fälle (1 Fehler, 2 
Fehler, 3 Fehler) über einen großen Zeitraum in gleicher Anzahl 
eintreten, bleibt die Summe der fehlerhaften Achsen in diesem Zeitraum 
gleich.

von Michael P. (praderbz)


Lesenswert?

> Du möchtest haben, dass eine fiktive Zahl mit einer gewissen
> Wahrscheinlichkeit entweder 0 oder 1 ist (0 = kein Fehler,
> 1 = Fehler).

Das ist nicht ganz korrekt. Ich möchte verschiedene Fehlerzahlen von 0-3 
haben. Der Beitrag hat aber fürs praktische Verständnis genützt, danke.

@ Sven

Aha, der Unterschied zu meiner Version ist, dass ein zweifacher Fehler 
nur bei seinem EIGENEN jeden zweiten Auftreten gültig ist. Ich hatte in 
bei JEDEM zweiten Ereignis zugelassen, was falsch ist, das seh ich ein. 
:-)

Tritt das Ereignis "2 Achsen" in deinem Code nicht erst beim 3. Mal auf?

(double_event < 2) ist bei 0 und 1 wahr, aber erst bei 2 unwahr. Das 
gibt zwei:eins.  Das ist ein (äußeres) Verhältnis von 3:1, und wenn ich 
mich nicht täusche, würde  damit es in nur einem Drittel der Fälle 
auftreten.

Michael

von Sven K. (skasko)


Lesenswert?

jo da hast Du natürlich Recht, sorry.

Sven

von Michael P. (praderbz)


Lesenswert?

Ok.

Vielen Dank für die wertvolle Hilfe!

Grüße
Michael

von Michael P. (praderbz)


Lesenswert?

@ Karl Heinz Buchegger

Noch ein Nachtrag zum Thema:

In der Fragestellung bin ich ja davon ausgegangen, dass die 
Fehlerroutine alle percentage-Achsen aufgerufen wird.

> Du musst folgendes machen:
>
> rand() liefert dir eine Zufallszahl im Bereich 0 bis RAND_MAX
> die alle gleich wahrscheinlich sind.
>
> Du möchtest haben, dass eine fiktive Zahl mit einer gewissen
> Wahrscheinlichkeit entweder 0 oder 1 ist (0 = kein Fehler,
> 1 = Fehler).

Dass bei deinem Verfahren alle Achsen unabhängig bewertet werden und die 
(von mir aus Unverständnis gestellte) Bedingung, die Routine nur alle p 
Achsen aufzurufen, gar nicht so elegant ist, habe ich erst jetzt 
erkannt.

Insofern werde ich dein Verfahren einsetzen. Sollten doch mal mehr 
Achsen pro Zug als erlaubt (Arraydimensionierung) auftreten (habe ich 
auf 6 hinaufgeschraubt, damit ist die Wahrscheinlichkeit im 
1:10.000-er-Bereich), lösch ich es. Darauf kommts dann auch nicht mehr 
an.

Herzlichen Dank
viel gelernt :-)

Michael Prader

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Nicht vergessen dass du den Zufallszahlengenerator irgendwann mal mit 
echtem Zufall initialisieren musst (srand), sonst treten die Ausfälle 
immer nach exakt der selben Anzahl von Vorbeifahrten auf. Am einfachsten 
kannst du das machen indem du schnellen Zähler mitlaufen lässt, und wenn 
der Zug über den Sensor fährt srand() mit diesem Zählerwert aufrufst. 
Genaugenommen könntest du dir das rand dann auch ganz sparen, und 
einfach nur den Zählerwert verwenden.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.