Cでランダムな整数を生成する関数はありますか?または、サードパーティのライブラリを使用する必要がありますか?
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.
time()
。1秒に1回だけ変更されます。からシードする場合time()
、への呼び出しrand()
ごとに、1秒間のすべての呼び出しで同じ値を取得します。しかし、より大きな理由は、そのrand()
ようなプロパティとそのような関数が、1回の呼び出しごとではなく、実行ごとに1回だけシードされるユースケースで最もよく知られていることです。テストされていない、または証明されていないプロパティの「ランダム性」に依存すると、問題が発生します。
rand()
通常はこれです)の@trusktrは、rand()
せいぜいまったく効果がなく、最悪の場合、ジェネレーターの既知の品質が損なわれます。これは深いテーマです。数学と落とし穴への最良の入門として、乱数についてのKnuth Vol 2 Chapter 3 を読むことから始めてください。
srand((unsigned int)time(NULL));
のrand()
関数<stdlib.h>
は、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;
}
}
%
はモジュラス演算子です。それはあなたに整数除算の残りをx % n
与えるので、常に0
との間の数を与えますn - 1
(x
そしてn
が両方とも正である限り)。あなたはまだ混乱していたプログラムの書き込みしようとしていることが判明した場合i
、0から100までカウントし、プリントアウトしi % n
、いくつかのためにn
100よりもあなたの選択小さいの
さまざまなプログラミング言語で安全に乱数を生成する方法で説明されているように、次のいずれかを実行する必要があります。
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()
暗号的に安全で公平です。
sodium_init()
するだけです。RNGについては心配しないでください。RNGはカーネルを使用しています。
sodium_init()
は重要な詳細であるため、必ずしも私の例の一部ではありませんが、承認しました。
これを見ていきましょう。まず、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;
}
else{ low_num=max_num; hi_num=min_num+1;
2)の場合に失敗するhi_num - low_num > INT_MAX
。3)まれな状況で値を省略しますINT_MAX > hi_num - low_num > RAND_MAX
。
hi_num = max_num + 1;
オーバーフローに対する保護が不足しています。
stdlib
提供しているものよりも高品質の疑似乱数が必要な場合は、Mersenne Twisterをチェックしてください。それも速いです。たとえば、ここに示すように、サンプル実装は豊富です。
標準の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がそれらを整数または浮動小数点に変換する方法を模倣しようとします。
整数の場合、我々はモジュロバイアスを避けたいです。からランダムな4桁の整数を取得しrand() % 10000
たrand()
が、0〜32767しか返せないとする(Microsoft Windowsの場合と同様)。0から2767までの各数値は、2768から9999までの各数値よりも頻繁に表示されます。バイアスを取り除くにはrand()
、2768から32767までの30000の値が0から9999。
floatの場合、a double
は53ビットの精度を保持するため、53のランダムビットが必要です(IEEEのdoubleであると想定)。53ビットを超える場合、丸めバイアスが発生します。一部のプログラマーはのようなコードを記述しますがrand() / (double)RAND_MAX
、rand()
Windowsでは31ビットまたは15ビットのみを返す場合があります。
OpenSSLはRAND_bytes()
、おそらく/dev/urandom
Linuxで読むことによって、種をまきます。多くの乱数が必要な場合、それらを/dev/urandom
カーネルからコピーする必要があるため、からすべてを読み取るのは遅すぎます。OpenSSLがシードからより多くの乱数を生成できるようにする方が高速です。
乱数の詳細:
srand()
。読み取れない場合は、現在の時刻からのビット、プロセスID、およびいくつかのポインターを混合します/dev/urandom
。float
/ を処理するために追加の解釈をしたのはあなただけだったことに注意してください。そのため、数字が広すぎるのを避けるためdouble
にint
数字に固執するように質問を明確にしました。特にfloat
/ double
ランダム値を扱う他のCの質問があるので、stackoverflow.com
システム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
次の規則に従うランダムな32ビット符号なし整数を返します0 <= arc4random_uniform(limit) < limit
。ここで、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
、しかしん持っている/dev/random
か、似たファイルを、あなたは単にに渡すパス変更することができますopen
ではurandom_init
。で使用され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;
}
C用のSTLは存在しません。を呼び出すrand
必要がありrandom
ます。これらは、標準ライブラリヘッダーで宣言されていますstdlib.h
。rand
はPOSIXでrandom
あり、BSD仕様の関数です。
差rand
とは、random
すなわちrandom
、はるかに使用可能な32ビットの乱数戻り、そしてrand
典型的には16ビットの数を返します。BSDのマンページは、の下位ビットrand
が循環的で予測可能であることを示しているrand
ため、小さい数では役に立たない可能性があります。
extern int rand(void);
ありextern void srand(unsigned int);
ます。
これは、選択した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
を任意の数値に変更でき、2つの数値の間の乱数が生成されます。
使いたいrand()
。注意(非常に重要):rand関数のシードを設定してください。そうしないと、乱数は真にランダムではありません。これは非常に、非常に、非常に重要です。ありがたいことに、通常、システムティックタイマーと日付の組み合わせを使用して、適切なシードを取得できます。
これは、を使用するよりも少しランダムであることを願っています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());
}
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)と言います。
#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<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()
して、特定の範囲内で一様に分布した乱数を生成することが悪い考えであるという良い説明を聞いて、出力が実際にどの程度歪んでいるかを調べることにしました。私のテストケースは公正なサイコロ投げでした。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%
乱数をどの程度均一にする必要があるかはわかりませんが、上記はほとんどのニーズに十分に均一に見えます。
編集:より良いものでPRNGを初期化することをお勧めしますtime(NULL)
。
最近のアプリケーションで疑似乱数ジェネレーターに深刻な問題がありました:pyhtonスクリプトを介してCプログラムを繰り返し呼び出し、次のコードをシードとして使用していました:
srand(time(NULL))
ただし、次の理由から:
man srand
ます(を参照)。time
毎回同じ値を返します。私のプログラムは同じ数列を生成しました。この問題を解決するには、3つのことを行うことができます。
実行時に変更される他のいくつかの情報と時間出力を混合します(私のアプリケーションでは、出力名):
srand(time(NULL) | getHashOfString(outputName))
時間分解能を上げます。私のプラットフォームで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は、(私の知る限り)最高のシードのランダム性を保証しますが、非常に高速なアプリケーションでのみ違いが生じる可能性があります。私の意見では、オプション2は安全な賭けです。
rand()
暗号化データには使用しないでください。同意します。少なくとも私にとって、私のアプリケーションには暗号化データが含まれていなかったので、与えられた方法で問題ありませんでした。
rand()
ここでのすべての人々の提案にもかかわらず、あなたがする必要がrand()
ない限り、あなたは使いたくないです!rand()
多くの場合、生成される乱数は非常に悪いものです。Linuxのmanページから引用するには:
バージョン
rand()
及びsrand()
LinuxのCライブラリでは同じ乱数発生器を使用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 Edition)。だから私はrandom()
非常に移植性があると考えます。
POSIX.1-2001はlrand48()
およびmrand48()
関数も導入しました。ここを参照してください:
この関数ファミリは、線形合同アルゴリズムと48ビット整数演算を使用して疑似乱数を生成します。
そして、かなり良い疑似ランダムソースは、arc4random()
多くのシステムで利用できる機能です。公式規格の一部ではなく、1997年頃にBSDで登場しましたが、LinuxやmacOS / iOSなどのシステムで見つけることができます。
random()
Windowsには存在しません。
rand()
、C標準でも要求されているため、サポートしているだけです。それ以外の場合は、通常どおり、Windows専用の特別なソリューションが必要です。#ifdef _WIN32
は、Windowsをサポートする必要があるクロスプラットフォームコードで最も頻繁に表示されるフレーズです。通常、すべてのシステムで機能するソリューションと、Windowsにのみ必要なソリューションがあります。
Linux Cアプリケーションの場合:
これは、私のCコードのプラクティスに従い、任意のサイズのランダムバッファーを返す(適切な戻りコードなどを含む)上記の回答からの再加工されたコードです。urandom_open()
プログラムの最初に必ず1回呼び出してください。
int gUrandomFd = -1;
int urandom_open(void)
{
if (gUrandomFd == -1) {
gUrandomFd = open("/dev/urandom", O_RDONLY);
}
if (gUrandomFd == -1) {
fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
errno, strerror(errno));
return -1;
} else {
return 0;
}
}
void urandom_close(void)
{
close(gUrandomFd);
gUrandomFd = -1;
}
//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
int ret = 0; // Return value
if (gUrandomFd == -1) {
fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
return -1;
}
ret = read(gUrandomFd, buf, size);
if (ret != size) {
fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
ret, size);
return -1;
} else {
return 0;
}
}
これを試してみてください、私はすでに上で参照されたいくつかの概念からそれをまとめました:
/*
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
*/
int random(int max) {
srand((unsigned) time(NULL));
return (rand() % max) + 1;
}
srand()
をかけたいときに電話をかけるのrand()
はひどい考えです。time()
通常、秒単位で値を返すため、この関数をすばやく呼び出すと、同じ「ランダムな」値が返されます。
random()
関数と混同されます。