Wie generiere ich ein zufälliges int in C?


Antworten:


662

Hinweis : Nicht rand()aus Sicherheitsgründen verwenden. Wenn Sie eine kryptografisch sichere Nummer benötigen, lesen Sie stattdessen diese Antwort .

#include <time.h>
#include <stdlib.h>

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

Bearbeiten : Unter Linux bevorzugen Sie möglicherweise die Verwendung von Random und Srandom .


194
Der Einfachheit halber +1, aber es ist wahrscheinlich eine gute Idee zu betonen, dass srand () nur einmal aufgerufen werden sollte . In einer Thread-Anwendung möchten Sie möglicherweise sicherstellen, dass der Status des Generators pro Thread gespeichert ist, und den Generator für jeden Thread einmal festlegen.
RBerteig

41
@trusktr, es ist kompliziert. Hier ist ein Grund: time()Änderungen nur einmal pro Sekunde. Wenn Sie time()für jeden Anruf an rand()einen Startwert festlegen , erhalten Sie für jeden Anruf innerhalb einer Sekunde den gleichen Wert. Der größere Grund ist jedoch, dass die Eigenschaften rand()und Funktionen wie diese am besten für den Anwendungsfall bekannt sind, bei dem sie genau einmal pro Lauf und nicht bei jedem einzelnen Aufruf gesetzt werden. Je nach "Zufälligkeit" mit ungetesteten oder unbewiesenen Eigenschaften kommt es zu Problemen.
RBerteig

9
@trusktr für einen einfachen linearen Kongruenzgenerator (was rand()normalerweise der Fall ist), mit dem Seeding rand()im besten Fall überhaupt keine Wirkung hat und im schlimmsten Fall die bekannten Eigenschaften des Generators beeinträchtigt. Dies ist ein tiefes Thema. Beginnen Sie mit dem Lesen von Knuth Vol 2 Kapitel 3 über Zufallszahlen als beste Einführung in die Mathematik und die Fallstricke.
RBerteig

21
Vermeiden Sie eine Compiler-Warnung mit einer Besetzung:srand((unsigned int)time(NULL));
GiovaMaster

9
Denken Sie daran, dass dies immer noch eine schwache Sichtweise auf das PRNG ist. Erst letztes Jahr machte ein Cryptolocker-Virus unter Linux den Fehler, mit der Zeit zu säen, und dies reduzierte den Suchraum dramatisch . Alles, was Sie tun mussten, war eine anständige Vorstellung davon zu bekommen, wann die Infektion auftrat, und dann Samen aus dieser Zeit zu probieren. Zuletzt habe ich gehört, dass die beste Quelle für Zufälligkeit / dev / urandom ist, die angeblich aus einem Mashup chaotischer Quellen wie Temperaturen auf der Hardware stammt. Wenn Sie jedoch nur möchten, dass Ihr Programm bei jedem Lauf anders funktioniert, ist die oben genannte Lösung in Ordnung.
Jemenake

238

Die rand()Funktion in <stdlib.h>gibt eine pseudozufällige Ganzzahl zwischen 0 und zurück RAND_MAX. Sie können srand(unsigned int seed)einen Startwert festlegen.

Es ist üblich, den %Operator in Verbindung mit rand()zu verwenden, um einen anderen Bereich zu erhalten (bedenken Sie jedoch, dass dies die Gleichmäßigkeit etwas beeinträchtigt). Zum Beispiel:

/* random int between 0 and 19 */
int r = rand() % 20;

Wenn Ihnen die Einheitlichkeit wirklich am Herzen liegt, können Sie Folgendes tun:

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}

18
Es ist eine gängige Praxis , aber nicht die richtige. Sehen Sie dies und das .
Lazer

35
@Lazer: Deshalb habe ich gesagt "obwohl ich bedenke, dass dies die Gleichförmigkeit etwas beeinträchtigt".
Laurence Gonsalves

3
@AbhimanyuAryan Das %ist der Moduloperator . Es gibt Ihnen den Rest einer ganzzahligen Division, gibt Ihnen also x % nimmer eine Zahl zwischen 0und n - 1(solange xund nbeide positiv sind). Wenn Sie das immer noch verwirrend finden, schreiben Sie ein Programm mit einer iAnzahl von 0 bis 100 und drucken Sie es i % nfür einige nIhrer Auswahl aus, die kleiner als 100 sind.
Laurence Gonsalves

