Forum: PC-Programmierung Echte Zufallszahl erzeugen?


von Mark M. (mom-jovi)


Lesenswert?

Hallo,
mein Programm mag nach
1
#include <time.h>
2
3
srand(time(0));
mit
1
int zahl=rand();
immernoch keine immer unterschiedlichen Zufallszahlen ausspucken. Bei 
häufigem Aufrufen von rand() scheint es sich auch aufzuhängen. Ich weiß, 
dass auch nach srand immer nur etwa jede Sekunde eine "neue" Zahl 
möglich ist. Im Debugger läuft deshalb auch alles problemlos, denn da 
sind ja die Zeitabstände wesentlich größer und das Programm hängt sich 
auch nicht auf.
Aber wie macht man denn nun eine "echte" Zufallszahl, auch wenn es das 
beim Computer nicht gibt?

von julian (Gast)


Lesenswert?

auch wenns nur bedingt hilfreich ist: ein wirklich guter 
Zufallsgenerator:
http://gamesbyemail.com/News/DiceOMatic

von Sam .. (sam1994)


Lesenswert?

Mark M. schrieb:
> Bei
> häufigem Aufrufen von rand() scheint es sich auch aufzuhängen. Ich weiß,
> dass auch nach srand immer nur etwa jede Sekunde eine "neue" Zahl
> möglich ist. Im Debugger läuft deshalb auch alles problemlos, denn da
> sind ja die Zeitabstände wesentlich größer und das Programm hängt sich
> auch nicht auf.

Bei meinem selbstgeproggten Spielen verwende ich immer rand() und es 
hängt sich nie auf. Es wird auch z.T mehrere hundert mal in der Sekunde 
aufgerufen. Da wird wohl eher ein Fehler in deinem Programm sein.

von Rolf M. (rmagnus)


Lesenswert?

Mark M. schrieb:
> mit
> int zahl=rand();
> immernoch keine immer unterschiedlichen Zufallszahlen ausspucken.

[...]

> Bei häufigem Aufrufen von rand() scheint es sich auch aufzuhängen.

Das rand() deiner C-Bilbiothek scheint ziemlich kaputt zu sein, oder 
dein Programm enthält einen Fehler.

> Ich weiß, dass auch nach srand immer nur etwa jede Sekunde eine "neue"
> Zahl möglich ist.

Wie kommst du denn darauf?

> Im Debugger läuft deshalb auch alles problemlos, denn da sind ja die
> Zeitabstände wesentlich größer und das Programm hängt sich auch nicht
> auf.

Es gibt jede Menge möglicher Gründe, warum sich das Programm im Debugger 
nicht aufhängt. Die Zeitabstände zwischen Aufrufen von rand() gehören da 
aber eher nicht dazu.

> Aber wie macht man denn nun eine "echte" Zufallszahl, auch wenn es das
> beim Computer nicht gibt?

Erstmal solltest du rausfinden, warum rand() bei dir nicht funktioniert. 
Wozu brauchst du denn eine echte Zufallszahl? Es gibt manche Computer, 
die einen Hardware-Zufallszahlengenerator eingebaut haben, der den Wert 
z.B. über das termische Rauschen eines Transistors ermittelt.

von Klaus W. (mfgkw)


Lesenswert?

Mark M. schrieb:
> Ich weiß,
> dass auch nach srand immer nur etwa jede Sekunde eine "neue" Zahl
> möglich ist.

Wer hat dir das erzählt?
Hat der auch vom Weihnachtsmann gesprochen und von komischen Stimmen?

von ... (Gast)


Lesenswert?

Aus deiner Aussage "immer unterschiedliche Zahlen" entnehme ich, das du 
vielleicht eine Liste mir Liedern abspielen willst und sich kein Lied 
wiederholen soll oder sowas ähnliches. Also erstmal ist es bei 
zufälligen Zahlen auf keinen Fall so, dass sie sich nciht wiederholen. 
Das die Zahlen gleichverteilt sind, gilt nur für unendlich viele Aufrufe 
der Funktion. Und da es Pseudozufallszahlen stimmt auch das mit der 
Gleichverteilung nur bedingt.
Wenn du wirklich eine Playliste oder sowas in der Art machen willst, 
dann schreibe die die Titel bzw. die Zahlen von z.B. 1 bis 100 in eine 
Liste. Dann sortierst du die Liste um, indem du zufällig zwei Elemente 
tauschst. Also so in der Art:

for (i = 0; i < 100; i++)
{
  int idx1 = rand() % 100;
  int idx2 = rand() % 100;

  int sp = liste[idx1];
  liste[idx1] = liste[idx2];
  liste[idx2] = sp;
}

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Hier ein sehr vielversprechendes Verfahren für die Bestimmung von 
Zufallszahlen, allerdings ist etwas Hardware erforderlich ...

http://www.lavarnd.org/

von Udo S. (urschmitt)


Lesenswert?

Wie benutzt Du die beiden Funktionen?
Wichtig ist daßß man den Pseudozufalsszahlengenerator EIN mal 
initialisiert
z.B. mit

time_t t;
time(&t);
srand((unsigned int)t);

