Как генерировать случайное 64-битное целое число без знака в C
Мне нужно генерировать случайные 64-разрядные целые числа без знака, используя C. Я имею в виду, что диапазон должен быть от 0
до 18446744073709551615
. RAND_MAX
- 1073741823
.
Я нашел некоторые решения в ссылках, которые могут быть дублирующимися, но ответы в основном объединяют некоторые результаты rand()
или делают некоторые дополнительные арифметические операции. Таким образом, результаты всегда состоят из 18 цифр или 20 цифр. Мне также нужны результаты, такие как 5
, 11
, 33387
, а не только 3771778641802345472
.
Кстати, у меня действительно не так много опыта работы с C, но любой подход, примеры кода и идея могут быть полезными.
Ответы
Ответ 1
Относительно "Итак, результаты всегда состоят из 18 цифр или 20 цифр".
См. комментарий @Томас. Если вы генерируете случайные числа достаточно долго, код будет создавать такие, как 5, 11 и 33387. Если код генерирует 1 000 000 000 номеров в секунду, это может занять год как очень маленькие числа < 100 000 настолько редки среди всех 64-битных чисел.
rand()
простой возвращает случайные биты. Простейший метод затягивает 1 бит за раз
uint64_t rand_uint64_slow(void) {
uint64_t r = 0;
for (int i=0; i<64; i++) {
r = r*2 + rand()%2;
}
return r;
}
Предполагая, что RAND_MAX
является некоторой степенью 2 - 1, как в случае OP 1073741823 == 0x3FFFFFFF
, используйте преимущество, которое генерируется каждый раз каждые 30 бит. Следующий код вызовет rand()
3 раза - немного расточительно. Вместо этого биты, сдвинутые, могут быть сохранены для следующего случайного числа, но это приводит к другим проблемам. Оставьте это на другой день.
uint64_t rand_uint64(void) {
uint64_t r = 0;
for (int i=0; i<64; i += 30) {
r = r*((uint64_t)RAND_MAX + 1) + rand();
}
return r;
}
Метод подсчета переносимых циклов позволяет избежать 30
#if RAND_MAX/256 >= 0xFFFFFFFFFFFFFF
#define LOOP_COUNT 1
#elif RAND_MAX/256 >= 0xFFFFFF
#define LOOP_COUNT 2
#elif RAND_MAX/256 >= 0x3FFFF
#define LOOP_COUNT 3
#elif RAND_MAX/256 >= 0x1FF
#define LOOP_COUNT 4
#else
#define LOOP_COUNT 5
#endif
uint64_t rand_uint64(void) {
uint64_t r = 0;
uint64_t r = 0;
for (int i=LOOP_COUNT; i > 0; i--) {
r = r*(RAND_MAX + (uint64_t)1) + rand();
}
return r;
}
Эффекты автокорреляции, прокомментированные здесь, вызваны слабым rand()
. C не указывает конкретный метод генерации случайных чисел. Вышеизложенное полагается на rand()
. Если rand()
является подпараметром, тогда код должен использовать для этого другие генераторы и rand()
.
Ответ 2
Если вам не нужны криптографически безопасные псевдослучайные числа, я бы предложил использовать MT19937-64. Это 64-разрядная версия Mersenne Twister PRNG.
Пожалуйста, не комбинируйте выходы rand()
и не строите на других трюках. Использовать существующую реализацию:
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html
Ответ 3
Iff у вас есть достаточно хороший источник случайных байтов (например,/dev/random или /dev/urandom на машине linux), вы можете просто потреблять 8 байтов из этого источника и объединить их. Если они независимы и имеют линейное распределение, вы настроены.
Если вы этого не сделаете, вы МОЖЕТЕ уйти, сделав то же самое, но в вашем псевдослучайном генераторе, вероятно, будут какие-то артефакты, которые дают ногу для всех видов привет-дзиньков.
Пример кода, предполагающий, что у нас есть открытый двоичный FILE *source
:
/* Implementation #1, slightly more elegant than looping yourself */
uint64_t 64bitrandom()
{
uint64_t rv;
size_t count;
do {
count = fread(&rv, sizeof(rv), 1, source);
} while (count != 1);
return rv;
}
/* Implementation #2 */
uint64_t 64bitrandom()
{
uint64_t rv = 0;
int c;
for (i=0; i < sizeof(rv); i++) {
do {
c = fgetc(source)
} while (c < 0);
rv = (rv << 8) | (c & 0xff);
}
return rv;
}
Если вы замените "читать случайные байты из устройства случайности" на "получить байты от вызова функции", все, что вам нужно сделать, это отрегулировать сдвиги в методе # 2.
У вас гораздо больше шансов получить "число со многими цифрами", чем "с небольшим числом цифр" (из всех чисел от 0 до 2 ** 64, примерно 95% имеют 19 или более десятичных цифр, так что действительно это то, что вы в основном получите.
Ответ 4
Я пробовал этот код здесь и, похоже, там отлично работает.
#include <time.h>
#include <stdlib.h>
#include <math.h>
int main(){
srand(time(NULL));
int a = rand();
int b = rand();
int c = rand();
int d = rand();
long e = (long)a*b;
e = abs(e);
long f = (long)c*d;
f = abs(f);
long long answer = (long long)e*f;
printf("value %lld",answer);
return 0;
}
Я выполнил несколько итераций, и я получил следующие результаты:
значение 1869044101095834648
значение 2104046041914393000
значение 1587782446298476296
стоимость 604955295827516250
значение 41152208336759610
значение 57792837533816000
Ответ 5
Если вы не возражаете против повторяющейся псевдослучайной последовательности (эй, для 64 бит вы не заметите повторение в вашей жизни;), и вы можете иметь дело с одним значением меньше, чем запрошенный диапазон (0 isn ' t произойдет), LCG или MCG - это удивительно простое решение. Википедия: линейный конгруэнтный генератор Перейдите в здесь, чтобы сгенерировать пара простых чисел и использовать один для модуля, а другой для множителя ниже. (оговорка: эта последовательность будет догадаться и, следовательно, небезопасна)
#include <stdio.h>
#include <stdint.h>
uint64_t
mcg64(void)
{
static uint64_t i = 1;
return (i = (164603309694725029ull * i) % 14738995463583502973ull);
}
int
main(int ac, char * av[])
{
for (int i = 0; i < 10; i++)
printf("%016p\n", mcg64());
}
Ответ 6
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
unsigned long long int randomize(unsigned long long int uint_64);
int main(void)
{
srand(time(0));
unsigned long long int random_number = randomize(18446744073709551615);
printf("%llu\n",random_number);
random_number = randomize(123);
printf("%llu\n",random_number);
return 0;
}
unsigned long long int randomize(unsigned long long int uint_64)
{
char buffer[100] , data[100] , tmp[2];
//convert llu to string,store in buffer
sprintf(buffer, "%llu", uint_64);
//store buffer length
size_t len = strlen(buffer);
//x : store converted char to int, rand_num : random number , index of data array
int x , rand_num , index = 0;
//condition that prevents the program from generating number that is bigger input value
bool Condition = 0;
//iterate over buffer array
for( int n = 0 ; n < len ; n++ )
{
//store the first character of buffer
tmp[0] = buffer[n];
tmp[1] = '\0';
//convert it to integer,store in x
x = atoi(tmp);
if( n == 0 )
{
//if first iteration,rand_num must be less than or equal to x
rand_num = rand() % ( x + 1 );
//if generated random number does not equal to x,condition is true
if( rand_num != x )
Condition = 1;
//convert character that corrosponds to integer to integer and store it in data array;increment index
data[index] = rand_num + '0';
index++;
}
//if not first iteration,do the following
else
{
if( Condition )
{
rand_num = rand() % ( 10 );
data[index] = rand_num + '0';
index++;
}
else
{
rand_num = rand() % ( x + 1 );
if( rand_num != x )
Condition = 1;
data[index] = rand_num + '0';
index++;
}
}
}
data[index] = '\0';
char *ptr ;
//convert the data array to unsigned long long int
unsigned long long int ret = _strtoui64(data,&ptr,10);
return ret;
}
Ответ 7
Если у вас есть 32 или 16-разрядное случайное значение - сгенерируйте 2 или 4 рандома и объедините их в один 64-разрядный с <<
и |
.