4

[0.0, 1.0) の範囲で一様分布を取得したい

可能であれば、実装で/dev/urandom からのランダム バイトを使用できるようにしてください。

あなたのソリューションがthread-safeであればそれもいいでしょう。不明な場合は、その旨をご指示ください。

他の回答を読んだ後に考えた解決策を見てください。

4

6 に答える 6

4

これはかなり良い方法のようです:

unsigned short int r1, r2, r3;
// let r1, r2 and r3 hold random values
double result = ldexp(r1, -48) + ldexp(r2, -32) + ldexp(r3, -16);

これは、NetBSD の drand48 実装に基づいています。

于 2008-09-29T17:40:58.843 に答える
3

単純:IEEEを想定した場合、doubleの精度は52ビットです。したがって、52ビット(またはそれ以上)の符号なし乱数を生成し(たとえば、dev / urandomからバイトを読み取ることにより)、それをdoubleに変換し、2 ^(ビット数)で除算します。

これにより、52番目の2進数まで、数値的に均一な分布(値が特定の範囲内にある確率が範囲に比例する)が得られます。

複雑:ただし、[0,1)の範囲には、上記では生成できないdouble値が多数あります。具体的には、[0,0.5)の範囲の半分の値(最下位ビットが設定されている値)は発生しません。[0,0.25)の範囲の値の4分の3(少なくとも2ビットのいずれかが設定されている値)などは発生しません。2^-51未満の正の値が1つだけ可能です。ダブルはそのような値の数十億を表すことができますが。したがって、指定された範囲全体で完全な精度まで真に均一であるとは言えません。

もちろん、同じ確率でこれらのdoubleの1つを選択することは望ましくありません。その場合、結果の数は平均して小さすぎるためです。結果が特定の範囲内にある確率は、その範囲に比例する必要がありますが、どの範囲で機能するかについてはより高い精度が必要です。

以下の作品だと思います。私はこのアルゴリズムを特に研究またはテストしていません(おそらくコードがないことからわかるように)。個人的には、有効であることを示す適切な参照を見つけずに使用することはありません。しかし、ここに行きます:

  • 指数を52から開始し、52ビットのランダムな符号なし整数を選択します(仮数が52ビットであると想定)。
  • 整数の最上位ビットが0の場合は、指数を1増やし、整数を1つ左にシフトし、最下位ビットを新しいランダムビットで埋めます。
  • 最も重要な場所で1をヒットするか、指数がダブルに対して大きくなりすぎるまで繰り返します(1023。またはおそらく1022)。
  • 1が見つかった場合は、値を2^exponentで割ります。すべてゼロになった場合は、0を返します(これは実際には特別なケースではありませんが、0が返される可能性が非常に低いことを強調しています[編集:実際には特別なケースである可能性があります-生成するかどうかによって異なりますそうでない場合は、行に十分な0があれば、残っているものをすべて破棄して0を返します。ただし、実際には、ランダムなソースがランダムでない限り、これは無視できるほどありそうにありません。

このようなランダムダブルの実用性が実際にあるかどうかはわかりませんが、気に留めておいてください。ランダムの定義は、その目的にある程度依存する必要があります。しかし、その重要なビットの52個すべてがランダムであるという利点を得ることができる場合、これは実際に役立つ可能性があります。

于 2008-09-29T01:13:40.557 に答える
1

ファイルからの読み取りはスレッドセーフなAFAIKであるため、 fopen() を使用して /dev/urandom から読み取ると、「真にランダムな」バイトが生成されます。

潜在的な落とし穴があるかもしれませんが、整数としてアクセスされるそのようなバイトのセットは、そのサイズの最大整数で割ると、ほぼその分布で 0 と 1 の間の浮動小数点値が得られると思います。

例えば:

#include <limits.h>
#include <stdint.h>
#include <stdio.h>
...
FILE* f = fopen("/dev/urandom", "r");
uint32_t i;
fread(&i, sizeof(i), 1, f);  // check return value in real world code!!
fclose(f);
double theRandomValue = i / (double) (UINT32_MAX);
于 2008-09-28T18:21:21.640 に答える
0
#include <stdlib.h>
printf("%f\n", drand48());

/dev/ランダム:

double c;
fd = open("/dev/random", O_RDONLY);
unsigned int a, b;
read(fd, &a, sizeof(a));
read(fd, &b, sizeof(b));
if (a > b)
   c = fabs((double)b / (double)a);
else
    c = fabs((double)a / (double)b);

c はランダムな値です

于 2008-09-28T18:19:17.980 に答える
0

/dev/urandom は POSIX ではないため、一般には利用できません。

[0,1) で double を一様に生成する標準的な方法は、[0,2^N) の範囲で整数を生成し、2^N で割ることです。お気に入りの乱数ジェネレーターを選んで使用してください。シミュレーションの場合、私のものはMersenne Twisterです。これは、非常に高速ですが、まだ十分に相関していないためです。実際、それはあなたのためにこれを行うことができ、さらに小さい数値に対してより高い精度を与えるバージョンもあります. 通常、最初にシードを与えます。これは、デバッグや他の人に結果を表示するための再現性に役立ちます。もちろん、乱数が指定されていない場合は、シードとして /dev/urandom から乱数を取得するコードを作成できます。

暗号化の目的で、代わりに標準の暗号化ライブラリのいずれかを使用する必要があります ( opensslなど)。これは、利用可能な場合は実際に /dev/urandom を使用します。

スレッド セーフに関しては、少なくとも標準インターフェイスではほとんどの場合そうではありません。そのため、レイヤーを上に構築するか、1 つのスレッドでのみ使用する必要があります。スレッドセーフのものは、変更する状態を提供するため、代わりに、相互作用しない複数の乱数ジェネレーターを効果的に実行しているため、探しているものではない可能性があります。

于 2008-09-29T18:02:43.223 に答える
0

秘訣は、要件を満たす 54 ビットのランダマイザーが必要なことです。これらの 54 ビットを仮数に固定する共用体を含む数行のコードで、数値が得られます。トリックはダブルフロートではなく、希望するランダマイザーです。

于 2008-09-28T18:25:45.327 に答える