www.mikrocontroller.net

Forum: Compiler & IDEs Zufallszahlen: Frage und Bemerkung


Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht 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:

#define PERCENTAGE 200  // Gesamtzahl an Achsen, bis Ereignis wieder auftritt
#define MAX_ANZAHL 3 // maximale Anzahl an Fehlern pro einzelnen Zug

unsigned int axle_count; //zählt die Achsen eines einzelnen vorbeifahrenden Zuges
unsigned int global_axles; //zählt Achsen aller Züge

unsigned char fehleranzahl;  // Anzahl an Fehlern pro untersuchten Zug
unsigned int fehler[MAX_ANZAHL];  //Array, wo Fehler gespeichert werden

...

if (global_axles >= PERCENTAGE)       // Ereignis tritt ein
{
    /* Erzeuge Fehleranzahl so lange, bis kleiner der Maximalanzahl */
    while( fehleranzahl > MAX_ANZAHL)
        fehleranzahl = rand() / rand();  // Bruch, um Zahl zu verkleinern

    /* generiere Fehler innerhalb Zuglänge; 0 ist nicht zugelassen */
    for (uint8_t j=0; j< fehleranzahl; j++)
    {
       while( (j > axle_count) || (fehler[j] == 0))
          fehler[j] = rand();
    }
    global_axles = 0;  // Setze Zähler wieder auf null

} else fehleranzahl = 0;

...


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

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht 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
do {
   fehler = rand();
} while (fehler > axle_count)

Autor: Maximilian K. (laplace)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Autsch, vielen Dank für den Hinweis, Maximilian.

Lg
Michael

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sven K. (skasko)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Sven,

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

#define PERCENTAGE 200  // Gesamtzahl an Achsen, bis Ereignis wieder auftritt: Prozentualität 0.5 %
#define MAX_ANZAHL 3 // maximale Anzahl an Fehlern pro einzelnen Zug

unsigned int axle_count; //zählt die Achsen eines einzelnen vorbeifahrenden Zuges
unsigned int global_axles; //zählt Achsen aller Züge

unsigned char fehleranzahl;  // Anzahl an Fehlern pro untersuchten Zug
unsigned int fehler[MAX_ANZAHL];  //Array, wo Fehler gespeichert werden

unsigned char total_events, double_event, triple_event;  // Zähler für alle Ereignisse, für solche mit 2 Fehlern und 3 Fehlern

...

if (global_axles >= PERCENTAGE)       // Ereignis tritt ein
{
    if (total_events >= 0xFF)
    {
      total_events = 0; double_event = 0; triple_event = 0;
    }

    total_events++;  // erhöhe Gesamtzahl der Ereignisse

    /* Erzeuge Fehleranzahl so lange, bis kleiner der Maximalanzahl */
    do {
        fehleranzahl = rand() % (MAX_ANZAHL +1);  // Bruch, um Zahl zu verkleinern
    } while( (fehleranzahl > MAX_ANZAHL) || (fehleranzahl == 0))  // Fehleranzahl 0 wird nicht zugelassen
    
    switch (fehleranzahl)
    {
        case 1: break;  // 1 wird immer zugelassen

        case 2: 
           if (double_event >= (total_events / 2)) fehleranzahl = 0;
           double_event++;
           break;  // nur in Hälfte der Fälle zugelassen

        case 3: if (triple_event >= (total_events / 3)) fehleranzahl = 0;
           triple_event++;
           break;  // nur in einem Drittel der Fälle zugelassen
        
        default: fehleranzahl = 0; break;  // zur Sicherheit
    }


    /* generiere Fehler innerhalb Zuglänge; Achse 0 ist nicht zugelassen 
       Die Wahrscheinlichkeit, dass zwei Fehler hintereinander gleich sind (an der selben Achse) wird nicht berücksichtigt
    */
    for (uint8_t j=0; j< fehleranzahl; j++)
    {
        do {
          fehler[j] = rand();
        } while( (fehler[j] > axle_count) || (fehler[j] == 0))
    }

    global_axles = 0;  // Setze Zähler wieder auf null
    
    

} else fehleranzahl = 0;

...


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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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;
  }

Autor: Sven K. (skasko)
Datum:

Bewertung
0 lesenswert
nicht 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.
    switch (fehleranzahl)
    {
        case 1: break;  // 1 wird immer zugelassen

        case 2: 
           if (double_event < 2))
           {           
             fehleranzahl = 0;
             double_event++;
           }
           else
           {
             double_event = 0;
           }
           break;  // nur in Hälfte der eintretenden Fälle zugelassen

        case 3:           
           if (triple_event < 3))
           {           
             fehleranzahl = 0;
             triple_event++;
           }
           else
           {
             triple_event = 0;
           }
           break;  // nur in einem Drittel der auftretenden Fälle zugelassen
        
        default: fehleranzahl = 0; break;  // zur Sicherheit
    }

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.

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Sven K. (skasko)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jo da hast Du natürlich Recht, sorry.

Sven

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok.

Vielen Dank für die wertvolle Hilfe!

Grüße
Michael

Autor: Michael Prader (praderbz)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Andreas Schwarz (andreas) (Admin) Benutzerseite Flattr this
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.