Cでランダムな整数を生成する方法は?


回答:


662

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では、ランダムとランダムを使用することをお勧めします。


194
単純化のために+1を使用しますが、srand()は1回だけ呼び出す必要があることを強調することをお勧めします。また、スレッド化されたアプリケーションでは、ジェネレーターの状態がスレッドごとに格納されていることを確認し、ジェネレーターをスレッドごとに1回シードすることができます。
RBerteig 2009

41
@trusktr、その複雑な。これには理由がありますtime()。1秒に1回だけ変更されます。からシードする場合time()、への呼び出しrand()ごとに、1秒間のすべての呼び出しで同じ値を取得します。しかし、より大きな理由は、そのrand()ようなプロパティとそのような関数が、1回の呼び出しごとではなく、実行ごとに1回だけシードされるユースケースで最もよく知られていることです。テストされていない、または証明されていないプロパティの「ランダム性」に依存すると、問題が発生します。
RBerteig

9
単純な線形合同ジェネレーター(rand()通常はこれです)の@trusktrは、rand()せいぜいまったく効果がなく、最悪の場合、ジェネレーターの既知の品質が損なわれます。これは深いテーマです。数学と落とし穴への最良の入門として、乱数についてのKnuth Vol 2 Chapter 3 を読むことから始めてください。
RBerteig

21
キャストでコンパイラの警告を回避する:srand((unsigned int)time(NULL));
GiovaMaster

9
これはまだPRNGを見る弱い方法であることを覚えておいてください。ちょうど去年、Linux上の暗号ロッカータイプのウイルスが時間とともにシードのミスを犯し、これにより検索スペースが劇的に減少しました。あなたがしなければならなかったすべては感染がいつ起こったかについての適切な考えを得て、それからその頃から種を試しました。最後に聞いたところによると、ランダム性の最良のソースは/ dev / urandomです。これはおそらく、ハードウェアの温度などの無秩序なソースのマッシュアップからシードされたものです。ただし、プログラムごとに実行ごとに異なる動作をさせるだけの場合は、上記の解決策で問題ありません。
Jemenake 2016

238

rand()関数<stdlib.h>は、0〜の疑似乱数整数を返しますRAND_MAXsrand(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;
  }
}

18
これは一般的な慣例ですが、正しいものではありません。これこれを見てください。
Lazer、2010

35
@Lazer:それが私が「これが多少均一性を失うことを覚えておいてください」と言った理由です。
ローレンスゴンサルベス2010

3
@AbhimanyuAryanこれ%はモジュラス演算子です。それはあなたに整数除算の残りをx % n与えるので、常に0との間の数を与えますn - 1xそしてnが両方とも正である限り)。あなたはまだ混乱していたプログラムの書き込みしようとしていることが判明した場合i、0から100までカウントし、プリントアウトしi % n、いくつかのためにn100よりもあなたの選択小さいの
ローレンス・ゴンサルベス

2
@necromancer私は先に進み、完全に均一なソリューションを追加しました。
ローレンスゴンサルベス2014

2
@Lazer投稿した2番目のリンクは、まだ完全に均一ではありません。doubleへのキャストとその逆は役に立ちません。あなたが投稿した最初のリンクは完全に均一な解決策を持っていますが、小さな上限では多くのループが発生します。私はこの回答に完全に均一なソリューションを追加しました。このソリューションは、小さな上限でもそれほどループしないはずです。
ローレンスゴンサルベス2014

53

安全なランダムな文字または整数が必要な場合:

さまざまなプログラミング言語で安全に乱数を生成する方法説明されているように、次のいずれかを実行する必要があります。

例えば:

#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() 暗号的に安全で公平です。


libsodium RNGはrandombytes_bufを呼び出す前にシードする必要がありますか?
user2199593 2017

いつか電話sodium_init()するだけです。RNGについては心配しないでください。RNGはカーネルを使用しています。
Scott Arciszewski、2017

注:最近の編集sodium_init()は重要な詳細であるため、必ずしも私の例の一部ではありませんが、承認しました。
Scott Arciszewski、

29

これを見ていきましょう。まず、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;
}

12
素晴らしいコードですが、「srand(time(NULL));」を呼び出すのは良い考えではありません。このメソッドは、forループで呼び出されたときに同じ数を生成します。
RayOldProf 2013

