Graphviz 14.1.2~dev.20260118.1035
Loading...
Searching...
No Matches
random.c
Go to the documentation of this file.
1
3
4#include "config.h"
5
6#include <assert.h>
7#include <limits.h>
8#include <stdint.h>
9#include <stdlib.h>
10#include <string.h>
11#include <util/alloc.h>
12#include <util/gv_math.h>
13#include <util/random.h>
14
15int *gv_permutation(int bound) {
16 if (bound <= 0) {
17 return NULL;
18 }
19
20 // initialize a sequence `{0, 1, …, bound - 1}`
21 int *const p = gv_calloc((size_t)bound, sizeof(int));
22 for (int i = 0; i < bound; i++) {
23 p[i] = i;
24 }
25
26 // perform a Fisher-Yates shuffle
27 for (int i = bound - 1; i > 0; --i) {
28 const int j = gv_random(i + 1);
29 SWAP(&p[i], &p[j]);
30 }
31
32 return p;
33}
34
36static int random_small(int bound) {
37 assert(bound > 0);
38 assert(bound <= RAND_MAX);
39
40 // The interval `[0, RAND_MAX]` is not necessarily neatly divided into
41 // `bound`-sized chunks. E.g. using a bound of 3 with a `RAND_MAX` of 7:
42 // ┌───┬───┬───┬───┬───┬───┬───┬───┐
43 // │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │
44 // └───┴───┴───┴───┴───┴───┴───┴───┘
45 // ◄─────────► ◄─────────► ◄─────►
46 // 3 values 3 values 2 values
47 // To guarantee a uniform distribution, derive the upper bound of the last
48 // complete chunk (5 in the example above), above which we discard and
49 // resample to avoid pulling from the partial trailing chunk.
50 const int discard_threshold =
51 RAND_MAX - (int)(((unsigned)RAND_MAX + 1) % (unsigned)bound);
52
53 int r;
54 do {
55 r = rand();
56 } while (r > discard_threshold);
57
58 return r % bound;
59}
60
61uint64_t gv_random_u64(uint64_t bound) {
62 assert(bound > 0);
63
64 // See comment in `random_small`, but note that our maximum generated value
65 // here will be `UINT64_MAX` instead of `RAND_MAX`. Note that we need to do a
66 // slightly different calculation to avoid the integer overflow that would
67 // otherwise result from `UINT64_MAX + 1`.
68 const uint64_t discard_threshold =
69 UINT64_MAX - (UINT64_MAX - bound + 1) % bound;
70
71 uint64_t r;
72 do {
73 // generate a random `uint64_t` value
74 r = 0;
75 for (size_t i = 0; i < sizeof(uint64_t); ++i) {
76 // `RAND_MAX ≥ 32767` is guaranteed, so `random_small(256)` is safe
77 const uint8_t byte = (uint8_t)random_small((int)UINT8_MAX + 1);
78 memcpy((char *)&r + i, &byte, sizeof(byte));
79 }
80 } while (r > discard_threshold);
81
82 return r % bound;
83}
84
85int gv_random(int bound) {
86 assert(bound > 0);
87
88 if (bound > RAND_MAX) {
89 _Static_assert(INT_MAX <= UINT64_MAX,
90 "the `int` type includes non-negative values that do not "
91 "fit in a `uint64_t`, hence some `int` values can never be "
92 "returned by `gv_random_u64`");
93 return (int)gv_random_u64((uint64_t)bound);
94 }
95 return random_small(bound);
96}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
#define UINT8_MAX
Definition gmlscan.c:337
node NULL
Definition grammar.y:181
Arithmetic helper functions.
#define SWAP(a, b)
Definition gv_math.h:134
static int random_small(int bound)
handle random number generation, bound ≤ RAND_MAX
Definition random.c:36
int gv_random(int bound)
Definition random.c:85
uint64_t gv_random_u64(uint64_t bound)
Definition random.c:61
int * gv_permutation(int bound)
Definition random.c:15
random number generation