问题
If the rand()
function creates a random number that is 4 bytes in length, and I wanted to create a random number that is 1024 bits in length (128 bytes), is the easiest method to get this by concatenating the rand()
function 256 times or is there an alternative method?
#include <stdio.h>
#include <string.h>
int main(void) {
const char data[128];
memset(&data, 0x36, 128);
printf("%s\n", data);
puts("");
printf("%d\n", sizeof(data)/sizeof(data[0]));
puts("");
int i = 0;
unsigned long rez = 0;
for(i = 0; i < 20; i++) {
unsigned int num = rand();
rez = rez + num;
printf("%x\n", rez);
}
printf("%x\n", rez);
return 0;
}
回答1:
is the easiest method to get this by concatenating the rand() function 256 times or is there an alternative method?
Each rand()
returns a value in the [0...RAND_MAX]
range. RAND_MAX
is limited to 32767 <= RAND_MAX <= INT_MAX
.
Very commonly RAND_MAX
is a Mersenne number of the form 2n − 1. Code can take advantage of this this very common implementation dependent value. Each rand()
call then provides RAND_MAX_BITS
and not 32 as suggested by OP for a 4-byte int
. @Matteo Italia
[See far below update]
#include <stdlib.h>
#if RAND_MAX == 0x7FFF
#define RAND_MAX_BITS 15
#elif RAND_MAX == 0x7FFFFFFF
#define RAND_MAX_BITS 31
#else
#error TBD code
#endif
Call rand()
⌈size * 8 / RAND_MAX_BITS⌉
times. This eases the number of rand()
calls needed from size
.
void rand_byte(uint8_t *dest, size_t size) {
int r_queue = 0;
int r_bit_count = 0;
for (size_t i = 0; i < size; i++) {
int r = 0;
//printf("%3zu %2d %8x\n", i, r_bit_count, r_queue);
if (r_bit_count < 8) {
int need = 8 - r_bit_count;
r = r_queue << need;
r_queue = rand();
r ^= r_queue; // OK to flip bits already saved in `r`
r_queue >>= need;
r_bit_count = RAND_MAX_BITS - need;
} else {
r = r_queue;
r_queue >>= 8;
r_bit_count -= 8;
}
dest[i] = r;
}
}
int main(void) {
uint8_t buf[128];
rand_byte(buf, sizeof buf);
...
return 0;
}
If you want the easiest bit less efficient code, simply call rand()
for each byte as answered by @dbush
[Update 2021]
@Anonymous Question Guy posted a nifty macro that returns the bit width of a Mersenne number, more generally than the #if RAND_MAX == 0x7FFF
approach above.
/* Number of bits in inttype_MAX, or in any (1<<b)-1 where 0 <= b < 3E+10 */
#define IMAX_BITS(m) ((m) /((m)%0x3fffffffL+1) /0x3fffffffL %0x3fffffffL *30 \
+ (m)%0x3fffffffL /((m)%31+1)/31%31*5 + 4-12/((m)%31+3))
_Static_assert((RAND_MAX & 1 && (RAND_MAX/2 + 1) & (RAND_MAX/2)) == 0,
"RAND_MAX is not a Mersenne number");
#define RAND_MAX_BITS IMAX_BITS(RAND_MAX)
回答2:
The C standard states that RAND_MAX
has a minimum value of 32767 (0x7fff), so it's best to work under that assumption.
Because the function will only return 15 random bits, using all the bits in one call will involve some bit shifting and masking to get the results in the proper place. The simplest way to do this would be to call rand
128 times, take the low order byte of each result, and write it to your byte array:
unsigned char rand_val[128];
for (int i=0; i<128; i++) {
rand_val[i] = rand() & 0xff;
}
Don't forget to call srand
exactly once somewhere before this in your code.
Using strcat
as you mentioned in your comment won't work because this function works on null terminated strings, and a byte containing 0 is a valid random number.
If you plan on using these random values for anything involving cryptography, you're better off using a secure random number generator. If you have OpenSSL available, use RAND_bytes
for this purpose:
unsigned char rand_val[128];
RAND_bytes(rand_val, sizeof(rand_val));
回答3:
On most POSIX (Unix-like) systems, you can also read 128 bytes from /dev/urandom
which you would open like a regular file in binary mode — even though POSIX does not specify the device.
回答4:
The properties of C rand()
are vaguely specified by the standard; as said in a comment, the number of actual usable bits depends from implementation, and their quality has been historically plagued by sub-par implementations. Also, rand()
affects the global state of the program and on many implementations is not thread safe.
Given that a there are good, known and simple PRNGs such as the ones from the XorShift family, I would just use one of them.
#include <stdint.h>
/* The state must be seeded so that it is not all zero */
uint64_t s[2];
uint64_t xorshift128plus(void) {
uint64_t x = s[0];
uint64_t const y = s[1];
s[0] = y;
x ^= x << 23;
s[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
return s[1] + y;
}
void next128bits(unsigned char ch[16]) {
uint64_t t = xorshift128plus();
memcpy(ch, &t, sizeof(t));
t = xorshift128plus();
memcpy(ch + 8, &t, sizeof(t));
}
来源:https://stackoverflow.com/questions/50819313/create-a-128-byte-random-number