danach kann man die Funktion so oft benutzen wie man will!
Wichtig: Mit dem selben Startwert bekommt amn immer die identische 
Zufallszahlenfolge, es ist halt ein Pseudozufallsgenerator, aber die 
Folge an sich ist relativ gut verteilt!
Deshalb muss man den Generator auch bei jedem Start durch eine neue Zahl 
(z.B Zeit) neu initialisieren.

von Udo S. (urschmitt)


Lesenswert?

Wenn mich meine eingerosteten C kenntnisse nicht täuschen sollte man 
beim Aufruf von time korrekterweise nicht time(0); sondern time(NULL); 
benutzen, um kenntlich zu machen, daß ein Zeiger übergeben wird (bzw 
halt der NULL Pointer)
Ausserdem sollte das entsprechende include nicht fehlen.
Ich glaube für die Zufallszahlen ist das stdlib.h.

von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:
> Wenn mich meine eingerosteten C kenntnisse nicht täuschen sollte man
> beim Aufruf von time korrekterweise nicht time(0); sondern time(NULL);
> benutzen, um kenntlich zu machen, daß ein Zeiger übergeben wird (bzw
> halt der NULL Pointer)

Psst.
NULL ist nichts anderes als eine Verpackung für 0
Und zwar für den Integer 0!
Und keinesfalls für einen void Pointer

(Übrigens sehr zu meinem Leidwesen)

von Karl H. (kbuchegg)


Lesenswert?

Rolf Magnus schrieb:

> Erstmal solltest du rausfinden, warum rand() bei dir nicht funktioniert.

Jede Wette es funktioniert perfekt.
Nur hat ihm keiner erzählt, dass man srand() nur EINMAL aufruft und dann 
lässt man den Generator in Ruhe seinen Job machen: bei jedem Aufruf von 
rand() eine Zahl zu liefern.

von Frank (Gast)


Lesenswert?

Ein schöner Algorithmus, um eine Zufallszahl zu erzeugen:

unsigned int m_z;
unsigned int m_w;

void InitRandomNumber(void)
{
  m_w = 521288629;
  m_z = 362436069;
}

USHORT GetRandomNumber(void)
{
  m_z = 36969 * (m_z & 65535) + (m_z >> 16);
  m_w = 18000 * (m_w & 65535) + (m_w >> 16);

  return(USHORT)(((m_z << 16) + m_w) & 0x0000ffff);
}

Ich habe hier auf USHORT gecastet, weil ich nur 16-Bit Zahlen gebraucht 
habe. Kannst Du ja entsprechend anpassen. Der Algorithmus erzeugt 
zumindest "bessere" Zufallszahlen als rand().

von Valentin B. (nitnelav) Benutzerseite


Lesenswert?

@Frank:
Siehe hier: http://xkcd.com/221/

@ Karl Heinz Buchegger:
Stimmt wahrscheinlich!

Mit freundlichen Grüßen,
Valentin Buck

von Noch ein Dummschwätzer (Gast)


Lesenswert?

>zumindest "bessere" Zufallszahlen als rand().

Und ich wäre ein besserer Trainer als Jogi Loew und ein besserer Kanzler 
als Merkel.

von Karl H. (kbuchegg)


Lesenswert?

Frank schrieb:
> Ein schöner Algorithmus, um eine Zufallszahl zu erzeugen:

Wo hast du ihn her?

> habe. Kannst Du ja entsprechend anpassen. Der Algorithmus erzeugt
> zumindest "bessere" Zufallszahlen als rand().

Kommt immer drauf an, was man unter besser versteht.
Zufallszahlen sind statistisch gar nicht so leicht zu fassen. Aussagen 
über die Qualität eines Generators kann man aber nur auf statistischer 
Basis erhalten.

von Frank (Gast)


Lesenswert?

Ach da sind sie ja wieder. Die Dummschwätzer. Mach' Dich zunächst im 
Internet über diesen Algorithmus schlau.

von Dussel (Gast)


Lesenswert?

Da kann ich mir das doch nicht verkneifen: http://xkcd.com/221/

von Frank (Gast)


Lesenswert?

@ Karl heinz Buchegger

http://en.wikipedia.org/wiki/Random_number_generation

http://www.codeproject.com/KB/recipes/SimpleRNG.aspx?msg=3459566

und weitere 24000 Links in Google.

Irgendwann habe ich auch einmal eine umfassende Abhandlung über diesen 
Algorithmus im Internet gelesen - finde ich auf die Schnelle aber nicht 
mehr.
Es wurde aber gezeigt, dass die Zahlen zufälliger sind, als mittels 
rand() erzeugt.

von Stefan Salewski (Gast)


Lesenswert?

>Mach' Dich zunächst im
>Internet über diesen Algorithmus schlau.

Warum sollte ich das tun?

Du gibst keine Referenz an, bezeichnest ihn als "zumindest "bessere" 
Zufallszahlen als rand()". Wobei ich kaum glaube, das jede (C) rand() 
Implementierung identisch ist.

Generierung von Zufallszahlen ist eine komplizierte Wissenschaft, soviel 
weiß ich immerhin, und das genügt mir derzeit.