2
@necromancer Ich ging voran und fügte eine vollkommen einheitliche Lösung hinzu.
Laurence Gonsalves

2
@Lazer Der zweite Link, den du gepostet hast, ist eigentlich immer noch nicht perfekt einheitlich. Das Casting auf ein Doppel und zurück hilft nicht. Der erste Link, den Sie gepostet haben, hat eine vollkommen einheitliche Lösung, obwohl er für kleine Obergrenzen eine Menge Schleifen enthält . Ich habe dieser Antwort eine vollkommen einheitliche Lösung hinzugefügt, die selbst für kleine Obergrenzen nicht so viele Schleifen bilden sollte.
Laurence Gonsalves

53

Wenn Sie sichere zufällige Zeichen oder Ganzzahlen benötigen:

Im Abschnitt zum sicheren Generieren von Zufallszahlen in verschiedenen Programmiersprachen sollten Sie einen der folgenden Schritte ausführen:

Zum Beispiel:

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1; 
    }


    /* myString will be an array of 32 random bytes, not null-terminated */        
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}

randombytes_uniform() ist kryptografisch sicher und unvoreingenommen.


sollte libsodium RNG vor dem Aufruf von randombytes_buf gesetzt werden?
user2199593

Rufen Sie einfach sodium_init()irgendwann an. Mach dir keine Sorgen über das RNG, es verwendet den Kernel.
Scott Arciszewski

Hinweis: Ich habe die letzte Bearbeitung genehmigt, sodium_init()obwohl dies nicht unbedingt Teil meines Beispiels ist, da es sich um ein wichtiges Detail handelt.
Scott Arciszewski

29

Lass uns das durchgehen. Zuerst verwenden wir die Funktion srand (), um den Randomizer zu setzen. Grundsätzlich kann der Computer Zufallszahlen basierend auf der Zahl generieren, die srand () zugeführt wird. Wenn Sie denselben Startwert angeben, werden jedes Mal dieselben Zufallszahlen generiert.

Daher müssen wir den Randomizer mit einem Wert setzen, der sich ständig ändert. Dazu geben wir den Wert der aktuellen Zeit mit der Funktion time () ein.

Wenn wir nun rand () aufrufen, wird jedes Mal eine neue Zufallszahl erzeugt.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}

12
Netter Code, aber keine gute Idee, 'srand (time (NULL));' zu nennen. Diese Methode erzeugt dieselbe Nummer, wenn sie in einer for-Schleife aufgerufen wird.
RayOldProf

1
Vorgeschlagene Änderungen mit Code werden häufig abgelehnt. Jemand hat hier einen mit dem Kommentar "Algorithmus war falsch. Könnte größere Zahlen als das Maximum produzieren" gemacht. Ich habe die Behauptung nicht selbst bewertet.
Martin Smith

1
@ Martin Smith Probleme: 1) sollte sein else{ low_num=max_num; hi_num=min_num+1;2) schlägt fehl wenn hi_num - low_num > INT_MAX. 3) Lässt Werte in der seltenen Situation aus INT_MAX > hi_num - low_num > RAND_MAX.
chux

1
Wenn Sie es so erneut festlegen, erzeugt diese Funktion dieselbe Nummer, wenn sie in derselben Sekunde mehrmals aufgerufen wird. Wenn Sie es wirklich neu säen möchten, säen Sie es nur einmal pro Sekunde neu.
Élektra

1
Klein: hi_num = max_num + 1;Mangel an Überlaufschutz.
chux

25

Wenn Sie Pseudozufallszahlen von besserer Qualität benötigen als diese stdlib, schauen Sie sich Mersenne Twister an . Es ist auch schneller. Beispielimplementierungen sind beispielsweise hier reichlich vorhanden .


2
+1: Sieht cool aus, aber ich habe gerade ein Ratespiel gemacht. Wenn ich in einer Geschäftsanwendung einen Zufallszahlengenerator verwenden würde, würde ich diesen definitiv verwenden.
Kredns

4
Verwenden Sie keinen Mersenne Twister, sondern etwas Gutes wie xoroshiro128 + oder PCG. (Relevanter Link.)
Veedrac

17