1
コードを含む提案された編集は、しばしば拒否されます。誰かが「アルゴリズムが間違っていました。最大値よりも大きな数を生成する可能性があります」というコメントでここに作成しました。申し立てを自分で評価していません。
マーティン・スミス

1
@マーティン・スミスの問題:1)でなければならない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
chux-モニカを2014年

1
このように再シードすると、この関数が1秒間に複数回呼び出されると、この関数は同じ数を生成します。本当に再シードしたい場合は、毎秒1回だけ再シードしてください。
エレクトラ

1
マイナー:hi_num = max_num + 1;オーバーフローに対する保護が不足しています。
chux-モニカを復活させる

25

stdlib提供しているものよりも高品質の疑似乱数が必要な場合は、Mersenne Twisterをチェックしてください。それも速いです。たとえば、ここに示すように、サンプル実装は豊富です


2
+1:見た目はかっこいいですが、推測ゲームを作成していただけです。ビジネスアプリケーションで乱数ジェネレーターを使用する場合は、間違いなくこれを使用します。
クレデンス2009年

4
Mersenne Twisterは使用せず、xoroshiro128 +やPCGなどを使用してください。(関連リンク)
Veedrac

17

標準の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() % 10000rand()が、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_MAXrand() Windowsでは31ビットまたは15ビットのみを返す場合があります。

OpenSSLはRAND_bytes()、おそらく/dev/urandomLinuxで読むことによって、種をまきます。多くの乱数が必要な場合、それらを/dev/urandomカーネルからコピーする必要があるため、からすべてを読み取るのは遅すぎます。OpenSSLがシードからより多くの乱数を生成できるようにする方が高速です。

乱数の詳細:

  • PerlのPerl_seed()は、Cのシードを計算する方法の例ですsrand()。読み取れない場合は、現在の時刻からのビット、プロセスID、およびいくつかのポインターを混合します/dev/urandom
  • OpenBSDのarc4random_uniform()は、モジュロバイアスについて説明しています。
  • java.util.RandomのJava APIは、ランダムな整数からバイアスを取り除き、53ビットをランダムな浮動小数点数にパックするアルゴリズムを記述しています。

この長い回答をありがとうございます。この質問に対する現在の24の回答のうち、あなたがfloat/ を処理するために追加の解釈をしたのはあなただけだったことに注意してください。そのため、数字が広すぎるのを避けるためdoubleint数字に固執するように質問を明確にしました。特にfloat/ doubleランダム値を扱う他のCの質問があるので、stackoverflow.com
–ur

11

システム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_addrandomarc4random_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_initurandomいる呼び出しとAPI はPOSIXに準拠している(私はそう思います)ため、すべてではないにしても、ほとんどのPOSIX準拠システムで機能するはずです。

注:/dev/urandom利用可能なエントロピーが不十分な場合、読み取りはブロックされません。そのため、このような状況で生成された値は、暗号的に安全ではない可能性があります。それが心配な場合は、を使用してください/dev/random。これは、エントロピーが不十分な場合に常にブロックします。

別のシステム(Windowsなど)を使用している場合は、randまたはWindows固有のプラットフォーム固有の非移植性のあるAPIを使用します。

ラッパーのための機能urandomrandまたはarc4random通話:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

C用のSTLは存在しません。を呼び出すrand必要がありrandomます。これらは、標準ライブラリヘッダーで宣言されていますstdlib.hrandはPOSIXでrandomあり、BSD仕様の関数です。

randとは、randomすなわちrandom、はるかに使用可能な32ビットの乱数戻り、そしてrand典型的には16ビットの数を返します。BSDのマンページは、の下位ビットrandが循環的で予測可能であることを示しているrandため、小さい数では役に立たない可能性があります。


3
@Neil-これまでのすべての回答はSTLについて言及しているため、不要な参照を削除するために質問がすばやく編集されたと思います。
マイケル・バー、

rand()は小さな数値には役に立たない-本当に必要な場合は、ビットシフトアウトして、よりランダムな上位ビットのみを使用できます。
Chris Lutz、

@Chris、乱数のサイズがわかっている場合は可能ですが、ランタイム中に必要な乱数のサイズが変化した場合(動的配列のシャッフルなど)、そのような警告を回避することは困難です。
dreamlax 2009年

