How to generate a random integer number from within a range

后端 未结 11 1226
隐瞒了意图╮
隐瞒了意图╮ 2020-11-21 23:57

This is a follow on from a previously posted question:

How to generate a random number in C?

I wish to be able to generate a random number from within a part

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

    As said before modulo isn't sufficient because it skews the distribution. Heres my code which masks off bits and uses them to ensure the distribution isn't skewed.

    static uint32_t randomInRange(uint32_t a,uint32_t b) {
        uint32_t v;
        uint32_t range;
        uint32_t upper;
        uint32_t lower;
        uint32_t mask;
    
        if(a == b) {
            return a;
        }
    
        if(a > b) {
            upper = a;
            lower = b;
        } else {
            upper = b;
            lower = a; 
        }
    
        range = upper - lower;
    
        mask = 0;
        //XXX calculate range with log and mask? nah, too lazy :).
        while(1) {
            if(mask >= range) {
                break;
            }
            mask = (mask << 1) | 1;
        }
    
    
        while(1) {
            v = rand() & mask;
            if(v <= range) {
                return lower + v;
            }
        }
    
    }
    

    The following simple code lets you look at the distribution:

    int main() {
    
        unsigned long long int i;
    
    
        unsigned int n = 10;
        unsigned int numbers[n];
    
    
        for (i = 0; i < n; i++) {
            numbers[i] = 0;
        }
    
        for (i = 0 ; i < 10000000 ; i++){
            uint32_t rand = random_in_range(0,n - 1);
            if(rand >= n){
                printf("bug: rand out of range %u\n",(unsigned int)rand);
                return 1;
            }
            numbers[rand] += 1;
        }
    
        for(i = 0; i < n; i++) {
            printf("%u: %u\n",i,numbers[i]);
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 00:51

    Wouldn't you just do:

    srand(time(NULL));
    int r = ( rand() % 6 ) + 1;
    

    % is the modulus operator. Essentially it will just divide by 6 and return the remainder... from 0 - 5

    0 讨论(0)
  • 2020-11-22 00:53
    unsigned int
    randr(unsigned int min, unsigned int max)
    {
           double scaled = (double)rand()/RAND_MAX;
    
           return (max - min +1)*scaled + min;
    }
    

    See here for other options.

    0 讨论(0)
  • 2020-11-22 00:56

    Following on from @Ryan Reich's answer, I thought I'd offer my cleaned up version. The first bounds check isn't required given the second bounds check, and I've made it iterative rather than recursive. It returns values in the range [min, max], where max >= min and 1+max-min < RAND_MAX.

    unsigned int rand_interval(unsigned int min, unsigned int max)
    {
        int r;
        const unsigned int range = 1 + max - min;
        const unsigned int buckets = RAND_MAX / range;
        const unsigned int limit = buckets * range;
    
        /* Create equal size buckets all in a row, then fire randomly towards
         * the buckets until you land in one of them. All buckets are equally
         * likely. If you land off the end of the line of buckets, try again. */
        do
        {
            r = rand();
        } while (r >= limit);
    
        return min + (r / buckets);
    }
    
    0 讨论(0)
  • 2020-11-22 01:00

    All the answers so far are mathematically wrong. Returning rand() % N does not uniformly give a number in the range [0, N) unless N divides the length of the interval into which rand() returns (i.e. is a power of 2). Furthermore, one has no idea whether the moduli of rand() are independent: it's possible that they go 0, 1, 2, ..., which is uniform but not very random. The only assumption it seems reasonable to make is that rand() puts out a Poisson distribution: any two nonoverlapping subintervals of the same size are equally likely and independent. For a finite set of values, this implies a uniform distribution and also ensures that the values of rand() are nicely scattered.

    This means that the only correct way of changing the range of rand() is to divide it into boxes; for example, if RAND_MAX == 11 and you want a range of 1..6, you should assign {0,1} to 1, {2,3} to 2, and so on. These are disjoint, equally-sized intervals and thus are uniformly and independently distributed.

    The suggestion to use floating-point division is mathematically plausible but suffers from rounding issues in principle. Perhaps double is high-enough precision to make it work; perhaps not. I don't know and I don't want to have to figure it out; in any case, the answer is system-dependent.

    The correct way is to use integer arithmetic. That is, you want something like the following:

    #include <stdlib.h> // For random(), RAND_MAX
    
    // Assumes 0 <= max <= RAND_MAX
    // Returns in the closed interval [0, max]
    long random_at_most(long max) {
      unsigned long
        // max <= RAND_MAX < ULONG_MAX, so this is okay.
        num_bins = (unsigned long) max + 1,
        num_rand = (unsigned long) RAND_MAX + 1,
        bin_size = num_rand / num_bins,
        defect   = num_rand % num_bins;
    
      long x;
      do {
       x = random();
      }
      // This is carefully written not to overflow
      while (num_rand - defect <= (unsigned long)x);
    
      // Truncated division is intentional
      return x/bin_size;
    }
    

    The loop is necessary to get a perfectly uniform distribution. For example, if you are given random numbers from 0 to 2 and you want only ones from 0 to 1, you just keep pulling until you don't get a 2; it's not hard to check that this gives 0 or 1 with equal probability. This method is also described in the link that nos gave in their answer, though coded differently. I'm using random() rather than rand() as it has a better distribution (as noted by the man page for rand()).

    If you want to get random values outside the default range [0, RAND_MAX], then you have to do something tricky. Perhaps the most expedient is to define a function random_extended() that pulls n bits (using random_at_most()) and returns in [0, 2**n), and then apply random_at_most() with random_extended() in place of random() (and 2**n - 1 in place of RAND_MAX) to pull a random value less than 2**n, assuming you have a numerical type that can hold such a value. Finally, of course, you can get values in [min, max] using min + random_at_most(max - min), including negative values.

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