Die Standard-C-Funktion ist rand(). Es ist gut genug, um Karten für Solitaire zu geben, aber es ist schrecklich. Viele Implementierungen von rand()Zyklen durch eine kurze Liste von Zahlen, und die niedrigen Bits haben kürzere Zyklen. Die Art rand()und Weise, wie manche Programme aufrufen, ist schrecklich, und es srand()ist schwierig , einen guten Startwert zu berechnen .

Der beste Weg, um Zufallszahlen in C zu generieren, ist die Verwendung einer Drittanbieter-Bibliothek wie OpenSSL. Zum Beispiel,

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}

Warum so viel Code? Andere Sprachen wie Java und Ruby haben Funktionen für zufällige Ganzzahlen oder Gleitkommazahlen. OpenSSL gibt nur zufällige Bytes an, daher versuche ich nachzuahmen, wie Java oder Ruby sie in Ganzzahlen oder Floats umwandeln würden.

Für ganze Zahlen wollen wir Modulo Bias vermeiden . Angenommen, wir haben zufällige 4-stellige Ganzzahlen erhalten rand() % 10000, können aber rand()nur 0 bis 32767 zurückgeben (wie in Microsoft Windows). Jede Zahl von 0 bis 2767 wird häufiger angezeigt als jede Zahl von 2768 bis 9999. Um die Abweichung zu beseitigen, können wir es erneut versuchen, rand()während der Wert unter 2768 liegt, da die 30000-Werte von 2768 bis 32767 einheitlich auf die 10000-Werte von 0 bis abgebildet werden 9999.

Für Floats benötigen wir 53 zufällige Bits, da a eine doubleGenauigkeit von 53 Bit enthält (vorausgesetzt, es handelt sich um ein IEEE-Double). Wenn wir mehr als 53 Bits verwenden, erhalten wir eine Rundungsvorspannung. Einige Programmierer schreiben Code wie rand() / (double)RAND_MAX, geben jedoch rand()möglicherweise nur 31 Bit oder nur 15 Bit in Windows zurück.

OpenSSLs RAND_bytes()Samen selbst, vielleicht durch Lesen /dev/urandomin Linux. Wenn wir viele Zufallszahlen benötigen, wäre es zu langsam, sie alle zu lesen /dev/urandom, da sie aus dem Kernel kopiert werden müssen. Es ist schneller, OpenSSL zu erlauben, mehr Zufallszahlen aus einem Startwert zu generieren.

Mehr über Zufallszahlen:

  • Perls Perl_seed () ist ein Beispiel für die Berechnung eines Seeds in C für srand(). Es mischt Bits aus der aktuellen Zeit, der Prozess-ID und einigen Zeigern, wenn es nicht lesen kann /dev/urandom.
  • OpenBSDs arc4random_uniform () erklärt die Modulo-Verzerrung.
  • Die Java-API für java.util.Random beschreibt Algorithmen zum Entfernen von Verzerrungen aus zufälligen Ganzzahlen und zum Packen von 53 Bit in zufällige Gleitkommazahlen.

Vielen Dank für diese erweiterte Antwort. Beachten Sie, dass Sie von den 24 aktuellen Antworten auf diese Frage die einzige mit einer zusätzlichen Interpretation waren, die sich mit float/ doublebefasste. Daher habe ich die Frage geklärt, sich an intZahlen zu halten, um zu vermeiden, dass sie zu weit gefasst wird. Es gibt andere C-Fragen, die sich speziell mit float/ doublezufälligen Werten befassen. Daher möchten Sie möglicherweise Ihre zweite Hälfte Ihrer Antwort auf Fragen wie stackoverflow.com/questions/13408990/…
Cœur

11

Wenn Ihr System die arc4randomFunktionsfamilie unterstützt, würde ich empfehlen, diese anstelle der Standardfunktion zu randverwenden.

Die arc4randomFamilie umfasst:

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)

arc4random Gibt eine zufällige vorzeichenlose 32-Bit-Ganzzahl zurück.

arc4random_buffügt zufälligen Inhalt in seinen Parameter ein buf : void *. Die Menge des Inhalts wird durch den bytes : size_tParameter bestimmt.

arc4random_uniformGibt eine zufällige vorzeichenlose 32-Bit-Ganzzahl zurück, die der Regel folgt: 0 <= arc4random_uniform(limit) < limitwobei limit auch eine vorzeichenlose 32-Bit-Ganzzahl ist.

