Как генерировать случайное 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-разрядный с << и |.