von Ronny T. (Gast)


Lesenswert?

Es kommt immer darauf an, zu was man die Zufallszahl braucht - für ein 
Spiel reicht eine einfache SW Generierung sicher aus. Aber wenn es um 
Krypto und Security geht, dann sieht die Sache wieder ganz anders aus, 
da kriegt man ganz schnell seine so wunderbar ausgeklügelte, 
selbstgebastelte Lösung ganz schnell um die Ohren gehauen...
(Nicht umsonst hat ST seiner neuen F2x Controller Reihe nun einen in 
Hardware gegossenen analogen Zufallszahlengenerator spendiert.)

von Udo S. (urschmitt)


Lesenswert?

Karl heinz Buchegger schrieb:
> Psst.
> NULL ist nichts anderes als eine Verpackung für 0
> Und zwar für den Integer 0!
> Und keinesfalls für einen void Pointer
Wie gesagt, die Kenntnisse sind etwas angerostet. Trotzdem weisst einem 
das NULL darauf hin daß es ein Pointer sein soll und kein int.
K&R sagt dazu: "Die symbolische Konstante NULL wird benutzt um 
hervorzuheben, daß dies ein spezieller Wert für einen Zeiger ist".
Obwohl, soweit ich mich erinnere NULL nicht immer als ((void *)0) 
definiert ist, sondern auch manchmal einfach als 0.

Karl heinz Buchegger schrieb:
> Jede Wette es funktioniert perfekt.
> Nur hat ihm keiner erzählt, dass man srand() nur EINMAL aufruft und dann
> lässt man den Generator in Ruhe seinen Job machen:
Doch, hatte ich 2 Postings vorher schon gemacht :-)
Schöne Weihnachten
Udo

von Frank (Gast)


Lesenswert?

@ Ronny T.
full ACK

von Karl H. (kbuchegg)


Lesenswert?

Frank schrieb:
> @ Karl heinz Buchegger
>
> http://en.wikipedia.org/wiki/Random_number_generation

Danke für die Links.
Aber ich weiß schon was es mit Zufallszahlen auf sich hat und auch wo 
die Probleme liegen.


> Es wurde aber gezeigt, dass die Zahlen zufälliger sind, als mittels
> rand() erzeugt.

Aha.
Da der C-Standard einem Entwickler nicht vorschreibt, welchen Generator 
er zu benutzen hat, ... woher weißt du, dass in der Runtime-Lib des 
Fragestellers nicht gerade dieses Verfahren benutzt wird?

Aber es stimmt schon.
Frühere rand() Implementierungen waren meistens nicht besonders gut. 
Daher wurde oft von der üblichen Methode

      a = rand() % Maximum

abgeraten, weil man wusste, dass die 'kleinen' Bits kein gutes 
statistisches Zufallsverhalten zeigten.

Für den Hausgebrauch gut genug. Für Ca*sinos allerdings zu schwach.
Allerdings sind einige Aussagen des Fragestellers dergestalt, dass die 
methodischen Probleme von rand() eher nicht zu seinen Problemen zählen 
dürften.

von Frank (Gast)


Lesenswert?

@ Karl heinz Buchegger

Da gebe ich Dir freundlicherweise einen Link über die 
Multiply-with-carry Methode des Mathematikers George Marsaglia. Und 
Deine Antwort ist: Aber ich weiß schon was es mit Zufallszahlen auf sich 
hat und auch wo
die Probleme liegen. (???)

Da gebe ich Hilfestellung und poste einen Algorithmus, der allgemein 
unter Softwerkern, die sich mit Zufallszahlen beschäftigen, bekannt sein 
sollte. Und ich soll mich auch noch dafür rechtfertigen?

von Karl H. (kbuchegg)


Lesenswert?

Frank schrieb:
> @ Karl heinz Buchegger
>
> Da gebe ich Dir freundlicherweise einen Link über die
> Multiply-with-carry Methode des Mathematikers George Marsaglia. Und
> Deine Antwort ist: Aber ich weiß schon was es mit Zufallszahlen auf sich
> hat und auch wo
> die Probleme liegen. (???)
>
> Da gebe ich Hilfestellung und poste einen Algorithmus, der allgemein
> unter Softwerkern, die sich mit Zufallszahlen beschäftigen, bekannt sein
> sollte. Und ich soll mich auch noch dafür rechtfertigen?#

Nein, du sollst dich nicht dafür rechtfertigen.
Du sollst überlegen, inwiefern dein Generator das Problem des 
Fargestellers lösen kann.
Seni Problem ist sicher nicht die mangelnde Qualität von rand() (über 
die man debattieren kann). Sein Problem ist, dass er rand() offenbar 
nicht richtig benutzen kann!

von Frank (Gast)


Lesenswert?

> @ Karl heinz Buchegger

Ich soll überlegen? Soll es doch der Fragesteller tun. Oder Google 
bemühen.

Tschüüüüüüüüüüüüüüüs.

von Sam .. (sam1994)


Lesenswert?

Udo Schmitt schrieb:
> Obwohl, soweit ich mich erinnere NULL nicht immer als ((void *)0)
> definiert ist, sondern auch manchmal einfach als 0.