arc4random_stirliest Daten aus /dev/urandomund übergibt die Daten an, arc4random_addrandomum den internen Zufallszahlenpool zusätzlich zu randomisieren.

arc4random_addrandomwird verwendet, um arc4random_stirden internen Zufallszahlenpool entsprechend den an ihn übergebenen Daten zu füllen.

Wenn Sie diese Funktionen nicht haben, aber unter Unix arbeiten, können Sie diesen Code verwenden:

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

Die urandom_initFunktion öffnet das /dev/urandomGerät und fügt den Dateideskriptor ein urandom_fd.

Die urandomFunktion ist im Grunde die gleiche wie ein Aufruf von rand, außer sicherer, und sie gibt a zurück long(leicht änderbar).

Da /dev/urandomdies jedoch etwas langsam sein kann, wird empfohlen, es als Startwert für einen anderen Zufallszahlengenerator zu verwenden.

Wenn Ihr System einen nicht haben /dev/urandom, aber nicht eine hat /dev/randomoder ähnliche Datei, dann können Sie einfach den Pfad zu übergeben ändern openin urandom_init. Die in urandom_initund urandommeiner Meinung nach POSIX-kompatiblen Aufrufe und APIs sollten als solche auf den meisten, wenn nicht allen POSIX-kompatiblen Systemen funktionieren.

Hinweise: Ein Lesevorgang /dev/urandomwird NICHT blockiert, wenn nicht genügend Entropie verfügbar ist. Daher können unter solchen Umständen generierte Werte kryptografisch unsicher sein. Wenn Sie sich darüber Sorgen machen, verwenden Sie diese Option /dev/random, die bei unzureichender Entropie immer blockiert.

Wenn Sie sich auf einem anderen System (z. B. Windows) befinden, verwenden Sie randoder eine interne Windows-spezifische plattformabhängige nicht portierbare API.

Wrapper - Funktion für urandom, randoder arc4randomAnrufe:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

STL existiert nicht für C. Sie müssen anrufen randoder noch besser random. Diese werden im Standardbibliotheksheader deklariert stdlib.h. randist POSIX, randomist eine BSD-Spezifikationsfunktion.

Der Unterschied zwischen randund randombesteht darin, dass randomeine viel besser verwendbare 32-Bit-Zufallszahl und randnormalerweise eine 16-Bit-Zahl zurückgegeben wird. Die BSD-Manpages zeigen, dass die unteren Bits von randzyklisch und vorhersehbar sind und daher randfür kleine Zahlen möglicherweise unbrauchbar sind.


3
@Neil - da alle Antworten bisher die STL erwähnen, vermute ich, dass die Frage schnell bearbeitet wurde, um unnötige Verweise zu entfernen.
Michael Burr

rand () ist für kleine Zahlen nicht nutzlos - Sie können sie herausschieben und nur die zufälligeren hohen Bits verwenden, wenn Sie es wirklich brauchen.
Chris Lutz

@Chris können Sie, wenn die Größe der Zufallszahl bekannt ist, aber wenn sich die erforderliche Größe der Zufallszahl zur Laufzeit ändert (z. B. das Mischen eines dynamischen Arrays usw.), ist es schwierig, eine solche Einschränkung zu umgehen.
Dreamlax

Ich kann hier keine Zufallsfunktion finden :-(
kasia.b

@ kasia.b in diesem Link gibt es extern int rand(void);und extern void srand(unsigned int);.
RastaJedi

7

Schauen Sie sich ISAAC an (Indirektion, Verschiebung, Akkumulieren, Hinzufügen und Zählen). Es ist gleichmäßig verteilt und hat eine durchschnittliche Zykluslänge von 2 ^ 8295.


2
ISAAC ist aufgrund seiner Geschwindigkeit ein interessantes RNG, hat jedoch noch keine ernsthafte kryptografische Aufmerksamkeit erhalten.
user2398029

4

Dies ist ein guter Weg, um eine Zufallszahl zwischen zwei Zahlen Ihrer Wahl zu erhalten.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

    #define randnum(min, max) \
        ((rand() % (int)(((max) + 1) - (min))) + (min))

int main()
{
    srand(time(NULL));

    printf("%d\n", randnum(1, 70));
}

Erstmalige Ausgabe: 39

Beim zweiten Mal ausgeben: 61

Zum dritten Mal ausgeben: 65

Sie können die Werte danach randnumin beliebige Zahlen ändern , und es wird eine Zufallszahl zwischen diesen beiden Zahlen für Sie generiert.


4

Sie möchten verwenden rand(). Hinweis ( SEHR WICHTIG ): Stellen Sie sicher, dass Sie den Startwert für die Rand-Funktion festlegen. Wenn Sie dies nicht tun, sind Ihre Zufallszahlen nicht wirklich zufällig . Das ist sehr, sehr, sehr wichtig. Zum Glück können Sie normalerweise eine Kombination aus dem System-Ticks-Timer und dem Datum verwenden, um einen guten Startwert zu erhalten.


6
Zwei Punkte a) Ihre Zufallszahlen sind nicht "wirklich" zufällig, egal wie Sie den Generator setzen. Und b) es ist sehr praktisch, dass die Pseudozufallsfolge unter vielen Umständen immer dieselbe ist - zum Beispiel zum Testen.

