web-dev-qa-db-ja.com

Cでランダムなintを生成する方法?

Cで乱数を生成する機能はありますか?それとも、私は第三者の図書館を使わなければなりませんか?

506
Kredns

注意 :セキュリティのために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 )を使うほうがいいかもしれません。

596
Łukasz Lew

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

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

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

例えば:

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

43

これを見てみましょう。まず、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;
}
28
Abhay Budakoti

あなたがstdlibが提供するものより良い品質の擬似乱数を必要とするならば、 Mersenne Twister を調べてください。それも速いです。サンプル実装は豊富です、例えば here

25
MH114

標準の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がシードからより多くの乱数を生成できるようにする方が高速です。

乱数の詳細:

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

あなたのシステムが 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_initopenに渡されるパスを変更することができます。 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;
}
9
Élektra

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

Randrandomの違いは、randomはもっと使いやすい32ビットの乱数を返し、Randは通常16ビットの数値を返すということです。 BSDのマンページでは、Randの下位ビットは巡回的で予測可能であることが示されているので、Randは少数の人にとっては役に立たない可能性があります。

8
dreamlax

_ isaac _ (間接指定、シフト、累積、追加、およびカウント)を見てください。一様分布で、平均サイクル長は2 ^ 8295です。

7
geofftnz

あなたはRand()を使いたいのです。注( 非常に重要 ):必ずRand関数のシードを設定してください。そうでなければ、あなたの乱数は 真にランダムではない です。これはとても、とても、とても重要です。ありがたいことに、あなたはたいてい良いティックを得るためにシステムティックタイマーと日付の何らかの組み合わせを使うことができます。 

4
Paul Sonier

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

4
tylerl

これは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());
}
4
kenorb

STLはCではなくC++なので、私はあなたが望むものがわかりません。 Cが欲しいなら、Rand()srand()関数があります。

int Rand(void);

void srand(unsigned seed);

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

long random(void);

しかし、私が言うことができる限り、random()は標準的なANSI Cではありません。サードパーティのライブラリは悪い考えではないかもしれませんが、それはすべてあなたが本当に生成する必要がある数字のランダム性次第です。

3
Chris Lutz

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)  

3

これはあなたが選んだ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の後の値をあなたが選んだどんな数字にでも変更することができ、そしてそれはそれら二つの数字の間にあなたのために乱数を生成するでしょう。

3
coderperson

Rand()は乱数を生成するための最も便利な方法です。

Random.orgのようなオンラインサービスから乱数をキャッチすることもできます。

2
Namit Sinha
#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]);
}
2
Muhammad Sadiq

最新の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
Serge Rogatch
#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
Bisu vs Utsab

私の最近のアプリケーションで疑似乱数発生器に重大な問題がありました。私はpyhtonスクリプトを介してCプログラムを繰り返し呼び出し、次のコードをシードとして使用していました。

srand(time(NULL))

しかし、

  • Randはsrandに同じシードを与える同じ擬似乱数列を生成します(man srandを参照)。
  • すでに述べたように、time関数は秒から2秒しか変化しません。アプリケーションが同じ秒内に複数回実行されると、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はあなたが(私の知る限りでは)最善のシードのランダム性を保証しますが、それは非常に速いアプリケーションでのみ違いを生み出すかもしれません。

0
Koldar
#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
shellhub

すべての人がここで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のようなシステムで見つけることができます。

0
Mecki

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を初期化するのは良い考えです。

0
Mouse