73

C++ で疑似乱数ジェネレーターを初期化する「良い」方法が必要です。次のような記事を見つけました。

乱数のような数値を生成するために、srand は通常、実行時間に関連する値など、特定の値に初期化されます。たとえば、関数 time によって返される値 (ヘッダー ctime で宣言) は 1 秒ごとに異なります。これは、ほとんどのランダム化のニーズに対して十分に特徴的です。

Unixtime は、私のアプリケーションには十分に特徴的ではありません。これを初期化するより良い方法は何ですか? 移植可能であればボーナス ポイントですが、コードは主に Linux ホストで実行されます。

int を取得するために pid/unixtime の計算を行うか、/dev/urandom.

ありがとう!

編集

はい、実際にアプリケーションを 1 秒間に複数回起動していますが、衝突が発生しました。

4

15 に答える 15

71

これは、頻繁に (1 秒間に複数回) 実行できる小さなコマンド ライン プログラムに使用したものです。

unsigned long seed = mix(clock(), time(NULL), getpid());

ミックスは次のとおりです。

// Robert Jenkins' 96 bit Mix Function
unsigned long mix(unsigned long a, unsigned long b, unsigned long c)
{
    a=a-b;  a=a-c;  a=a^(c >> 13);
    b=b-c;  b=b-a;  b=b^(a << 8);
    c=c-a;  c=c-b;  c=c^(b >> 13);
    a=a-b;  a=a-c;  a=a^(c >> 12);
    b=b-c;  b=b-a;  b=b^(a << 16);
    c=c-a;  c=c-b;  c=c^(b >> 5);
    a=a-b;  a=a-c;  a=a^(c >> 3);
    b=b-c;  b=b-a;  b=b^(a << 10);
    c=c-a;  c=c-b;  c=c^(b >> 15);
    return c;
}
于 2008-11-27T09:20:01.827 に答える
63

最良の答えは<random>です。C ++ 11より前のバージョンを使用している場合は、代わりにBoost乱数のものを見ることができます。

しかし、私たちが話している場合、rand()最良srand()
単純な方法は単に使用することです:time()

int main()
{
    srand(time(nullptr));

    ...
}

これは、プログラムの最初に行うようにしてください。電話をかけるたびに行う必要はありませんrand()


サイドノート:

:以下のコメントには、これが安全でないことについての議論があります(これは真実ですが、最終的には関連性がありません(続きを読む))。/dev/randomしたがって、別の方法は、ランダムデバイス(または他の安全な実数(er)乱数ジェネレーター)からシードすることしかし:これであなたを誤った安心感に陥らせないでください。これはrand()私たちが使用しているものです。見事に生成されたシードを使用してシードした場合でも、予測可能です(値がある場合は、次の値の完全なシーケンスを予測できます)。"pseudo"これは、ランダムな値を生成する場合にのみ役立ち

「安全」が必要な場合は、おそらく使用する必要があります<random>(ただし、セキュリティ情報のあるサイトでもう少し読んでみます)。出発点として以下の回答を参照してください:https ://stackoverflow.com/a/29190957/14065より良い回答については。

二次的な注意:ランダムデバイスを使用すると、実際には、以下の元の提案よりも1秒あたり複数のコピーを開始する問題が解決されます(セキュリティの問題ではありません)。


元の話に戻る:

起動するたびに、time()は一意の値を返します(アプリケーションを1秒間に複数回起動しない限り)。32ビットシステムでは、60年程度ごとにしか繰り返されません。

時間が十分にユニークだとは思わないことは知っていますが、それは信じがたいことです。しかし、私は間違っていることが知られています。

アプリケーションの多数のコピーを同時に開始する場合は、より細かい解像度のタイマーを使用できます。ただし、値が繰り返されるまでの期間が短くなるリスクがあります。

OK、それで、あなたが本当にあなたが1秒間に複数のアプリケーションを始めていると思うなら。
次に、タイマーでより細かい粒子を使用します。

 int main()
 {
     struct timeval time; 
     gettimeofday(&time,NULL);

     // microsecond has 1 000 000
     // Assuming you did not need quite that accuracy
     // Also do not assume the system clock has that accuracy.
     srand((time.tv_sec * 1000) + (time.tv_usec / 1000));

     // The trouble here is that the seed will repeat every
     // 24 days or so.

     // If you use 100 (rather than 1000) the seed repeats every 248 days.

     // Do not make the MISTAKE of using just the tv_usec
     // This will mean your seed repeats every second.
 }