16
Wenn es SEHR WICHTIG ist, dass Ihre Zahl wirklich zufällig ist, sollten Sie die Funktion rand () nicht verwenden.
Tyler

1
Die Werte von rand sind überhaupt nicht "wirklich" zufällig, egal ob Sie den Startwert setzen oder nicht. Bei einem bekannten Startwert ist die Sequenz vorhersehbar. "Wirklich" Zufallszahlengenerierung ist schwierig. Mit Rand ist keine Entropie verbunden.
Dreamlax

2
Natürlich werden sie - der Generator wird von der Bibliothek für Sie gesetzt (wahrscheinlich auf Null, aber das ist ein gültiger Samen).

4
Ah, aber ein bekannter Algorithmus / bekannter Startwert ist wichtig für das Debuggen eines Programms, das Zufallszahlen verwendet. Es ist nicht ungewöhnlich, den verwendeten Startwert zusammen mit einem Simulationslauf zu protokollieren, damit er für eine detailliertere Analyse neu erstellt werden kann. Das Nichtaufrufen von srand () entspricht dem Aufrufen von srand (1).
RBerteig

4

FWIW, die Antwort lautet: Ja, es gibt eine stdlib.hFunktion namens rand; Diese Funktion ist in erster Linie auf Geschwindigkeit und Verteilung abgestimmt, nicht auf Unvorhersehbarkeit. Fast alle integrierten Zufallsfunktionen für verschiedene Sprachen und Frameworks verwenden diese Funktion standardmäßig. Es gibt auch "kryptografische" Zufallszahlengeneratoren, die viel weniger vorhersehbar sind, aber viel langsamer laufen. Diese sollten in jeder sicherheitsrelevanten Anwendung verwendet werden.


4

Dies ist hoffentlich etwas zufälliger als nur zu verwenden srand(time(NULL)).

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
    srand(rand());

    for (int i = 0; i < 10; i++)
        printf("%d\n", rand());
}

1
Das Hinzufügen von srand (rand ()) erhöht die Zufälligkeit der Sequenz nicht, wenn dieses Programm innerhalb von 1 Sekunde mehrmals ausgeführt wird. time (NULL) gibt immer noch den gleichen Wert für jeden von ihnen zurück, der erste rand () gibt den gleichen langen Wert zurück und der zweite Aufruf von srand () hat den gleichen Wert, was dazu führt, dass immer noch die gleiche Zufallsfolge vorliegt. Die Verwendung der Adresse von argc kann nur dann hilfreich sein, wenn garantiert ist, dass diese Adresse bei jeder Ausführung des Programms unterschiedlich ist, was nicht immer der Fall ist.
theferrit32

3

Nun, STL ist C ++, nicht C, also weiß ich nicht, was Sie wollen. Wenn Sie jedoch C möchten, gibt es die rand()und srand()-Funktionen:

int rand(void);

void srand(unsigned seed);

Diese sind beide Teil von ANSI C. Es gibt auch die random()Funktion:

long random(void);

Aber soweit ich das beurteilen kann, random()handelt es sich nicht um Standard-ANSI C. Eine Bibliothek von Drittanbietern ist möglicherweise keine schlechte Idee, aber alles hängt davon ab, wie zufällig eine Zahl ist, die Sie wirklich generieren müssen.


3

C Programm zur Erzeugung einer Zufallszahl zwischen 9 und 50

#include <time.h>
#include <stdlib.h>

