it-swarm.com.de

Wie erzeugt man ein zufälliges int in C?

Gibt es eine Funktion, um eine zufällige int-Zahl in C zu generieren? Oder muss ich eine Fremdbibliothek verwenden?

482
Kredns

Note: Verwenden Sie Rand() nicht zur Sicherheit. Wenn Sie eine kryptografisch sichere Nummer benötigen, verwenden Sie stattdessen siehe 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 können Sie zufällig und zufällig verwenden.

596
Łukasz Lew

Die Funktion Rand() in <stdlib.h> gibt eine pseudozufällige Ganzzahl zwischen 0 und Rand_MAX zurück. Sie können srand(unsigned int seed) verwenden, um einen Startwert festzulegen.

Es ist üblich, den %-Operator in Verbindung mit Rand() zu verwenden, um einen anderen Bereich zu erhalten (bedenken Sie jedoch, dass dies die Einheitlichkeit etwas abwirft). Zum Beispiel:

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

Wenn Sie wirklich sich um Einheitlichkeit kümmern, 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;
  }
}
216

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

In wie man zufällig Zahlen in verschiedenen Programmiersprachen generiert , 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 kryptographisch sicher und unvoreingenommen.

43

Lass uns das durchmachen. Zuerst verwenden wir die Funktion srand (), um den Randomizer zu samen. Grundsätzlich kann der Computer Zufallszahlen basierend auf der Anzahl erzeugen, die an srand () gesendet wird. Wenn Sie denselben Startwert angegeben haben, werden jedes Mal die gleichen Zufallszahlen generiert. 

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

Wenn wir jetzt 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;
}
28
Abhay Budakoti

Wenn Sie Pseudo-Zufallszahlen mit besserer Qualität benötigen als stdlib, überprüfen Sie die Option Mersenne Twister . Es ist auch schneller. Beispielimplementierungen sind ausreichend, zum Beispiel hier .

25
MH114

Die Standard-C-Funktion ist Rand(). Es ist gut genug, um Karten für Solitaire auszugeben, aber es ist schrecklich. Viele Implementierungen von Rand() durchlaufen eine kurze Liste von Zahlen, und die niedrigen Bits haben kürzere Zyklen. Die Art, wie einige Programme Rand() aufrufen, ist schrecklich, und die Berechnung eines guten Samens für die Weitergabe an srand() ist schwierig.

Die beste Möglichkeit, 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 verfügen über Funktionen für zufällige Ganzzahlen oder Floats. OpenSSL gibt nur zufällige Bytes, daher versuche ich zu simulieren, wie Java oder Ruby sie in Ganzzahlen oder Floats umwandeln würde.

Bei Ganzzahlen möchten wir modulo bias vermeiden. Angenommen, wir haben einige zufällige 4-stellige Ganzzahlen von Rand() % 10000 erhalten, aber Rand() kann nur 0 bis 32767 zurückgeben (wie in Microsoft Windows). Jede Zahl von 0 bis 2767 erscheint häufiger als jede Zahl von 2768 bis 9999. Um die Verzerrung zu entfernen, können Sie Rand() erneut versuchen, solange der Wert unter 2768 liegt, da die 30000-Werte von 2768 bis 32767 einheitlich auf die 10000-Werte von 0 abbilden bis 9999.

Für Floats wollen wir 53 zufällige Bits, da eine double eine Genauigkeit von 53 Bits besitzt (vorausgesetzt, es handelt sich um ein IEEE-Double). Wenn wir mehr als 53 Bits verwenden, erhalten wir Rundungsverzerrungen. Einige Programmierer schreiben Code wie Rand() / (double)Rand_MAX, aber Rand() gibt in Windows möglicherweise nur 31 Bit oder nur 15 Bit zurück.

OpenSSLs Rand_bytes() sät sich, indem er /dev/urandom in Linux liest. Wenn wir viele Zufallszahlen benötigen, wäre es zu langsam, sie alle aus /dev/urandom zu lesen, da sie aus dem Kernel kopiert werden müssen. Es ist schneller, wenn OpenSSL mehr Zufallszahlen aus einem Seed generiert.