Geh mal in VC++ bei NULL auf Gehe zu Definition

von Klaus W. (mfgkw)


Lesenswert?

In C ist m.W. NULL ein void*, aber in C++ jedenfalls ist es die int-0.

von (prx) A. K. (prx)


Lesenswert?

Mark M. schrieb:

> Aber wie macht man denn nun eine "echte" Zufallszahl, auch wenn es das
> beim Computer nicht gibt?

Die üblichen Software-Implementierungen erzeugen deterministische 
Zahlenfolgen, was für mancherlei Anwendungen ausreicht, aber für 
kryptographische Verfahren allenfalls, wenn der Startwert gut zufällig 
ist (die beliebte Uhrzeit ist es nicht).

Echt wird es, indem physikalische Vorgänge zufälligen Charakters 
gemessen werden, wie Rauschen. Trivial ist das aber nicht unbedingt, 
denn man sollte schon sicher sein, dass man es wirklich mit zufälligem 
Rauschen zu tun hat und nicht mit irgendwelchen Störsignalen.

Dem annähern kann man sich, indem man diverse nicht miteinander 
korrelierte Faktoren zusammenführt, wie irgendwelche Daten und 
Wartezeiten aus Device Drivern und interaktiven Aktionen. In Linux 
findet man das in /dev/random.

von Dussel (Gast)


Lesenswert?

> Aber wie macht man denn nun eine "echte" Zufallszahl, auch wenn es das
> beim Computer nicht gibt?
TrueCrypt erzeugt den Schlüssel (aus Zufallszahlen) unter Anderem mit 
Hilfe der Benutzereingaben. Der Benutzer muss möglichst lange seine Maus 
bewegen und verändert mit jedem Bewegungsschritt den Schlüssel. Je 
nachdem, was der Benutzer mit der Maus macht, ist das ziemlich 
'zufällig'.

