Forum: Mikrocontroller und Digitale Elektronik Zufallszahl [0;6], ATtiny 2313


von Mike (Gast)


Lesenswert?

Hallo!

Ich stehe vor folgendem Problem, und zwar möchte ich mit einem ATtiny 
2313 eine Zufallszahl generieren, die im bereich inkl. 0 bis inkl. 6 
liegt.
Der Hintergrund dazu ist eine Simulation eines Würfels.

Wenn ich das folgender maßen mache:
1
    double r;
2
    double zufallszahl;
3
    unsigned char min = 0;
4
    unsigned char max = 6;
5
6
    srand(TCNT1L);
7
    // zufallszahl aus [0,1[
8
    r = rand() / (RAND_MAX + 1.0);
9
10
    // transformation -> zufallszahl aus [a,b[
11
    zufallszahl = min + (max - min) * r;
12
    // Zuzfallszahl in general Purpose Register schreiben
13
    GPIOR0 = zufallszahl;

die Funktion srand() füttere ich dabei mit dem jeweiligen Registerinhalt 
der unteren 8 Bits der Timers1 welcher permanent läuft und dessen 
Overflow Interrupt disabled ist.

Der Algorithmus oben ist der wie wir ihn auf der Uni in C kennengelernt 
haben, allerdings haben wir da auf einem normalen PC programmiert -> die 
Register des AVR haben aber nur 8 Bit. und genau da liegt der Hund, den 
ich nicht ausgraben kann, nämlich in der allerletzten Anweisung meines 
obenstehenden Codes... AVRstudio sagt mir dann nämlich dass der Flash zu 
193% voll ist. Kommentiere ich die letzte Zeile aus, gibts kein problem. 
Klar, zufallszahl is ein double (=16Bit) und das register fasst nur 8...


Und jetzt eigentlich recht knapp gehalten: wie umgeh ich das? auf 
irgendeine art und weise muss des ja einfach funktionieren...

Bin schon auf eure Antworten gespannt und sag schon mal danke ;)
LG markus

von Karl H. (kbuchegg)


Lesenswert?

Mike schrieb:

> 2313 eine Zufallszahl generieren, die im bereich inkl. 0 bis inkl. 6
> liegt.
> Der Hintergrund dazu ist eine Simulation eines Würfels.

Was hast denn du für Würfel?


> die Funktion srand() füttere ich dabei mit dem jeweiligen Registerinhalt
> der unteren 8 Bits der Timers1 welcher permanent läuft und dessen
> Overflow Interrupt disabled ist.

Lass das.
srand wird am Anfang des Programms (oder vor dem ersten rand()) einmal 
aufgerufen und danach lässt man es in Ruhe.
Es ist wichtig, dass du srand() in Ruhe lässt. Ansonsten kannst du 
gleich deinen Registerinhalt als Zufallszahl nehmen.

Zufallszahlen charakterisieren sich dadurch, dass eine große Menge von 
Zahlen bestimmte statistische Merkmale aufbauen. Das geht aber nur, wenn 
du den Generator in Ruhe arbeiten lässt und nicht jedesmal neu 
eingreifst. Zufallszahlen sind heikle Gebilde und es ist sehr leicht, 
sich eine zufällige Verteilung durch gut gemeinte Manipulationen zu 
zerstören.


> 193% voll ist. Kommentiere ich die letzte Zeile aus, gibts kein problem.
> Klar, zufallszahl is ein double (=16Bit) und das register fasst nur 8...

Kommentierst du die Zuweisung aus, gibt es keinen Grund mehr 
'zufallszahl' zu berechnen. Dadurch fällt auch der Grund für diese 
Berechnung

    r = rand() / (RAND_MAX + 1.0);

weg und es bleibt keine Floating Point Arithmetik mehr übrig, die daher 
dann auch aus dem Programm rausfliegt.

Zieh die Formeln zusammen und verzichte komplett auf Floating Point. Du 
brauchst sie nicht.

   GPIOR0 = min + rand() % ( max - min );