Mehr über Zufallszahlen:

  • Perl's 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 /dev/urandom nicht gelesen werden kann.
  • OpenBSDs arc4random_uniform () erklärt die Modulo-Neigung.
  • Java-API für Java.util.Random beschreibt Algorithmen zum Entfernen von Bias von zufälligen Ganzzahlen und zum Packen von 53 Bits in zufällige Floats.
16
George Koehler

Wenn Ihr System die arc4random - Funktionsfamilie unterstützt, würde ich empfehlen, diese anstelle der Standardfunktion Rand zu verwenden.

Die arc4random-Familie 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 32-Bit-Ganzzahl ohne Vorzeichen zurück.

arc4random_buf setzt zufälligen Inhalt in den Parameter buf : void *. Die Menge des Inhalts wird durch den Parameter bytes : size_t bestimmt.

arc4random_uniform gibt eine zufällige 32-Bit-Ganzzahl ohne Vorzeichen zurück, die der Regel folgt: 0 <= arc4random_uniform(limit) < limit.

arc4random_stir liest Daten aus /dev/urandom und übergibt die Daten an arc4random_addrandom, um den internen Zufallszahlenpool zusätzlich zu randomisieren.

arc4random_addrandom wird von arc4random_stir verwendet, um den internen Zufallszahlenpool entsprechend den übergebenen Daten aufzufüllen.

Wenn Sie nicht über diese Funktionen verfügen, sich aber unter Unix befinden, 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_init-Funktion öffnet das /dev/urandom-Gerät und setzt den Dateideskriptor in urandom_fd

Die urandom-Funktion ist im Grunde dasselbe wie ein Aufruf von Rand, außer sicherer, und gibt eine long (leicht änderbar) zurück.

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

Wenn Ihr System keinen /dev/urandom hat, aber über eine /dev/random- oder eine ähnliche Datei verfügt, können Sie einfach den übergebenen Pfad in open in urandom_init ändern. Die Aufrufe und APIs, die in urandom_init und urandom verwendet werden, sind (meiner Meinung nach) POSIX-kompatibel und sollten daher auf den meisten, wenn nicht allen POSIX-kompatiblen Systemen funktionieren.

Hinweise: Ein Lesen von /dev/urandom wird 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 /dev/random, der bei unzureichender Entropie immer blockiert.

Wenn Sie sich auf einem anderen System befinden (d. H. Windows), verwenden Sie Rand oder eine interne, Windows-spezifische, plattformabhängige, nicht portierbare API.

Wrapper-Funktion für urandom-, Rand- oder arc4random-Aufrufe:

#define Rand_IMPL /* urandom(see large code block) | Rand | arc4random */

int myRandom(int bottom, int top){
    return (Rand_IMPL() % (top - bottom)) + bottom;
}
9
Élektra

Für C existiert keine STL. Sie müssen Rand oder besser random aufrufen. Diese sind im Standard-Bibliothekskopf stdlib.h deklariert. Rand ist POSIX, random ist eine BSD-Spezifikationsfunktion.

Der Unterschied zwischen Rand und random besteht darin, dass random eine viel nutzbare 32-Bit-Zufallszahl zurückgibt und Rand normalerweise eine 16-Bit-Zahl zurückgibt. Die BSD-Manpages zeigen, dass die unteren Bits von Rand zyklisch und vorhersagbar sind, sodass Rand für kleine Zahlen möglicherweise unbrauchbar ist.

8
dreamlax

Werfen Sie einen Blick auf ISAAC (Indirektion, Shift, Accumulate, Add und Count). Sie ist gleichmäßig verteilt und hat eine durchschnittliche Zykluslänge von 2 8295.

7
geofftnz

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

4
Paul Sonier

FWIW, die Antwort lautet: ja, es gibt eine stdlib.h-Funktion namens Rand; Diese Funktion ist hauptsächlich 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 vorhersagbar sind, aber viel langsamer laufen. Diese sollten in jeder Art von sicherheitsbezogener Anwendung verwendet werden.

4
tylerl

Dies ist hoffentlich etwas zufälliger als nur 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());
}
4
kenorb

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

int Rand(void);

void srand(unsigned seed);

Beide sind Teil von ANSI C. Außerdem gibt es die Funktion random():