int main()
{
    srand(time(NULL));
    int lowerLimit = 10, upperLimit = 50;
    int r =  lowerLimit + rand() % (upperLimit - lowerLimit);
    printf("%d", r);
}

Im Allgemeinen können wir eine Zufallszahl zwischen LowerLimit und UpperLimit-1 generieren

dh LowerLimit ist inklusive oder sagt r ∈ [LowerLimit, UpperLimit)


@Pang Das habe ich deutlich zwischen 9 und 50 erwähnt, nicht zwischen 9 und 50.
Shivam K. Thakkar

2
Ihre Modulo-Operation hat eine Verzerrung eingeführt.
Jww

2

rand() ist der bequemste Weg, um Zufallszahlen zu generieren.

Sie können auch Zufallszahlen von jedem Onlinedienst wie random.org abrufen.


1
Sie können auch Zufallszahlen von jedem Onlinedienst wie random.org Bounty abrufen, wenn Sie eine tragbare, effiziente Methode in C.
MD XF

2
#include <stdio.h>
#include <stdlib.h>

void main() 
{
    int visited[100];
    int randValue, a, b, vindex = 0;

    randValue = (rand() % 100) + 1;

    while (vindex < 100) {
        for (b = 0; b < vindex; b++) {
            if (visited[b] == randValue) {
                randValue = (rand() % 100) + 1;
                b = 0;
            }
        }

        visited[vindex++] = randValue;
    }

    for (a = 0; a < 100; a++)
        printf("%d ", visited[a]);
}

dann oben mit Variablen definieren @ b4hand
Muhammad Sadiq

Zu dem Zeitpunkt, als ich diesen Kommentar abgegeben habe, hatte ich keine universellen Bearbeitungsberechtigungen, und im Allgemeinen werden Codeänderungen, die die tatsächliche Antwort geändert haben, abgelehnt. Wenn Sie Ihre Antwort nicht korrigieren möchten, kann ich das trotzdem.
b4hand

Wenn Sie beabsichtigen, eine zufällige Permutation eindeutiger Werte zu erstellen, weist dieser Code immer noch einen Fehler auf. Es erzeugt den Wert 84 zweimal und nicht den Wert 48. Außerdem wird der Zufallszahlengenerator nicht gesetzt, sodass die Reihenfolge bei jeder Ausführung gleich ist.
b4hand

1
Die Variablen a und b sind in diesem Code definiert. Es ist fehlerfrei. Es gibt auch keine Syntax und keinen logischen Fehler.
Muhammad Sadiq

Falsch. Ich habe die Ausgabe bereits von Hand bestätigt, wie ich bereits erwähnt habe.
b4hand

1
#include <stdio.h>
#include <dos.h>

int random(int range);

int main(void)
{
    printf("%d", random(10));
    return 0;
}

int random(int range)
{
    struct time t;
    int r;

    gettime(&t);
    r = t.ti_sec % range;
    return r;
}

1

Auf modernen x86_64-CPUs können Sie den Hardware-Zufallszahlengenerator über verwenden _rdrand64_step()

Beispielcode:

#include <immintrin.h>

uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
  // Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number

1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//generate number in range [min,max)
int random(int min, int max){
    int number = min + rand() % (max - min);
    return number; 
}

//Driver code
int main(){
    srand(time(NULL));
    for(int i = 1; i <= 10; i++){
        printf("%d\t", random(10, 100));
    }
    return 0;
}

0

Als rand()ich eine gute Erklärung hörte, warum es eine schlechte Idee ist, gleichmäßig verteilte Zufallszahlen in einem bestimmten Bereich zu erzeugen, beschloss ich, einen Blick darauf zu werfen, wie verzerrt die Ausgabe tatsächlich ist. Mein Testfall war faires Würfeln. Hier ist der C-Code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char *argv[])
{
    int i;
    int dice[6];

    for (i = 0; i < 6; i++) 
      dice[i] = 0;
    srand(time(NULL));

    const int TOTAL = 10000000;
    for (i = 0; i < TOTAL; i++)
      dice[(rand() % 6)] += 1;

    double pers = 0.0, tpers = 0.0;
    for (i = 0; i < 6; i++) {
      pers = (dice[i] * 100.0) / TOTAL;
      printf("\t%1d  %5.2f%%\n", dice[i], pers);
      tpers += pers;
    }
    printf("\ttotal:  %6.2f%%\n", tpers);
}