Dir ist aber hoffentlich klar, dass das hier, genauso wie deine Version, 
keine gute Formel zur Berechnung von Zufallszahlen darstellt. Stichwort: 
Schubladenargument. Es gibt keine Möglichkeit m Socken in n Schubladen 
so zu verteilen, dass alle Schubladen gleich viele Socken enthalten. Es 
sei denn m ist ein ganzzahliges Vielfaches von n.
Den Fall hast du aber nicht. RAND_MAX ist typischerweise 32767 (du hast 
also 32768 Socken) während du 7 Schubladen hast. 32768 / 7 = 4681.1428 = 
nicht ganzzahlig. Einige Augenzahlen werden auf deinem Würfel 
signifikant häufiger auftreten als andere.

von Mike (Gast)


Lesenswert?

Hehe, ja gut da muss ich wohl zugeben dass ich nicht nachgedacht hab, 
solche Würfel besitze ich nicht ;)


Mit der Formel:
GPIOR0 = min + rand() % ( max - min );
Klappt es leider nicht... Ich habe mein programm jetzt reduziert (also 
alles auskommentiert bis auf:

Hauptoprogramm bis externer Interrupt, und in der ISR steht unmittelbar
PORTB = min + rand() % ( max - min );

Leider werden mir an meinen würfelaugenfürmig angeordneten LEDS nur 3 
(dafür aber unterschiedlich) angesteuert, die den Pins PB0, PB1 und PB2 
entsprechen...ahja, und das der komplette PORTB null ist kommt auch vor 
:/

Ich verstehs nicht... :(

von Flo (Gast)


Lesenswert?

Kannst es ja auch so machen, dass du während des Tastendrucks des 
Würflers eine Variable hochzählst (0 1 2 3 4 5 6 0 1 2 3 ...).
Das geht so schnell, dass der Benutzer unmöglich ausnutzen kann, dass 
einfach nur gezählt wird.
So funktionieren auch die guten alten Selbstbau-Würfel (mit Zähler und 
Decoder).

von Mike (Gast)


Lesenswert?

Flo schrieb:
> Kannst es ja auch so machen, dass du während des Tastendrucks des
>
> Würflers eine Variable hochzählst (0 1 2 3 4 5 6 0 1 2 3 ...).
>
> Das geht so schnell, dass der Benutzer unmöglich ausnutzen kann, dass
>
> einfach nur gezählt wird.
>


Die Variante habe ich schon probiert, und zwar im main(), und wenn der 
ext interrupt kommt steht irgendein wert im register. witzigerweise ist 
eine klare tendenz zu hohen zahlen zu bemerken, darum will ich mich ja 
nach einer vernünftigen variante umsehen :)


übrigens... kommt mir das nur so vor oder ist die Simulations Funktion 
von AVRstudio eher müll?

von Karl H. (kbuchegg)


Lesenswert?

Mike schrieb:

> Ich verstehs nicht... :(

Ich auch nicht.
Programm herzeigen, nicht beschreiben.

Edit:
Ähm. Wenn du die generierten Zahlen direkt ausgibst, ist klar, dass du 
nur BP0, PB1 und PB2 siehst.
Die Zahlen von 0 bis 6 sind nun mal in ihrer Bitdarstellung

    0      0000
    1      0001
    2      0010
    3      0011
    4      0100
    5      0101
    6      0110

Nur die Bits 0, 1 und 2 sind betroffen.

Du musst schon eine Umkodierung der Zahlen auf die Bitmuster machen, die 
die richtigen Leds aufleuchten lassen.

von Mike (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Edit:
>
> Ähm. Wenn du die generierten Zahlen direkt ausgibst, ist klar, dass du
>
> nur BP0, PB1 und PB2 siehst.
>
> Die Zahlen von 0 bis 6 sind nun mal in ihrer Bitdarstellung
>
>
>
>     0      0000
>     1      0001
>     2      0010
>     3      0011
>     4      0100
>     5      0101
>     6      0110
>
> Nur die Bits 0, 1 und 2 sind betroffen.
> Du musst schon eine Umkodierung der Zahlen auf die Bitmuster machen, die
> die richtigen Leds aufleuchten lassen.

Um Gottes Willen, STIMMT!!! Ohjeoje... jetzt habe ich gerade das ganze 
Progrmm eliminiert - manchmal ist abreißen und neu bauen besser als 
dranflicken...  Na gut, dann werd ich mich wieder melden und sagen wie's 
ausgegangen ist ;)
Aber heute eher nicht mehr.

LG

von Ralli (Gast)


Lesenswert?

Geht es hier um möglichst guten (Pseudo-)Zufall, oder um die Umsetzung
einer unangebrachten PC-Routine (real-Mathematik) auf einen µC?

Wo liegt das Problem bei hohen Zahlen, wenn man sie (z.B. durch
modulo 7) auf 0..6 begrenzt? Je höher, desto geringer die
Wahrscheinlichkeit, dass einer der Werte bevorzugt wird...

Falls das nicht einleuchtet:

ERST EIN BISSCHEN NACHDENKEN, dann loswursteln!

von Karl H. (kbuchegg)


Lesenswert?

Ralli schrieb:

> Wo liegt das Problem bei hohen Zahlen, wenn man sie (z.B. durch
> modulo 7) auf 0..6 begrenzt? Je höher, desto geringer die
> Wahrscheinlichkeit, dass einer der Werte bevorzugt wird...

Kommt drauf an, was du mit 'Wahrscheinlichkeit' meinst.
Je höher RAND_MAX, desto kleiner wird die prozentuale Abweichung der 
generierten Verteilung von der idealen Gleich-Verteilung. Aber 
verschwinden wird sie nie, solange m % n != 0

von Mike (Gast)


Lesenswert?

1
void zufallszahl()
2
{    unsigned char min = 1;
3
    unsigned char max = 6;
4
    unsigned char erg = 0;
5
6
    erg = (rand() % (max-min+1) + min);
7
8
    switch(erg)
9
    {  case 1: GPIOR0 = 0b11000000; break;
10
      case 2: GPIOR0 = 0b10100100; break;
11
      case 3: GPIOR0 = 0b11100100; break;
12
      case 4: GPIOR0 = 0b10101101; break;
13
      case 5: GPIOR0 = 0b11101101; break;
14
      case 6: GPIOR0 = 0b10111111; break;
15
         
16
    }
17
    
18
  }
So funktioniert es ganz gut!
Echt, 2 fragen bisher hier gestellt, und 2 mal superschnelle 
zielgerichtete antworten bekommen! super :)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> GPIOR0 = min + rand() % ( max - min );

Im Intervall [a,b] mit ganzzahligen
Intervallgrenzen hat's b-a+1 ganze Zahlen.

Ergo:
GPIOR0 = min + rand() % (max - min +1);

> Den Fall hast du aber nicht. RAND_MAX ist typischerweise 32767 (du hast
> also 32768 Socken) während du 7 Schubladen hast. 32768 / 7 = 4681.1428 =
> nicht ganzzahlig. Einige Augenzahlen werden auf deinem Würfel
> signifikant häufiger auftreten als andere.

Naja, signifikant...? Notfalls schleift man solange wie
 wert >= RAND_MAX - (RAND_MAX % 7)

von Sohn von Gosar (Gast)


Lesenswert?

Du wirst bei so einer Routine durch die CPU nie "echte" Zufallszahlen 
generieren können, das Thema wurde schon x mal durchgekaut.

Aber du hast doch 2 externe willkürlich und nicht kalkulierbare 
Ereignisse, nämlich den Zeitpunkt wann der Taster "jetzt würfeln" 
gedrückt wird und wann er wieder losgelassen wird. Lass bei beiden 
Ereignissen einen Timer abfragen und die Ergebnisse in einen 
entprechenden Algoritmus einfließen. An die Gleichverteilung der so 
erzeugten Zufallszahlen kommst du mit einer reinen µC Berechnung nie 
heran.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
> Karl heinz Buchegger schrieb:
>> GPIOR0 = min + rand() % ( max - min );
>
> Im Intervall [a,b] mit ganzzahligen
> Intervallgrenzen hat's b-a+1 ganze Zahlen.

Yep. Da hab ich nicht nachgedacht bzw. zu schnell getippt.

>> Den Fall hast du aber nicht. RAND_MAX ist typischerweise 32767 (du hast
>> also 32768 Socken) während du 7 Schubladen hast. 32768 / 7 = 4681.1428 =
>> nicht ganzzahlig. Einige Augenzahlen werden auf deinem Würfel
>> signifikant häufiger auftreten als andere.
>
> Naja, signifikant...?

In einem Spielcaslno wäre so was tödlich. Fürs Haus, nicht für die 
Spieler.

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.