Cで乱数を生成する機能はありますか?それとも、私は第三者の図書館を使わなければなりませんか?
注意 :セキュリティのために
Rand()
を使わないでください。暗号的に安全な番号が必要な場合は、 代わりにこの回答 を参照).
#include <time.h>
#include <stdlib.h>
srand(time(NULL)); // Initialization, should only be called once.
int r = Rand(); // Returns a pseudo-random integer between 0 and Rand_MAX.
編集:Linuxでは、 ランダムかつsrandom )を使うほうがいいかもしれません。
<stdlib.h>
のRand()
関数は、0からRand_MAX
の間の疑似乱数整数を返します。 srand(unsigned int seed)
を使ってシードを設定することができます。
異なる範囲を取得するには、Rand()
と一緒に%
演算子を使用するのが一般的な方法です(ただし、これにより統一性がいくらか低下することに注意してください)。例えば:
/* random int between 0 and 19 */
int r = Rand() % 20;
あなたが 本当に 均一性を気にかけているなら、あなたはこのようなことをすることができます:
/* Returns an integer in the range [0, n).
*
* Uses Rand(), and so is affected-by/affects the same seed.
*/
int randint(int n) {
if ((n - 1) == Rand_MAX) {
return Rand();
} else {
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to Rand()
assert (n <= Rand_MAX)
// Chop off all of the values that would cause skew...
int end = Rand_MAX / n; // truncate skew
assert (end > 0);
end *= n;
// ... and ignore results from Rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ((r = Rand()) >= end);
return r % n;
}
}
さまざまなプログラミング言語で安全に乱数を生成する方法 で説明されているように、次のいずれかを実行する必要があります。
randombytes
APIを使用する/dev/urandom
ではなく/dev/random
を使用してください。 OpenSSL(または他のユーザースペースPRNG)ではありません。例えば:
#include "sodium.h"
int foo()
{
char myString[32];
uint32_t myInt;
if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */
return 1;
}
/* myString will be an array of 32 random bytes, not null-terminated */
randombytes_buf(myString, 32);
/* myInt will be a random number between 0 and 9 */
myInt = randombytes_uniform(10);
}
randombytes_uniform()
は暗号学的に安全で公平です。
これを見てみましょう。まず、srand()関数を使ってランダマイザをシードします。基本的に、コンピュータはsrand()に渡される数に基づいて乱数を生成できます。同じシード値を指定した場合は、毎回同じ乱数が生成されます。
したがって、常に変化する値をランダマイザにシードする必要があります。これを行うには、time()関数を使って現在時刻の値を渡します。
さて、Rand()を呼び出すと、毎回新しい乱数が生成されます。
#include <stdio.h>
int random_number(int min_num, int max_num);
int main(void)
{
printf("Min : 1 Max : 40 %d\n", random_number(1,40));
printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
return 0;
}
int random_number(int min_num, int max_num)
{
int result = 0, low_num = 0, hi_num = 0;
if (min_num < max_num)
{
low_num = min_num;
hi_num = max_num + 1; // include max_num in output
} else {
low_num = max_num + 1; // include max_num in output
hi_num = min_num;
}
srand(time(NULL));
result = (Rand() % (hi_num - low_num)) + low_num;
return result;
}
あなたがstdlib
が提供するものより良い品質の擬似乱数を必要とするならば、 Mersenne Twister を調べてください。それも速いです。サンプル実装は豊富です、例えば here 。
標準のC関数はRand()
です。ソリティアにカードを配るには十分ですが、ひどいです。 Rand()
の多くの実装は、短い数のリストを循環し、下位ビットのサイクルは短くなります。一部のプログラムがRand()
を呼び出す方法はひどく、srand()
に渡す適切なシードを計算するのは困難です。
Cで乱数を生成する最良の方法は、OpenSSLなどのサードパーティライブラリを使用することです。例えば、
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/Rand.h>
/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
union {
unsigned int i;
unsigned char c[sizeof(unsigned int)];
} u;
do {
if (!Rand_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
} while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
return u.i % limit;
}
/* Random double in [0.0, 1.0) */
double random_double() {
union {
uint64_t i;
unsigned char c[sizeof(uint64_t)];
} u;
if (!Rand_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
/* 53 bits / 2**53 */
return (u.i >> 11) * (1.0/9007199254740992.0);
}
int main() {
printf("Dice: %d\n", (int)(random_uint(6) + 1));
printf("Double: %f\n", random_double());
return 0;
}
なぜそんなに多くのコードがあるのですか? JavaやRubyなどの他の言語には、ランダムな整数または浮動小数点の関数があります。 OpenSSLはランダムバイトしか与えないため、JavaまたはRubyが整数または浮動小数点数に変換する方法を模倣しようとします。
整数の場合、modulo biasを避けたいです。 Rand() % 10000
からランダムな4桁の整数を取得したが、Rand()
が返すことができるのは0〜32767のみです(Microsoft Windowsの場合)。 0〜2767の各数値は、2768〜9999の各数値よりも頻繁に表示されます。バイアスを取り除くには、2768〜32767の30000の値が0の10000の値に均一にマッピングされるため、値が2768未満のときにRand()
を再試行できます9999に。
double
は53ビットの精度を保持するため、浮動小数点の場合、53のランダムビットが必要です(IEEEの倍精度であると仮定)。 53ビット以上を使用すると、丸めバイアスが発生します。一部のプログラマーはRand() / (double)Rand_MAX
のようなコードを記述しますが、Rand()
は31ビットのみ、またはWindowsでは15ビットのみを返す場合があります。
OpenSSLのRand_bytes()
は、おそらくLinuxで/dev/urandom
を読み取ることで、それ自体をシードします。多くの乱数が必要な場合、それらをカーネルからコピーする必要があるため、/dev/urandom
からすべてを読み取るのは遅すぎます。 OpenSSLがシードからより多くの乱数を生成できるようにする方が高速です。
乱数の詳細:
srand()
のCでシードを計算する方法の例です。 /dev/urandom
を読み取れない場合は、現在の時間のビット、プロセスID、およびいくつかのポインターを混合します。あなたのシステムが arc4random
ファミリーの関数をサポートしているなら、私はそれらの代わりに標準のRand
関数を使うことを勧めます。
arc4random
ファミリーには以下が含まれます。
uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)
arc4random
は、ランダムな32ビット符号なし整数を返します。
arc4random_buf
は、パラメータbuf : void *
にランダムな内容を入れます。コンテンツの量はbytes : size_t
パラメータによって決まります。
arc4random_uniform
は、規則0 <= arc4random_uniform(limit) < limit
に従うランダムな32ビット符号なし整数を返します。ここで、limitも符号なし32ビット整数です。
arc4random_stir
は/dev/urandom
からデータを読み取り、そのデータをarc4random_addrandom
に渡して、内部の乱数プールをさらにランダム化します。
arc4random_addrandom
は、渡されたデータに従って内部乱数プールを生成するためにarc4random_stir
によって使用されます。
あなたがこれらの機能を持っていなくても、あなたがUnix上にいるならば、あなたはこのコードを使うことができます:
/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */
int urandom_fd = -2;
void urandom_init() {
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd == -1) {
int errsv = urandom_fd;
printf("Error opening [/dev/urandom]: %i\n", errsv);
exit(1);
}
}
unsigned long urandom() {
unsigned long buf_impl;
unsigned long *buf = &buf_impl;
if (urandom_fd == -2) {
urandom_init();
}
/* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
read(urandom_fd, buf, sizeof(long));
return buf_impl;
}
urandom_init
関数は/dev/urandom
デバイスを開き、ファイル記述子をurandom_fd
に入れます。
urandom
関数は、より安全であることを除けば、基本的にRand
の呼び出しと同じですが、long
(簡単に変更可能)を返します。
ただし、/dev/urandom
は少し遅くなる可能性があるため、別の乱数ジェネレータのシードとして使用することをお勧めします。
あなたのシステムが/dev/urandom
を持っていなくても、doesが/dev/random
やそれに似たファイルを持っているのなら、urandom_init
でopen
に渡されるパスを変更することができます。 urandom_init
とurandom
で使用されている呼び出しとAPIは(私は信じていますが)POSIXに準拠しているので、すべてのPOSIX準拠システムではないとしてもほとんどのシステムで動作するはずです。
注意:利用可能なエントロピーが十分でない場合、/dev/urandom
からの読み取りはブロックされません。そのため、このような状況下で生成された値は暗号的に安全でない可能性があります。それが心配な場合は、/dev/random
を使用してください。エントロピーが不十分な場合は常にブロックされます。
もしあなたが他のシステム(つまりWindows)を使っているのなら、Rand
かいくつかのWindows固有のプラットフォーム依存の移植性のないAPIを使ってください。
urandom
、Rand
、またはarc4random
呼び出しのラッパー関数
#define Rand_IMPL /* urandom(see large code block) | Rand | arc4random */
int myRandom(int bottom, int top){
return (Rand_IMPL() % (top - bottom)) + bottom;
}
STLはCには存在しません。Rand
、あるいはもっと良いのはrandom
を呼び出す必要があります。これらは標準ライブラリヘッダstdlib.h
で宣言されています。 Rand
はPOSIX、random
はBSD仕様の関数です。
Rand
とrandom
の違いは、random
はもっと使いやすい32ビットの乱数を返し、Rand
は通常16ビットの数値を返すということです。 BSDのマンページでは、Rand
の下位ビットは巡回的で予測可能であることが示されているので、Rand
は少数の人にとっては役に立たない可能性があります。
_ isaac _ (間接指定、シフト、累積、追加、およびカウント)を見てください。一様分布で、平均サイクル長は2 ^ 8295です。
あなたはRand()
を使いたいのです。注( 非常に重要 ):必ずRand関数のシードを設定してください。そうでなければ、あなたの乱数は 真にランダムではない です。これはとても、とても、とても重要です。ありがたいことに、あなたはたいてい良いティックを得るためにシステムティックタイマーと日付の何らかの組み合わせを使うことができます。
確かに、その答えは、はい、Rand
と呼ばれるstdlib.h
関数があるということです。この機能は、予測不可能性のためではなく、主に速度と分散のために調整されています。さまざまな言語およびフレームワーク用のほとんどすべての組み込みランダム関数は、デフォルトでこの関数を使用しています。予測がはるかに難しいが、実行がはるかに遅い「暗号化」乱数ジェネレータもあります。これらはあらゆるセキュリティ関連のアプリケーションで使用されるべきです。
これはsrand(time(NULL))
を使うよりも少しランダムなことを願います。
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
srand(Rand());
for (int i = 0; i < 10; i++)
printf("%d\n", Rand());
}
STLはCではなくC++なので、私はあなたが望むものがわかりません。 Cが欲しいなら、Rand()
とsrand()
関数があります。
int Rand(void);
void srand(unsigned seed);
これらはどちらもANSI Cの一部です。random()
関数もあります。
long random(void);
しかし、私が言うことができる限り、random()
は標準的なANSI Cではありません。サードパーティのライブラリは悪い考えではないかもしれませんが、それはすべてあなたが本当に生成する必要がある数字のランダム性次第です。
C 9から50の間の乱数を生成するプログラム
#include <time.h>
#include <stdlib.h>
int main()
{
srand(time(NULL));
int lowerLimit = 10, upperLimit = 50;
int r = lowerLimit + Rand() % (upperLimit - lowerLimit);
printf("%d", r);
}
一般に、 lowerLimitとupperLimit-1の間の乱数を生成することができます
すなわち、lowerLimitは包括的である、または r∈[lowerLimit、upperLimit)
これはあなたが選んだ2つの数字の間の乱数を得るための良い方法です。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define randnum(min, max) \
((Rand() % (int)(((max) + 1) - (min))) + (min))
int main()
{
srand(time(NULL));
printf("%d\n", randnum(1, 70));
}
初回出力:39
2回目の出力:61
3回目の出力:65
あなたはrandnum
の後の値をあなたが選んだどんな数字にでも変更することができ、そしてそれはそれら二つの数字の間にあなたのために乱数を生成するでしょう。
Rand()
は乱数を生成するための最も便利な方法です。
Random.orgのようなオンラインサービスから乱数をキャッチすることもできます。
#include <stdio.h>
#include <stdlib.h>
void main()
{
int visited[100];
int randValue, a, b, vindex = 0;
randValue = (Rand() % 100) + 1;
while (vindex < 100) {
for (b = 0; b < vindex; b++) {
if (visited[b] == randValue) {
randValue = (Rand() % 100) + 1;
b = 0;
}
}
visited[vindex++] = randValue;
}
for (a = 0; a < 100; a++)
printf("%d ", visited[a]);
}
最新のx86_64 CPUでは、 _rdrand64_step()
によってハードウェア乱数ジェネレータを使用できます
コード例:
#include <immintrin.h>
uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
// Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number
#include <stdio.h>
#include <dos.h>
int random(int range);
int main(void)
{
printf("%d", random(10));
return 0;
}
int random(int range)
{
struct time t;
int r;
gettime(&t);
r = t.ti_sec % range;
return r;
}
私の最近のアプリケーションで疑似乱数発生器に重大な問題がありました。私はpyhtonスクリプトを介してCプログラムを繰り返し呼び出し、次のコードをシードとして使用していました。
srand(time(NULL))
しかし、
man srand
を参照)。time
は毎回同じ値を返します。私のプログラムは同じ数列を生成しました。あなたはこの問題を解決するために3つのことをすることができます:
実行時に変化する他の情報と時間出力を混ぜ合わせます(私のアプリケーションでは、出力名)。
srand(time(NULL) | getHashOfString(outputName))
ハッシュ関数として djb2 を使用しました。
時間分解能を上げます。私のプラットフォームではclock_gettime
が利用可能でしたので、それを使用します。
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec);
両方の方法を一緒に使用します。
#include<time.h>
struct timespec nanos;
clock_gettime(CLOCK_MONOTONIC, &nanos)
srand(nanos.tv_nsec | getHashOfString(outputName));
オプション3はあなたが(私の知る限りでは)最善のシードのランダム性を保証しますが、それは非常に速いアプリケーションでのみ違いを生み出すかもしれません。
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//generate number in range [min,max)
int random(int min, int max){
int number = min + Rand() % (max - min);
return number;
}
//Driver code
int main(){
srand(time(NULL));
for(int i = 1; i <= 10; i++){
printf("%d\t", random(10, 100));
}
return 0;
}
すべての人がここでRand()
を提案しているにもかかわらず、あなたがしなければならないのであれば、あなたはRand()
を使いたくありません! Rand()
が生成する乱数はしばしば非常に悪いです。 Linuxのmanページから引用するには:
Linux Cライブラリの
Rand()
とsrand()
のバージョンは、random(3)
とsrandom(3)
と同じ乱数ジェネレータを使っているので、下位ビットは上位ビットと同じくらいランダムであるべきです。しかし、より古いRand()の実装、および異なるシステムでの現在の実装では、下位ビットは上位ビットよりもはるかにランダム性が低いです。優れたランダム性が必要な場合は、移植性を目的としたアプリケーションでこの関数を使用しないでください。 ( 代わりにrandom(3)
を使用してください。 )
移植性に関しては、random()
もかなり長い間POSIX標準によって定義されています。 Rand()
はより古く、それは最初のPOSIX.1仕様(IEEE Std 1003.1-1988)にすでに現れましたが、random()
はPOSIX.1-2001(IEEE Std 1003.1-2001)にはじめて登場しましたが、現在のPOSIX標準はすでにPOSIX.1です-2008(IEEE Std 1003.1-2008)、ちょうど1年前に更新されました(IEEE Std 1003.1-2008、2016年版)。それで私はrandom()
を非常に移植性があると考えるでしょう。
POSIX.1-2001はlrand48()
とmrand48()
関数も導入しました。 ここを見てください :
このファミリの関数は、線形合同アルゴリズムと48ビット整数演算を使用して擬似乱数を生成します。
そして、かなり良い擬似乱数ソースは、多くのシステムで利用可能なarc4random()
関数です。 1997年頃にBSDに登場した公式の標準の一部ではありませんが、LinuxやmacOS/iOSのようなシステムで見つけることができます。
Rand()
を使って一定の範囲の一様に分布した乱数を生成するのはよくない説明ですが、出力が実際にどの程度歪んでいるのかを見てみることにしました。私のテストケースは公正なダイス投げでした。これがCコードです。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
int i;
int dice[6];
for (i = 0; i < 6; i++)
dice[i] = 0;
srand(time(NULL));
const int TOTAL = 10000000;
for (i = 0; i < TOTAL; i++)
dice[(Rand() % 6)] += 1;
double pers = 0.0, tpers = 0.0;
for (i = 0; i < 6; i++) {
pers = (dice[i] * 100.0) / TOTAL;
printf("\t%1d %5.2f%%\n", dice[i], pers);
tpers += pers;
}
printf("\ttotal: %6.2f%%\n", tpers);
}
そしてその出力は次のとおりです。
$ gcc -o t3 t3.c
$ ./t3
1666598 16.67%
1668630 16.69%
1667682 16.68%
1666049 16.66%
1665948 16.66%
1665093 16.65%
total: 100.00%
$ ./t3
1667634 16.68%
1665914 16.66%
1665542 16.66%
1667828 16.68%
1663649 16.64%
1669433 16.69%
total: 100.00%
私はあなたがあなたの乱数がどのくらい均一である必要があるかを知りませんが、上記はほとんどのニーズに対して十分に均一に見えます。
編集:time(NULL)
以上のものでPRNGを初期化するのは良い考えです。