und hier ist seine Ausgabe:

 $ gcc -o t3 t3.c
 $ ./t3 
        1666598  16.67%     
        1668630  16.69%
        1667682  16.68%
        1666049  16.66%
        1665948  16.66%
        1665093  16.65%
        total:  100.00%
 $ ./t3     
        1667634  16.68%
        1665914  16.66%
        1665542  16.66%
        1667828  16.68%
        1663649  16.64%
        1669433  16.69%
        total:  100.00%

Ich weiß nicht, wie einheitlich Ihre Zufallszahlen sein müssen, aber das Obige scheint für die meisten Anforderungen einheitlich genug zu sein.

Bearbeiten: Es wäre eine gute Idee, das PRNG mit etwas Besserem als zu initialisieren time(NULL).


rand () kann andere Zufälligkeitstests wie die eingefleischten Tests nicht bestehen . rand () unterscheidet sich von Plattform zu Plattform; rand () -Werte von GNU / Linux sind möglicherweise besser als Werte von BSD oder Windows.
George Koehler

Dies ist keine gültige Methode zum Testen der Zufälligkeit.
Veedrac

Hängt vom Zweck und dem Bedrohungs- / Risikomodell ab. Verwenden Sie für kryptografisch starkes RNG RDRAND (oder RDSEED). Für einen einfachen Würfelwerfer (nicht auf Casino-Ebene) sollte meiner Meinung nach das oben Genannte ausreichen. Das Schlüsselwort lautet "gut genug ".
Maus

0

Ich hatte in meiner letzten Anwendung ein ernstes Problem mit dem Pseudozufallszahlengenerator: Ich habe mein C-Programm wiederholt über ein Pyhton-Skript aufgerufen und den folgenden Code als Startwert verwendet:

srand(time(NULL))

Da jedoch:

  • rand erzeugt die gleiche Pseudozufallssequenz und gibt den gleichen Startwert in srand (siehe man srand );
  • Wie bereits erwähnt, ändert sich die Zeitfunktion nur von Sekunde zu Sekunde: Wenn Ihre Anwendung innerhalb derselben Sekunde mehrmals ausgeführt wird, time wird wird jedes Mal derselbe Wert zurückgegeben.

Mein Programm hat die gleiche Zahlenfolge generiert. Sie können drei Dinge tun, um dieses Problem zu lösen:

  1. Mischzeitausgabe mit einigen anderen Informationen, die sich bei Läufen ändern (in meiner Anwendung der Ausgabename):

    srand(time(NULL) | getHashOfString(outputName))

    Ich habe djb2 als Hash-Funktion verwendet.

  2. Erhöhen Sie die Zeitauflösung. Auf meiner Plattform clock_gettimewar verfügbar, also benutze ich es:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. Verwenden Sie beide Methoden zusammen:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));

Option 3 stellt sicher, dass Sie (soweit ich weiß) die beste Zufalls-Zufälligkeit haben, kann jedoch nur bei sehr schneller Anwendung einen Unterschied bewirken. Meiner Meinung nach ist Option 2 eine sichere Wette.


Verlassen Sie sich auch bei diesen Heuristiken nicht auf rand () für kryptografische Daten.
Domenukk

rand()sollte nicht für kryptografische Daten verwendet werden, stimme ich zu. Zumindest für mich enthielt meine Anwendung keine kryptografischen Daten, daher war die angegebene Methode für mich in Ordnung.
Koldar

0

Trotz aller Vorschläge der Leute rand()hier möchten Sie nicht verwenden, es rand()sei denn, Sie müssen! Die Zufallszahlen, dierand() erzeugt werden sind oft sehr schlecht. Um aus der Linux-Manpage zu zitieren:

Die Versionen von rand()und srand()in der Linux C-Bibliothek verwenden denselben Zufallszahlengenerator wie random(3)und srandom(3), daher sollten die Bits niedrigerer Ordnung genauso zufällig sein wie die Bits höherer Ordnung. Bei älteren rand () - Implementierungen und bei aktuellen Implementierungen auf verschiedenen Systemen sind die Bits niedrigerer Ordnung jedoch viel weniger zufällig als die Bits höherer Ordnung . Verwenden Sie diese Funktion nicht in Anwendungen, die portabel sein sollen, wenn eine gute Zufälligkeit erforderlich ist. ( Verwenden Sie random(3)stattdessen. )