私はここにランダム関数を見つけることができません:-(
kasia.b

そのリンクに@ kasia.b、とがextern int rand(void);ありextern void srand(unsigned int);ます。
ラスタジェダイ2016年

7

ISAAC(間接参照、シフト、累積、追加、カウント)をご覧ください。均一に分布し、平均サイクル長は2 ^ 8295です。


2
ISAACは高速であるため興味深いRNGですが、暗号化についてはまだ深刻な注目を集めていません。
user2398029 14

4

これは、選択した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つの数値の間の乱数が生成されます。


4

使いたいrand()。注意(非常に重要):rand関数のシードを設定してください。そうしないと、乱数は真にランダムではありません。これは非常に、非常に、非常に重要です。ありがたいことに、通常、システムティックタイマーと日付の組み合わせを使用して、適切なシードを取得できます。


6
2つのポイントa)ジェネレーターのシード方法に関係なく、乱数は「真に」ランダムではありません。また、b)疑似ランダムシーケンスが多くの状況で常に同じであると非常に便利です。

16
数値が本当にランダムであることが非常に重要な場合は、rand()関数を使用しないでください。
tylerl 2009年

1
シードを設定したかどうかに関係なく、randの値はまったく「真に」ランダムではありません。既知のシードが与えられた場合、シーケンスは予測可能です。「真の」乱数生成は困難です。randに関係するエントロピーはありません。
dreamlax 2009年

2
もちろん彼らはそうするでしょう-ジェネレータはライブラリによってあなたのためにシードされています(おそらくゼロまでですが、それは有効なシードです)。

4
ああ、でも乱数を使用するプログラムをデバッグするには、既知のアルゴリズム/既知のシードが不可欠です。より詳細な分析のために再作成できるように、シミュレーションの実行とともに使用されたシードをログに記録することは珍しいことではありません。srand()をまったく呼び出さないことは、srand(1)を呼び出すことと同じです。
RBerteig 2009

4

FWIW、答えは、はい、というstdlib.h関数がありますrand。この関数は、予測不能ではなく、主に速度と分散のために調整されています。さまざまな言語およびフレームワークのほとんどすべての組み込みランダム関数は、デフォルトでこの関数を使用します。予測可能性ははるかに低いが、実行速度がはるかに遅い「暗号化」乱数ジェネレータもあります。これらは、セキュリティ関連のあらゆる種類のアプリケーションで使用する必要があります。


4

これは、を使用するよりも少しランダムであることを願っています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());
}

1
このプログラムが1秒以内に複数回実行される場合、srand(rand())を追加してもシーケンスのランダム性は増加しません。time(NULL)はそれらのそれぞれに対して同じ値を返し、最初のrand()は同じlongを返し、srand()への2番目の呼び出しは同じ値を持つため、結果として同じランダムシーケンスになります。argcのアドレスを使用すると、このアドレスがプログラムの実行ごとに異なることが保証される場合にのみ役立つことがありますが、常にそうであるとは限りません。
theferrit32 2018年

3

まあ、STLはCではなくC ++なので、何が欲しいのかわかりません。ただし、Cが必要な場合は、rand()およびsrand()関数があります。

int rand(void);

void srand(unsigned seed);

これらはどちらもANSI Cの一部です。random()関数もあります。

long random(void);

しかし、私が知る限り、random()標準のANSI C ではありません。サードパーティのライブラリは悪い考えではないかもしれませんが、それはすべて、実際に生成する必要がある数値のランダム度に依存します。


3

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)と言います。


私は明確ではない9および50から9と50との間に言及したものだ@Pang
Shivam K. Thakkar

2
あなたのモジュロ演算はバイアスを導入しました。
jww '20年

2

rand() 乱数を生成する最も便利な方法です。

また、random.orgなどのオンラインサービスから乱数を取得することもできます。


1
またrandom.orgのような任意のオンラインサービスから乱数をキャッチすることがありますが、ポータブル、効率的なCでこれを行う方法などがあればバウンティ
MD XF

2
#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]);
}

次に、変数@ b4handで上部を定義します
Muhammad Sadiq

そのコメントを書いた時点では、私にはユニバーサル編集権限がありませんでした。一般的に、実際の回答を変更するコード変更は拒否されます。あなたがあなたの答えを修正する気がない場合でも、私はできます。
b4hand 2015年