von Frank Morster (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> In C ist m.W. NULL ein void*, aber in C++ jedenfalls ist es die int-0.

Eine spannende Theorie.

http://www.c-faq.com/null/macro.html

von Klaus W. (mfgkw)


Lesenswert?

Wenn ich praktischerweise in meine /usr/include/linux/stddef.h
reinschaue, sehe ich:
1
#ifndef _LINUX_STDDEF_H
2
#define _LINUX_STDDEF_H
3
4
5
#undef NULL
6
#if defined(__cplusplus)
7
#define NULL 0
8
#else
9
#define NULL ((void *)0)
10
#endif
11
12
13
#endif

von (prx) A. K. (prx)


Lesenswert?

Zu NULL, per Standard:
C: implementation defined.
C++: ebenso, aber: possible definitions include 0 and 0L, but not 
(void*)0.

von Mark M. (mom-jovi)


Lesenswert?

Rolf Magnus schrieb:
>> Ich weiß, dass auch nach srand immer nur etwa jede Sekunde eine "neue"
>> Zahl möglich ist.
>
> Wie kommst du denn darauf?

Hier Beitrag "zufallszahl mit rand() erzeugen" steht:
Rolf Magnus schrieb:
>>Das Problem ist, dass bei jedem Aufruf der Funktion der gleiche
>> zufallswert erzeugt wird und ich benötige unterschiedliche Werte
>
> Vermutlich nicht bei jedem Aufruf, sondern immer genau eine Sekunde lang
> bzw bis time() einen neuen Wert liefert.

Das ist auch mein Problem. Wenn ich die Funktion in ner while-Schleife 
mehrmals durchlaufen lasse, kommt mehrmals direkt hintereinander das 
gleiche. Wenn ich dazwischen andere Aktionen packe, dann nicht.
Ich hab zum Bespiel die Eingabe einer beliebigen Taste dazwischen 
gemacht, das läuft auch, hängt sich aber eben nach dem 4., 5. mal auf.
1
struct vokabel vokabel_aus_datei(char datei[])
2
{
3
    int zeilenzahl=zeilen(datei);            // zeilenzahl/7==anzahl vokabeln
4
    srand(time(0));
5
    int nummer=0;
6
    do
7
    {
8
         nummer=rand()%(zeilenzahl/7); // nummer==zahl zwischen 0 und anzahl_vokabeln-1
9
    } while(vorigeChk(nummer)==1);
10
11
    int zeilennummer=nummer*7+1; // zeilennummer, ab der die Vokabel steht
12
13
    vorigeApp(nummer); // Zufallszahl speichern, damit sie nicht mehr so schnell vorkommt
14
15
    FILE* stream;
16
    stream=fopen(datei,"r");
17
    fseek(stream,0,SEEK_SET);  // an den Anfang springen
18
    long int cntln=0; // zählt die Zeilen
19
    char zeichen=fgetc(stream);
20
    while(zeichen!=EOF)
21
    {
22
        if(zeichen=='\n') cntln++;
23
        if(cntln==zeilennummer) break; // zufälliges Wort gefunden, Zeiger steht vor diesem
24
        zeichen=fgetc(stream);
25
    } // am Ende steht der Zeiger vor dem gesuchten Wort
26
27
    long int akt_pos=ftell(stream)-2;
28
    fclose(stream);
29
30
    struct vokabel neu; // neue Vokabel angelegt, sie wird im Folgenden beschrieben
31
    int lesen=0;
32
    while(lesen<8)
33
    {
34
        long int cntch=cntcharacter(datei,akt_pos); // Zeichen zählen
35
36
        stream=fopen(datei,"r");
37
        fseek(stream,akt_pos,SEEK_SET);
38
        switch(lesen)
39
        {
40
            case 0: break;
41
            case 1: fgets(neu.wort,cntch,stream);  
42
            break;
43
            case 2: fgets(neu.bed_1,cntch,stream);
44
            break;
45
            case 3: fgets(neu.bed_2,cntch,stream);
46
            break;
47
            case 4: fgets(neu.bed_3,cntch,stream);
48
            break;
49
            case 5: fgets(neu.bed_4,cntch,stream);
50
            break;
51
            case 6: fgets(neu.bed_5,cntch,stream);
52
            break;
53
            case 7: break;
54
            default: fehler("beim Einlesen von Datei in struct!");
55
        }
56
        fclose(stream);
57
        lesen++;
58
        akt_pos=akt_pos+cntch+1; // 1 für den Zeilenumbruch
59
    }
60
61
    return neu;
62
}

von DirkB (Gast)


Lesenswert?

Wie Karl heinz Buchegger schon gestern (22.12.2010 15:52) schrieb:

srand NUR einmal am Anfang vom Programm aufrufen.

rand() ermittelt den neuen Wert aus dem letzten Zufallswert.
Wenn du mit srand() jedes mal den  Startwert für rand() neu setzt kann 
das nichts werden.

Dann musst du den Rückgabewert von fopen auch prüfen ob es geklappt hat.

Ein Aufruf von fopen in der Funktion sollte auch reichen.

von Johnny B. (johnnyb)


Lesenswert?

Einer der besten Pseudozufallsgeneratoren heisst "Mersenne-Twister MT 
19937":
http://de.wikipedia.org/wiki/Mersenne-Twister

Sourcecode befindet sich im Wikipedia Artikel.
Allerdings ist natürlich auch hier ein gescheiter Startwert nötig.

von Mark M. (mom-jovi)


Lesenswert?

DirkB schrieb:
> srand NUR einmal am Anfang vom Programm aufrufen.

Wenn ich das mache, hängt er sich auf.

DirkB schrieb:
> Ein Aufruf von fopen in der Funktion sollte auch reichen.

Sicher? In
1
long int cntcharacter(FILE* datei, long int akt_pos);
 wird die Datei aber auch gelesen. Kann ich den Stream dann wie ein 
Zeiger übergeben?

von Karl H. (kbuchegg)


Lesenswert?

Nachdem die Sache mit dem srand (nur EIN Aufruf am Programmstart) jetzt 
geklärt ist: Was hast du hier eigentlich vor?
1
struct vokabel vokabel_aus_datei(char datei[])
2
{
3
    int zeilenzahl=zeilen(datei);            // zeilenzahl/7==anzahl vokabeln
4
    srand(time(0));
5
    int nummer=0;
6
    do
7
    {
8
         nummer=rand()%(zeilenzahl/7); // nummer==zahl zwischen 0 und anzahl_vokabeln-1
9
    } while(vorigeChk(nummer)==1);
10
11
    int zeilennummer=nummer*7+1; // zeilennummer, ab der die Vokabel steht

Wenn du eien Sequenz von Zahlen in einem bestimmten Bereich benötigst, 
von denen garantiert keine doppelt vorkommt und die bunt durcheinander 
gewürfelt sind, dann macht man das so:

Man erzeugt sich ein Array mit den Zahlen von 0 bis zur Obergrenze

  for( i = 0; i < ANZAHL; ++i )
    arr[i] = i;

und dann würfelt man die durcheinander, indem man zufällig immer 2 
Elemente vertauscht

  for( i = 0; i < ANZAHL; ++i ) {
    randInd = rand() % ANZAHL;
    tmp = arr[i];
    arr[i] = arr[randInd];
    arr[randInd] = tmp;
  }

und erhält dann ein Array mit der gewünschten Länge, in der die Zahlen 
von 0 bis ANZAHL-1 in einer zufälligen Reihenfolge angeordnet sind.
Man braucht dann nur noch 1 Element nach dem nächsten sich dort abholen 
und kann das dann zb zur Auswahl der nächsten Vokabel benutzen.

von Karl H. (kbuchegg)


Lesenswert?

Mark M. schrieb:
> DirkB schrieb:
>> srand NUR einmal am Anfang vom Programm aufrufen.
>
> Wenn ich das mache, hängt er sich auf.

Wo genau?
Wahrscheinlich hier
1
    do
2
    {
3
         nummer=rand()%(zeilenzahl/7); // nummer==zahl zwischen 0 und anzahl_vokabeln-1
4
    } while(vorigeChk(nummer)==1);

wenn du prüfst ob diese Zahl schon vorgekommen ist.
Zeig doch mal den Mechanismus der da dahinter steckt oder schmeiss ihn 
gleich über Board. Wenn du die Sache mit dem srand korrigiert hast, 
brauchst du ihn nicht mehr (ist ja nicht schlimm, wenn ab und zu 
dieselbe Vokabel 2 mal hintereinander abgefragt wird. Das wird nicht 
sehr oft vorkommen, sobald du srand() richtig einsetzt)

von Karl H. (kbuchegg)


Lesenswert?

Mark M. schrieb:
> DirkB schrieb:
>> srand NUR einmal am Anfang vom Programm aufrufen.
>
> Wenn ich das mache, hängt er sich auf.
>
> DirkB schrieb:
>> Ein Aufruf von fopen in der Funktion sollte auch reichen.
>
> Sicher? In
>
1
long int cntcharacter(FILE* datei, long int akt_pos);
 wird die
> Datei aber auch gelesen. Kann ich den Stream dann wie ein Zeiger
> übergeben?


Du liest mir ehrlich gesagt etwas zuviel von den Dateien ein.

Gibt es einen Grund, warum du den Vokabelvorrat nicht einfach im 
Speicher halten willst?

Wenn eine Vokabel dazukommt, wird sofort alles auf die Datei gesichert, 
aber ansonsten wird alles im Speicher abgewickelt.

von Karl H. (kbuchegg)


Lesenswert?

Ja, definitiv.
Du hast dich da mächtig verrant.
1
   int lesen=0;
2
    while(lesen<8)
3
    {
4
        long int cntch=cntcharacter(datei,akt_pos); // Zeichen zählen
5
6
        stream=fopen(datei,"r");
7
        fseek(stream,akt_pos,SEEK_SET);
8
        switch(lesen)
9
        {
10
            case 0: break;
11
            case 1: fgets(neu.wort,cntch,stream);  
12
            break;
13
            case 2: fgets(neu.bed_1,cntch,stream);
14
            break;
15
            case 3: fgets(neu.bed_2,cntch,stream);
16
            break;
17
            case 4: fgets(neu.bed_3,cntch,stream);
18
            break;
19
            case 5: fgets(neu.bed_4,cntch,stream);
20
            break;
21
            case 6: fgets(neu.bed_5,cntch,stream);
22
            break;
23
            case 7: break;
24
            default: fehler("beim Einlesen von Datei in struct!");
25
        }
26
        fclose(stream);
27
        lesen++;
28
        akt_pos=akt_pos+cntch+1; // 1 für den Zeilenumbruch
29
    }

das alles ist hauptsächlich eines:
maximale Beschäftigungstherapie für Rechner und Festplatte.

aber ansonsten ziemlich sinnlos und maximal kompliziert
1
    FILE* stream;
2
    stream=fopen(datei,"r");
3
    fseek(stream,0,SEEK_SET);  // an den Anfang springen
wenn du eine Datei zum Lesen öffnest, steht der Filepointer sowieso am 
Anfang. Da musst du nicht extra hinseeken

1
    long int cntln=0; // zählt die Zeilen
2
    char zeichen=fgetc(stream);
3
    while(zeichen!=EOF)
4
    {
5
        if(zeichen=='\n') cntln++;
6
        if(cntln==zeilennummer) break; // zufälliges Wort gefunden, Zeiger steht vor diesem
7
        zeichen=fgetc(stream);
8
    } // am Ende steht der Zeiger vor dem gesuchten Wort
kann man so machen. Aber wozu? fgets liest perfekt eine Zeile. Wenn du 
bis zur i-ten Zeile nach vorne willst, dann lies einfach i Zeilen 
mittels fgets ein

1
    long cntln = 0;
2
3
    while( cntln < zeilennummer && fgets( dummy, sizeof( dummy ), stream ) )
4
      cntln++;

jetzt steht der FilePointer am Anfang der gewünschten Zeile, und da 
jetzt einfach 7 Zeilen lesen
1
    fgets( neu.wort, sizeof(neu.wort), stream );
2
    fgets( neu.bed_1, sizeof(neu.bed1), stream );
3
    fgets( neu.bed_2, sizeof(neu.bed2), stream );
4
    fgets( neu.bed_3, sizeof(neu.bed3), stream );
5
    fgets( neu.bed_4, sizeof(neu.bed4), stream );
6
    fgets( neu.bed_5, sizeof(neu.bed5), stream );

Fertig. (Die gelesenen \n am Stringende müsste man eventuell noch 
entfernen)

Dir scheint auch nicht klar zu sein, welche Bedeutung der 2. Parameter 
von fgets hat.
Das ist nicht die Anzahl der Zeichen, die du zu lesen gedenkst, sondern 
die Größe des Buffers, den du fgets zum Lesen übergibst! Du musst daher 
nicht vorher feststellen, wie lang eine Zeile überhaupt ist. Das wäre 
ziemlich sinnlos, wenn dich fgets dazu zwingen würde. fgets liest von 
sich aus bis zum Zeilenende. Aber: wie bei allen Funktionen, die einen 
String in einen Buffer schreiben, wäre es tunlichst angeraten, wenn 
nicht zuviele Zeichen in diesen Buffer geschrieben werden und diesen 
überlaufen. Daher muss fgets wissen, wie gross der Buffer ist. Das ist 
dann auch gleichzeitig die maximale Anzahl an Zeichen, die fgets 
gefahrlos lesen darf.
1
    fclose(stream);

Das passt wieder.

Den Rückgabewert von fopen musst du noch testen!


Und so sieht das dann in Summe aus:
1
    FILE* stream;
2
    stream = fopen( datei,"r" );
3
4
    long cntln = 0;
5
6
    while( cntln < zeilennummer && fgets( dummy, sizeof( dummy ), stream ) )
7
      cntln++;
8
9
    fgets( neu.wort, sizeof(neu.wort), stream );
10
    fgets( neu.bed_1, sizeof(neu.bed1), stream );
11
    fgets( neu.bed_2, sizeof(neu.bed2), stream );
12
    fgets( neu.bed_3, sizeof(neu.bed3), stream );
13
    fgets( neu.bed_4, sizeof(neu.bed4), stream );
14
    fgets( neu.bed_5, sizeof(neu.bed5), stream );
15
16
    fclose(stream);



PS:
Wieso eigentlich 7?
Eine Vokabelbeschreibung besteht bei dir aus 6 Zeilen!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Wo genau?
> Wahrscheinlich hier    do
>     {
>          nummer=rand()%(zeilenzahl/7); anzahl_vokabeln-1
>     } while(vorigeChk(nummer)==1);
>
> wenn du prüfst ob diese Zahl schon vorgekommen ist.
Zumal eine solche Funktion potenziell eine unendliche Laufzeit hat im 
Worstcase ;)

von Mark M. (mom-jovi)


Lesenswert?

Karl heinz Buchegger schrieb:
> PS:
> Wieso eigentlich 7?
> Eine Vokabelbeschreibung besteht bei dir aus 6 Zeilen!

Weil ich zwischen jeder Vokabel eine Leerzeile habe, damit es 
übersichtlich bleibt, wenn man die Datei von Hand erweitert.

Karl heinz Buchegger schrieb:
> Was hast du hier eigentlich vor?
> struct vokabel vokabel_aus_datei(char datei[])
> {
>     int zeilenzahl=zeilen(datei);            // zeilenzahl/7==anzahl vokabeln
>     srand(time(0));
>     int nummer=0;
>     do
>     {
>          nummer=rand()%(zeilenzahl/7); // nummer==zahl zwischen 0 und 
anzahl_vokabeln-1
>     } while(vorigeChk(nummer)==1);
>
>     int zeilennummer=nummer*7+1; // zeilennummer, ab der die Vokabel steht

Der Check ist wahrscheinlich etwas umständlich impplementiert. 
zeilennummer soll aber danach genau die Zahl haben, ab der das 
fremdsprachige Wort steht, und das muss eine durch 7 teilbare Zahl sein 
(mit einem "Offset" von 1, weil am Anfang der Datei auch eine Leerzeile 
steht).

von Karl H. (kbuchegg)


Lesenswert?

Mark M. schrieb:

> Der Check ist wahrscheinlich etwas umständlich impplementiert.

Wie gesagt: Lass ihn weg
Sobald du srand richtig einsetzt ist er nur eine Fehlerquelle 
zusätzlich.

von DirkB (Gast)


Lesenswert?

Mark M. schrieb:
>  ... Kann ich den Stream dann wie ein Zeiger
> übergeben?


Klar, an fgets übergibst du doch auch den stream.

von Frank Morster (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Wenn ich praktischerweise in meine /usr/include/linux/stddef.h
> reinschaue

Und wen kümmert, was du in deiner Implementierung siehst?

"#define NULL (1-1)" wäre in C übrigens ebenso möglich und korrekt.

Oder "#define NULL (1/2)"

von Klaus W. (mfgkw)


Lesenswert?

Frank Morster schrieb:
> Und wen kümmert, was du in deiner Implementierung siehst?

mich.

von Klaus W. (mfgkw)


Lesenswert?

Frank Morster schrieb:
> "#define NULL (1-1)" wäre in C übrigens ebenso möglich und korrekt.
>
> Oder "#define NULL (1/2)"

Trägt das jetzt irgendwie zu (void*) oder int bei?
Kümmert das jemanden?

von Rolf M. (rmagnus)


Lesenswert?

Schauen wir doch mal in die Sprachdefinitionen:

C:
1
An integer constant expression with the value 0, or such an expression cast to type
2
void *, is called a null pointer constant.

C++:
1
A null pointer constant is an integral constant expression rvalue of integer type that evaluates to
2
zero.

In beiden Sprachen muß NULL natürlich eine null pointer constant sein.

von Klaus W. (mfgkw)


Lesenswert?

Rolf Magnus schrieb:
> ... die Sprachdefinitionen ...

Schön, daß es nur je eine gibt für C und C++  :-)


Ich denke, da liegt einfach der Hund begraben.
Alleine für C gibt es soviele Varianten, daß man jahrelang sinnlos
streiten kann, wie es denn in C definiert sei, solange sich niemand
äußert, auf welche Version er sich denn nun beziehen mag.
C als solches gibt es nicht, wenn man Korinthenkackerei betreiben will.

Abgesehen davon, daß es wichtigeres gibt, ob man 0 als 0 schreibt,
oder 1-1.

Frohe Weihnachten, egal ob mit void* oder int - jeder wie er will!

von Rolf M. (rmagnus)


Lesenswert?

Klaus Wachtler schrieb:
> Rolf Magnus schrieb:
>> ... die Sprachdefinitionen ...
>
> Schön, daß es nur je eine gibt für C und C++  :-)

Es gibt tatsächlich nur je eine.

> Ich denke, da liegt einfach der Hund begraben.
> Alleine für C gibt es soviele Varianten, daß man jahrelang sinnlos
> streiten, wie es denn in C definiert, solange sich niemand äußert,
> auf welche Version er sich denn nun beziehen mag.

Es gibt halt verschiedene Compiler, die sich mehr oder weniger gut an 
die Spezifikation halten. Und es gibt natürlich alten Code, der noch aus 
der Zeit davor stammt.

> C als solches gibt es nicht, wenn man Korinthenkackerei betreiben will.

Doch. Es ist definiert in ISO/IEC 9899:1999, wo ich auch den zitierten 
Satz her habe.

> Abgesehen davon, daß es wichtigeres gibt, ob man 0 als 0 schreibt,
> oder 1-1.

Das sicherlich. Ob NULL nun 0 oder (void*)0 ist, spielt in der Praxis an 
sich auch keine Rolle. Bei manchen Compilern mag letzteres von Vorteil 
sein, weil man dann eine Warnung bekommt, wenn man es versehentlich im 
Kontext von Integern verwendet.


> Frohe Weihnachten, egal ob mit void* oder int - jeder wie er will!

Ebenso.

von Klaus W. (mfgkw)


Lesenswert?

Rolf Magnus schrieb:
>> C als solches gibt es nicht, wenn man Korinthenkackerei betreiben will.
>
> Doch. Es ist definiert in ISO/IEC 9899:1999, wo ich auch den zitierten
> Satz her habe.

ok, ich meinte etwa "einen Standard, an den sich auch alle halten und 
der damit für alle Programmierer verbindlich ist".
Das ist C99 nun leider nicht.

von Frank Morster (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> mich.

Schön. Dann unterlaß doch bitte deine anmaßenden allgemeingültigen 
Behauptungen, in C sei etwas so oder anders.

von Frank Morster (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Trägt das jetzt irgendwie zu (void*) oder int bei?

Ja. Es ist ein direkter Widerspruch zu deiner falschen Behauptung.

> Kümmert das jemanden?

Mich. Und jeden, der sich für die Sprache C interessiert und das Makro 
NULL sowie Nullpointer verstehen will.

Aber pfusche du ruhig auf Basis von "ich hab da mal mal gesehen" 
weiter...

von Rolf M. (rmagnus)


Lesenswert?

Um nochmal kurz zurück zum Thema zu kommen:

Karl heinz Buchegger schrieb:
> Wenn du eien Sequenz von Zahlen in einem bestimmten Bereich benötigst,
> von denen garantiert keine doppelt vorkommt und die bunt durcheinander
> gewürfelt sind, dann macht man das so:
>
> Man erzeugt sich ein Array mit den Zahlen von 0 bis zur Obergrenze
>
>   for( i = 0; i < ANZAHL; ++i )
>     arr[i] = i;
>
> und dann würfelt man die durcheinander, indem man zufällig immer 2
> Elemente vertauscht

Hat jemand eine Idee, wie man das ohne ein solches Array hinbekommen 
kann?
Bevor die offensichtliche Gegenfrage kommt: In meinem Fall wäre dieses 
Array 32 GB groß, und soviel RAM habe ich nicht.

von Bartli (Gast)


Lesenswert?

> Hat jemand eine Idee, wie man das ohne ein
> solches Array hinbekommen kann?

Mit einem genügend grossen LFSR? Kommt aber wohl darauf an, was du sonst 
noch für Ansprüche an die generierte Sequenz hast.

von Rolf M. (rmagnus)


Lesenswert?

Bartli schrieb:
>> Hat jemand eine Idee, wie man das ohne ein
>> solches Array hinbekommen kann?
>
> Mit einem genügend grossen LFSR? Kommt aber wohl darauf an, was du sonst
> noch für Ansprüche an die generierte Sequenz hast.

Ich habe einen Wertebereich von 0 bis n (n variabel, grob im Bereich 
einige Millionen bis mehrere Milliarden - 32 Bit reichen notfalls, 64 
wären aber besser) und hätte nun gerne eine C-Funktion, die mir alle 
Werte aus diesem Bereich in pseudozufälliger Reihenfolge ausspuckt, 
jeden genanu einmal. Die Qualität des Zufallsgenerators muß nicht sehr 
hoch sein.

von Klaus W. (mfgkw)


Lesenswert?

Wie wäre es mit einer Funktion, die mit einem Wert im Bereich
aufgerufen wird und immer nach demselben Algo die Bits verwürfelt?
Diese Funktion kann man dann mit fortlaufenden Werten aufrufe, oder
auch mit jedem mittendrin.

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.