于 2008-11-27T05:30:14.160 に答える
16

より良い乱数ジェネレーターが必要な場合は、libc rand を使用しないでください。/dev/random代わりに、または直接のようなものを使用して/dev/urandomください(直接またはそのようなものから直接読み取りますint)。

libc rand の唯一の本当の利点は、シードが与えられると予測可能になり、デバッグに役立つことです。

于 2008-11-27T04:54:18.413 に答える
13

Windows の場合:

srand(GetTickCount());

time()ミリ秒単位よりも優れたシードを提供します。

于 2008-11-27T08:34:04.197 に答える
9

C++11random_device

妥当な品質が必要な場合は、そもそも rand() を使用しないでください。<random>ライブラリを使用する必要があります。さまざまな品質/サイズ/パフォーマンスのトレードオフ、再入可能性、事前定義されたディストリビューション用のさまざまなエンジンなど、多くの優れた機能を提供するため、それらを誤解することはありません。実装によっては、非決定論的なランダム データ (/dev/random など) に簡単にアクセスできる場合もあります。

#include <random>
#include <iostream>

int main() {
    std::random_device r;
    std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
    std::mt19937 eng(seed);

    std::uniform_int_distribution<> dist{1,100};

    for (int i=0; i<50; ++i)
        std::cout << dist(eng) << '\n';
}

engランダム性のソースです。ここではメルセンヌ ツイスターの組み込み実装で​​す。random_device を使用してそれをシードします。これは適切な実装では非決定論的な RNG であり、seed_seq を使用して 32 ビットを超えるランダム データを結合します。たとえば、libc++ では、random_device はデフォルトで /dev/urandom にアクセスします (代わりに別のファイルにアクセスすることもできます)。

次に、ランダム性のソースが与えられた場合に、ディストリビューションを繰り返し呼び出すと、1 から 100 までの整数の均一なディストリビューションが生成されるようなディストリビューションを作成します。次に、ディストリビューションを繰り返し使用して結果を出力します。

于 2012-10-22T03:12:20.030 に答える
7

最良の方法は、別の疑似乱数ジェネレーターを使用することです。Mersenne twister (および Wichmann-Hill) が私のお勧めです。

http://en.wikipedia.org/wiki/Mersenne_twister

于 2008-11-27T04:55:09.847 に答える
7

mozilla コードで unix_random.c ファイルを参照することをお勧めします。( mozilla/security/freebl/ だと思います ...) freebl ライブラリにあるはずです。

そこでは、システムコール情報(pwd、netstat ....など)を使用して乱数のノイズを生成します。ほとんどのプラットフォームをサポートするように書かれています(ボーナスポイントを得ることができます:D)。

于 2008-11-27T06:07:32.667 に答える
6

自問しなければならない本当の質問は、必要なランダム性の品質です。

libc random はLCGです

ランダム性の品質は、srand にどのような入力を提供しても低くなります。

異なるインスタンスが異なる初期化を持つことを確認する必要があるだけの場合は、プロセス ID (getpid)、スレッド ID、およびタイマーを混在させることができます。結果を xor で混ぜます。ほとんどのアプリケーションでは、エントロピーで十分です。

例 :

struct timeb tp;
ftime(&tp);   
srand(static_cast<unsigned int>(getpid()) ^ 
static_cast<unsigned int>(pthread_self()) ^ 
static_cast<unsigned int >(tp.millitm));

ランダム品質を向上させるには、/dev/urandom を使用します。boost::thread と boost::date_time を使用して、上記のコードを移植可能にすることができます。

于 2008-11-27T13:24:01.490 に答える
3

c++11ジョナサン・ライトによるトップ投票の投稿のバージョン:

#include <ctime>
#include <random>
#include <thread>

...

const auto time_seed = static_cast<size_t>(std::time(0));
const auto clock_seed = static_cast<size_t>(std::clock());
const size_t pid_seed =
      std::hash<std::thread::id>()(std::this_thread::get_id());

std::seed_seq seed_value { time_seed, clock_seed, pid_seed };

...
// E.g seeding an engine with the above seed.
std::mt19937 gen;
gen.seed(seed_value);
于 2015-03-22T04:20:46.963 に答える
2
#include <stdio.h>
#include <sys/time.h>
main()
{
     struct timeval tv;
     gettimeofday(&tv,NULL);
     printf("%d\n",  tv.tv_usec);
     return 0;
}

