How to generate a random int in C?

后端 未结 27 2080
故里飘歌
故里飘歌 2020-11-22 00:31

Is there a function to generate a random int number in C? Or will I have to use a third party library?

相关标签:
27条回答
  • 2020-11-22 00:46

    STL doesn't exist for C. You have to call rand, or better yet, random. These are declared in the standard library header stdlib.h. rand is POSIX, random is a BSD spec function.

    The difference between rand and random is that random returns a much more usable 32-bit random number, and rand typically returns a 16-bit number. The BSD manpages show that the lower bits of rand are cyclic and predictable, so rand is potentially useless for small numbers.

    0 讨论(0)
  • 2020-11-22 00:46
    #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;
    }
    
    0 讨论(0)
  • 2020-11-22 00:50

    The standard C function is rand(). It's good enough to deal cards for solitaire, but it's awful. Many implementations of rand() cycle through a short list of numbers, and the low bits have shorter cycles. The way that some programs call rand() is awful, and calculating a good seed to pass to srand() is hard.

    The best way to generate random numbers in C is to use a third-party library like OpenSSL. For example,

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

    Why so much code? Other languages like Java and Ruby have functions for random integers or floats. OpenSSL only gives random bytes, so I try to mimic how Java or Ruby would transform them into integers or floats.

    For integers, we want to avoid modulo bias. Suppose that we got some random 4 digit integers from rand() % 10000, but rand() can only return 0 to 32767 (as it does in Microsoft Windows). Each number from 0 to 2767 would appear more often than each number from 2768 to 9999. To remove the bias, we can retry rand() while the value is below 2768, because the 30000 values from 2768 to 32767 map uniformly onto the 10000 values from 0 to 9999.

    For floats, we want 53 random bits, because a double holds 53 bits of precision (assuming it's an IEEE double). If we use more than 53 bits, we get rounding bias. Some programmers write code like rand() / (double)RAND_MAX, but rand() might return only 31 bits, or only 15 bits in Windows.

    OpenSSL's RAND_bytes() seeds itself, perhaps by reading /dev/urandom in Linux. If we need many random numbers, it would be too slow to read them all from /dev/urandom, because they must be copied from the kernel. It is faster to allow OpenSSL to generate more random numbers from a seed.

    More about random numbers:

    • Perl's Perl_seed() is an example of how to calculate a seed in C for srand(). It mixes bits from the current time, the process ID, and some pointers, if it can't read /dev/urandom.
    • OpenBSD's arc4random_uniform() explains modulo bias.
    • Java API for java.util.Random describes algorithms for removing bias from random integers, and packing 53 bits into random floats.
    0 讨论(0)
  • 2020-11-22 00:51

    This is hopefully a bit more random than just using 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());
    }
    
    0 讨论(0)
  • 2020-11-22 00:51

    For Linux C applications:

    This is my reworked code from an answer above that follows my C code practices and returns a random buffer of any size (with proper return codes, etc.). Make sure to call urandom_open() once at the beginning of your program.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:52

    Well, STL is C++, not C, so I don't know what you want. If you want C, however, there is the rand() and srand() functions:

    int rand(void);
    
    void srand(unsigned seed);
    

    These are both part of ANSI C. There is also the random() function:

    long random(void);
    

    But as far as I can tell, random() is not standard ANSI C. A third-party library may not be a bad idea, but it all depends on how random of a number you really need to generate.

    0 讨论(0)
提交回复
热议问题