In Bezug auf die Portabilität random()wird dies seit geraumer Zeit auch vom POSIX-Standard definiert. rand()älter ist, erschien es bereits in der ersten POSIX.1-Spezifikation (IEEE Std 1003.1-1988), wohingegenrandom() zuerst in POSIX.1-2001 (IEEE Std 1003.1-2001) erschien, der aktuelle POSIX-Standard ist jedoch bereits POSIX.1-2008 (IEEE Std 1003.1-2008), die vor einem Jahr ein Update erhalten hat (IEEE Std 1003.1-2008, Ausgabe 2016). Daher würde ich es als random()sehr tragbar betrachten.

POSIX.1-2001 führte auch die Funktionen lrand48()und ein mrand48(), siehe hier :

Diese Funktionsfamilie soll Pseudozufallszahlen unter Verwendung eines linearen Kongruenzalgorithmus und einer 48-Bit-Ganzzahlarithmetik erzeugen.

Und eine ziemlich gute Pseudozufallsquelle ist die arc4random()Funktion, die auf vielen Systemen verfügbar ist. Nicht Teil eines offiziellen Standards, erschien um 1997 in BSD, aber Sie können ihn auf Systemen wie Linux und macOS / iOS finden.


random()existiert unter Windows nicht.
Björn Lindqvist

@ BjörnLindqvist Windows ist auch kein POSIX-System; Es ist so ziemlich das einzige System auf dem Markt, das zumindest die Basis-POSIX-APIs nicht unterstützt (die sogar gesperrte Systeme wie iOS unterstützen). Windows wird nur unterstützt, rand()da dies auch vom C-Standard verlangt wird. Für alles andere benötigen Sie wie gewohnt nur eine spezielle Lösung für Windows. #ifdef _WIN32ist der Ausdruck, den Sie am häufigsten in plattformübergreifendem Code sehen, der Windows unterstützen möchte. In der Regel gibt es eine Lösung, die mit allen Systemen funktioniert, und eine, die nur für Windows erforderlich ist.
Mecki

0

Für Linux C-Anwendungen:

Dies ist mein überarbeiteter Code aus einer Antwort oben, die meinen C-Code-Praktiken folgt und einen zufälligen Puffer beliebiger Größe (mit geeigneten Rückkehrcodes usw.) zurückgibt. Stellen Sie sicher, dass Sie zu urandom_open()Beginn Ihres Programms einmal aufrufen .

int gUrandomFd = -1;

int urandom_open(void)
{
    if (gUrandomFd == -1) {
        gUrandomFd = open("/dev/urandom", O_RDONLY);
    }

    if (gUrandomFd == -1) {
        fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
                  errno, strerror(errno));
        return -1;
    } else {
        return 0;
    }
}


void urandom_close(void)
{
    close(gUrandomFd);
    gUrandomFd = -1;
}


//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
    int ret = 0; // Return value

    if (gUrandomFd == -1) {
        fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
        return -1;
    }

    ret = read(gUrandomFd, buf, size);

    if (ret != size) {
        fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
                 ret, size);
        return -1;
    } else {
        return 0;
    }
}

-2

Meine minimalistische Lösung sollte für Zufallszahlen im Bereich funktionieren [min, max). Verwenden Sie diese srand(time(NULL))Option, bevor Sie die Funktion aufrufen.

int range_rand(int min_num, int max_num) {
    if (min_num >= max_num) {
        fprintf(stderr, "min_num is greater or equal than max_num!\n"); 
    }
    return min_num + (rand() % (max_num - min_num));
} 

-3

Versuchen Sie dies, ich habe es aus einigen der Konzepte zusammengestellt, auf die oben bereits verwiesen wurde:

/*    
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
 */

int random(int max) {
    srand((unsigned) time(NULL));
    return (rand() % max) + 1;
}

16
Dieser Code ist nicht gut. Es ist eine schreckliche Idee, srand()jedes Mal anzurufen, wenn Sie anrufen möchten rand(). Da time()normalerweise ein Wert in Sekunden zurückgegeben wird , gibt das schnelle Aufrufen dieser Funktion denselben "zufälligen" Wert zurück.
Blastfurnace

3
Diese Funktion würde mit der Unix- random()Funktion verwechselt .
George Koehler
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.