tv.tv_usec はマイクロ秒単位です。これは許容できるシードです。

于 2008-11-27T09:13:52.070 に答える
0

Visual Studioを使用している場合は、さらに別の方法があります。

#include "stdafx.h"
#include <time.h>
#include <windows.h> 

const __int64 DELTA_EPOCH_IN_MICROSECS= 11644473600000000;

struct timezone2 
{
  __int32  tz_minuteswest; /* minutes W of Greenwich */
  bool  tz_dsttime;     /* type of dst correction */
};

struct timeval2 {
__int32    tv_sec;         /* seconds */
__int32    tv_usec;        /* microseconds */
};

int gettimeofday(struct timeval2 *tv/*in*/, struct timezone2 *tz/*in*/)
{
  FILETIME ft;
  __int64 tmpres = 0;
  TIME_ZONE_INFORMATION tz_winapi;
  int rez = 0;

  ZeroMemory(&ft, sizeof(ft));
  ZeroMemory(&tz_winapi, sizeof(tz_winapi));

  GetSystemTimeAsFileTime(&ft);

  tmpres = ft.dwHighDateTime;
  tmpres <<= 32;
  tmpres |= ft.dwLowDateTime;

  /*converting file time to unix epoch*/
  tmpres /= 10;  /*convert into microseconds*/
  tmpres -= DELTA_EPOCH_IN_MICROSECS; 
  tv->tv_sec = (__int32)(tmpres * 0.000001);
  tv->tv_usec = (tmpres % 1000000);


  //_tzset(),don't work properly, so we use GetTimeZoneInformation
  rez = GetTimeZoneInformation(&tz_winapi);
  tz->tz_dsttime = (rez == 2) ? true : false;
  tz->tz_minuteswest = tz_winapi.Bias + ((rez == 2) ? tz_winapi.DaylightBias : 0);

  return 0;
}


int main(int argc, char** argv) {

  struct timeval2 tv;
  struct timezone2 tz;

  ZeroMemory(&tv, sizeof(tv));
  ZeroMemory(&tz, sizeof(tz));

  gettimeofday(&tv, &tz);

  unsigned long seed = tv.tv_sec ^ (tv.tv_usec << 12);

  srand(seed);

}

多分少しやり過ぎですが、短い間隔でうまく機能します。gettimeofday関数はここにあります

編集:さらに調査すると、rand_sはVisual Studioの優れた代替手段になる可能性があります。これは、単なる安全なrand()ではなく、まったく異なり、srandのシードを使用しません。私はそれがちょうど「より安全な」ランドとほとんど同じであると推測しました。

rand_sを使用するには、stdlib.hを含める前に#define_CRT_RAND_Sを忘れないでください。

于 2013-01-16T04:57:16.373 に答える
0

次のようなシグネチャを持つ関数があるとします。

int foo(char *p);

ランダム シードの優れたエントロピー ソースは、次のハッシュです。

  • clock_gettime下位ビットを捨てずに (秒とナノ秒) の完全な結果- それらは最も価値があります。
  • の値はp、にキャストされuintptr_tます。
  • pにキャストされたのアドレスuintptr_t

少なくとも 3 番目、場合によっては 2 番目も、利用可能な場合はシステムの ASLR からエントロピーを導出します (初期スタック アドレス、したがって現在のスタック アドレスは多少ランダムです)。

また、グローバル状態に触れないようにするためと、使用される PRNG をより細かく制御できるようにするために、rand/を完全に使用することも避けます。srandしかし、上記の手順は、使用する PRNG に関係なく、多くの作業をせずに適切なエントロピーを取得するための優れた (そしてかなり移植性のある) 方法です。

于 2011-05-10T05:19:11.830 に答える
-2

プログラムの先頭にヘッダーを含め、次のように記述します。

srand(time(NULL));

乱数を宣言する前にプログラムで。以下は、1 から 10 までの乱数を出力するプログラムの例です。

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
   //Initialize srand
   srand(time(NULL));

   //Create random number
   int n = rand() % 10 + 1;

   //Print the number
   cout << n << endl; //End the line

   //The main function is an int, so it must return a value
   return 0;
}
于 2014-12-21T21:33:29.650 に答える