一意の値のランダムな順列を生成することが目的である場合、このコードにはまだバグがあります。値84を2回生成し、値48は生成しません。さらに、乱数ジェネレータをシードしないため、シーケンスはすべての実行で同じです。
b4hand 2015年

1
変数aとbはこのコードで定義されています。エラーはありません。構文も論理エラーもありません。
Muhammad Sadiq 2015年

不正解です。私が述べたように、私はすでに手作業で出力を確認しました。
b4hand 2015年

1
#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;
}

1

最新の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

1
#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;
}

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)


rand()は、diehardテストなどの他のランダム性テストに失敗する可能性があります。rand()はプラットフォームごとに異なります。GNU / Linuxのrand()値は、BSDまたはWindowsの値よりも優れている場合があります。
ジョージケーラー2015

これは、ランダム性をテストする有効な方法ではありません。
Veedrac

目的と脅威/リスクモデルによって異なります。暗号的に強力なRNGの場合-確かに、RDRAND(またはRDSEED)を使用します。単純なサイコロ投げ(カジノレベルではない)の場合は、上記の条件で十分です。キーワードは「十分いい」です。
マウス

0

最近のアプリケーションで疑似乱数ジェネレーターに深刻な問題がありました:pyhtonスクリプトを介してCプログラムを繰り返し呼び出し、次のコードをシードとして使用していました:

srand(time(NULL))

ただし、次の理由から:

  • randは同じ擬似ランダムシーケンスを生成し、srandに同じシードを与えman srandます(を参照)。
  • すでに述べたように、時間関数は秒から秒にのみ変化します。アプリケーションが同じ秒内に複数回実行された場合、time毎回同じ値を返します。

私のプログラムは同じ数列を生成しました。この問題を解決するには、3つのことを行うことができます。

  1. 実行時に変更される他のいくつかの情報と時間出力を混合します(私のアプリケーションでは、出力名):

    srand(time(NULL) | getHashOfString(outputName))

    ハッシュ関数としてdjb2を使用しました

  2. 時間分解能を上げます。私のプラットフォームでclock_gettimeは、利用可能だったので、それを使用します:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. 両方の方法を一緒に使用します。

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));

オプション3は、(私の知る限り)最高のシードのランダム性を保証しますが、非常に高速なアプリケーションでのみ違いが生じる可能性があります。私の意見では、オプション2は安全な賭けです。


これらのヒューリスティックを使用しても、暗号化データをrand()に依存しないでください。
domenukk

rand()暗号化データには使用しないでください。同意し​​ます。少なくとも私にとって、私のアプリケーションには暗号化データが含まれていなかったので、与えられた方法で問題ありませんでした。
Koldar

0

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には存在しません。
ビョルンLindqvist

@BjörnLindqvistWindowsもPOSIXシステムではありません。これは、少なくとも基本的なPOSIX APIをサポートしていない市場で唯一のシステムです(iOSのようなロックダウンされたシステムでもサポートされています)。Windowsはrand()、C標準でも要求されているため、サポートしているだけです。それ以外の場合は、通常どおり、Windows専用の特別なソリューションが必要です。#ifdef _WIN32は、Windowsをサポートする必要があるクロスプラットフォームコードで最も頻繁に表示されるフレーズです。通常、すべてのシステムで機能するソリューションと、Windowsにのみ必要なソリューションがあります。
メッキー

0

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;
    }
}

-2

私の最小限のソリューションは、範囲内の乱数に対して機能するはずです[min, max)srand(time(NULL))関数を呼び出す前に使用します。

int range_rand(int min_num, int max_num) {
    if (min_num >= max_num) {
        fprintf(stderr, "min_num is greater or equal than max_num!\n"); 
    }
    return min_num + (rand() % (max_num - min_num));
} 

-3

これを試してみてください、私はすでに上で参照されたいくつかの概念からそれをまとめました:

/*    
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;
}

16
このコードは良くありません。電話srand()をかけたいときに電話をかけるのrand()はひどい考えです。time()通常、秒単位で値を返すため、この関数をすばやく呼び出すと、同じ「ランダムな」値が返されます。
高炉2013

3
この関数は、Unixのrandom()関数と混同されます。
ジョージケーラー2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.