web-dev-qa-db-ja.com

Cでランダムな64ビット符号なし整数を生成する方法

Cを使用してランダムな64ビット符号なし整数を生成する必要があります。つまり、範囲は_0_から_18446744073709551615_である必要があります。 _Rand_MAX_は_1073741823_です。

重複する可能性のあるいくつかの解決策をリンクで見つけましたが、答えは主にいくつかのRand()結果を連結するか、いくつかの増分算術演算を行います。したがって、結果は常に18桁または20桁になります。 _5_だけでなく、_11_、_33387_、_3771778641802345472_のような結果も必要です。

ちなみに、私はCの経験はあまりありませんが、アプローチ、コードサンプル、アイデアがあれば有益です。

13
Erdi İzgi

暗号的に安全な疑似乱数が必要ない場合は、MT19937-64を使用することをお勧めします。 Mersenne Twister PRNG の64ビットバージョンです。

Rand()出力を組み合わせたり、他のトリックに基づいて構築したりしないでください。既存の実装を使用します。

http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt64.html

4
Stas

「結果は常に18桁または20桁です」について。

@ Thomasコメント を参照してください。十分な長さの乱数を生成すると、コードは5、11、33387のようなものを作成します。コードが1,000,000,000の数/秒を生成する場合、すべての64ビット数の中で100,000未満の非常に小さい数は非常にまれであるため、1年かかる場合があります。


Rand() simpleは、ランダムビットを返します。単純な方法では、一度に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;
}
_

OPの場合のように_Rand_MAX_が2の1の累乗であると仮定すると、_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;
  for (int i=LOOP_COUNT; i > 0; i--) {
    r = r*(Rand_MAX + (uint64_t)1) + Rand();
  }
  return r;
}
_

コメントされた自己相関効果 ここ は弱いRand()によって引き起こされます。 Cは、乱数生成の特定の方法を指定していません。上記は、Rand()-または使用される基本ランダム関数-が適切であることに依存しています。

Rand()が標準以下の場合、コードは他のジェネレーターを使用する必要があります。それでも、このアプローチを使用して、より大きな乱数を作成することができます。

4
chux

ランダムバイトの十分に優れたソース(たとえば、Linuxマシンの/ dev/randomまたは/ dev/urandom)がある場合は、そのソースから8バイトを消費し、それらを連結するだけです。それらが独立していて線形分布を持っている場合、あなたは設定されています。

そうしない場合は、同じことを行うことで逃げることができますが、疑似乱数ジェネレーターには、あらゆる種類のhi-jinxに足を引っ張るアーティファクトが含まれている可能性があります。

オープンバイナリがあると仮定したサンプルコード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%が10進数で19桁以上であるため、実際にはあなたが主に得るものです。

1
Vatine

繰り返しの疑似ランダムシーケンスを気にしない場合(64ビットの場合、生涯の繰り返しに気付くことはありません;)、要求された範囲よりも1つ小さい値を処理できます(0は発生)、LCGまたはMCGは驚くほど単純なソリューションです。 ウィキペディア:線形合同法ここ に移動して、いくつかの素数を生成し、1つをモジュラスに使用し、もう1つを以下の乗数に使用します。 (注意:このシーケンスは推測可能であるため、安全ではありません)

#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());
}
0
duanev

32ビットまたは16ビットのランダム値がある場合-2つまたは4つのランダムを生成し、それらを<<および|で1つの64ビットに結合します。

uint64_t Rand_uint64(void) {
    // Assuming Rand_MAX is 2^31.
    uint64_t r = Rand();
    r = r<<30 | Rand();
    r = r<<30 | Rand();
    return r;
}
0
i486

私はこのコードを試しました ここ そしてそれはそこでうまくいくようです。

#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

0
Jibin Mathew