long random(void);

Soweit ich das beurteilen kann, ist random() kein Standard-ANSI-C. Eine Bibliothek eines Drittanbieters ist zwar keine schlechte Idee, aber es hängt alles davon ab, wie zufällig eine Zahl generiert werden muss.

3
Chris Lutz

C Programm zur Generierung von Zufallszahlen 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 sagen r ∈ [lowerLimit, upperLimit)  

3

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));
}

Das erste Mal ausgeben: 39

Das zweite Mal ausgeben: 61

Das dritte Mal ausgeben: 65

Sie können die Werte nach randnum in die von Ihnen gewählten Zahlen ändern und es wird eine Zufallszahl für Sie zwischen diesen beiden Zahlen generiert.

3
coderperson

Rand() ist der bequemste Weg, Zufallszahlen zu generieren.

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

2
Namit Sinha
#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]);
}
2
Muhammad Sadiq

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

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
Serge Rogatch
#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
Bisu vs Utsab

In meiner letzten Anwendung hatte ich ein ernsthaftes Problem mit dem Pseudo-Zufallszahlengenerator: Ich rief mein C-Programm wiederholt über ein Pyhton-Skript an und verwendete den folgenden Code als Startwert:

srand(time(NULL))

Jedoch seit:

  • Rand erzeugt dieselbe Pseudo-Zufallssequenz, die denselben Samen in srand liefert (siehe man srand);
  • Wie bereits erwähnt, ändert sich die Zeitfunktion nur von Sekunde zu Sekunde: Wenn Ihre Anwendung mehrmals in derselben Sekunde ausgeführt wird, gibt time jedes Mal den gleichen Wert zurück.

Mein Programm hat die gleiche Zahlenfolge generiert. Sie können dieses Problem mit 3 Schritten lösen:

  1. mischzeitausgabe mit einigen anderen Informationen, die sich während der Ausführung ä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 war clock_gettime 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 gewährleistet (soweit ich weiß) die beste Zufallsverteilung, aber es kann nur bei sehr schneller Anwendung einen Unterschied ausmachen . Option 2 ist meiner Meinung nach eine sichere Sache.

0
Koldar
#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
shellhub

Trotz all der Leute, die hier Rand() vorschlagen, möchten Sie Rand() nur verwenden, wenn Sie müssen! Die Zufallszahlen, die Rand() erzeugt, sind oft sehr schlecht. So zitieren Sie auf der Linux-Manpage:

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

In Bezug auf die Portabilität wird random() schon seit geraumer Zeit vom POSIX-Standard definiert. Rand() ist älter, es erschien bereits in der ersten POSIX.1-Spezifikation (IEEE Std 1003.1-1988), während random() erstmals in POSIX.1-2001 (IEEE Std 1003.1-2001) erschien, der aktuelle POSIX-Standard jedoch bereits POSIX.1 ist -2008 (IEEE Std 1003.1-2008), das vor einem Jahr ein Update erhalten hat (IEEE Std 1003.1-2008, Ausgabe 2016). Ich würde random() als sehr portabel betrachten.

POSIX.1-2001 hat auch die Funktionen lrand48() und mrand48() eingeführt, siehe hier

Diese Familie von Funktionen soll Pseudozufallszahlen mit einem linearen Kongruenzalgorithmus und einer 48-Bit-Ganzzahlarithmetik erzeugen.

Und eine ziemlich gute Pseudo-Zufallsquelle ist die Funktion arc4random(), die auf vielen Systemen verfügbar ist. Kein Bestandteil eines offiziellen Standards, wurde in BSD um 1997 veröffentlicht, aber Sie können ihn auf Systemen wie Linux und Mac OS/iOS finden.

0
Mecki

Eine gute Erklärung zu hören, warum die Verwendung von Rand() zur Erzeugung gleichmäßig verteilter Zufallszahlen in einem bestimmten Bereich eine schlechte Idee ist, beschloss ich, einen Blick darauf zu werfen, wie schief die Ausgabe 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 Sie Ihre Zufallszahlen benötigen, aber die obigen Angaben erscheinen für die meisten Bedürfnisse gleich genug.

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

0
Mouse