web-dev-qa-db-ja.com

時間を使用して乱数生成をシードする代わりの方法はありますか?

コンピューティングクラスターで、コードの複数のインスタンス(2000インスタンス程度)を同時に実行しようとしています。それが機能する方法は、私がジョブを送信し、ノードがノードごとにいくつかのジョブで頻繁に開くときにクラスターがそれらを実行することです。これは、タイムシードを使用する乱数生成で、かなりの数のインスタンスに対して同じ値を生成するようです。

代わりに使用できる簡単な代替手段はありますか?再現性とセキュリティは重要ではなく、独自のシードを迅速に生成することが重要です。これに対する最も簡単なアプローチは何でしょうか。可能であれば、クロスプラットフォームアプローチが適しています。

30
CHP

rdtsc命令は、かなり信頼できる(そしてランダムな)シードです。

Windowsでは、__rdtsc()組み込み関数を介してアクセスできます。

GNU Cでは、次の方法でアクセスできます。

unsigned long long rdtsc(){
    unsigned int lo,hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return ((unsigned long long)hi << 32) | lo;
}

この命令は、プロセッサの電源がオンになってからの合計疑似サイクルを測定します。今日のマシンの頻度が高いことを考えると、2つのプロセッサが同時に起動し、同じ速度でクロックされている場合でも、2つのプロセッサが同じ値を返す可能性はほとんどありません。

26
Mysticial
unsigned seed;

read(open("/dev/urandom", O_RDONLY), &seed, sizeof seed);
srand(seed); // IRL, check for errors, close the fd, etc...

また、より優れた乱数ジェネレーターをお勧めします。

5
DigitalRoss

他のプロセスを起動するプロセスがあると思います。使用するシードを渡してもらいます。次に、そのマスタープロセスに、各プロセスの乱数を渡してシードとして使用させることができます。そうすれば、選択された任意のシードは実際には1つだけです...そのために時間を使用できます。

他のプロセスを起動するマスタープロセスがない場合、各プロセスに少なくとも一意のインデックスがある場合は、1つのプロセスで一連の乱数をメモリ(共有メモリの場合)またはファイルに生成することができます。 (共有ディスクの場合)次に、各プロセスにインデックスの乱数を引き出してシードとして使用させます。

単一のシードからの一連の乱数ほど、シードの均一な分布を提供するものはありません。

5
Brian Kennedy

PIDと時間の組み合わせは、一意のシードを取得するのに十分なはずです。 100%クロスプラットフォームではありませんが、* nixプラットフォームでは getpid(3) 、Windowsでは GetProcessId の99.9%が得られます。そこへの道。このようなものが機能するはずです:

srand((time(NULL) & 0xFFFF) | (getpid() << 16));

* nixシステムでは/dev/urandomからデータを読み取ることもできますが、Windowsでのデータに相当するものはありません。

5
Adam Rosenfield

C++ 11を使用できる場合は、std::random_deviceを検討してください。包括的なガイドについては、 リンク をご覧になることをお勧めします。

ビデオリンクからessentialメッセージを抽出する:neversrandRandを使用しますが、代わりにstd::random_devicestd::mt19937を使用します-ほとんどの場合、次のようになりますあなたが欲しい:

#include <iostream>
#include <random>
int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_int_distribution<int> dist(0,99);
    for (int i = 0; i < 16; i++) {
        std::cout << dist(mt) << " ";
    }
    std::cout << std::endl;
}
3

C std lib time()関数から秒単位で測定される直線時間の代わりに、代わりにプロセッサのカウンタを使用できますか?ほとんどのプロセッサにはフリーランニングティックカウントがあります。たとえば、x86/x64には タイムスタンプカウンター :があります。

タイムスタンプカウンタは、Pentium以降のすべてのx86プロセッサに存在する64ビットレジスタです。リセット後のティック数をカウントします。

(このページには、さまざまなプラットフォームでこのカウンターにアクセスするための多くの方法もあります-gcc/msvisual c/etc)

タイムスタンプカウンターには欠陥がないわけではなく、プロセッサー間で同期されない可能性があることに注意してください(おそらくアプリケーションを気にしないでしょう)。また、省電力機能によってプロセッサのクロックがアップまたはダウンする場合があります(ここでもおそらく気にしないでしょう)。

1
Doug T.

ただのアイデア... GUID(16バイト)を生成し、その4バイトまたは8バイトのチャンクを合計して(シードの予想される幅に応じて)、整数の折り返しを可能にします-結果をシードとして使用します。

GUIDは通常、それらを生成したコンピューターの特性(MACアドレスなど)をカプセル化します。これにより、2つの異なるマシンが同じランダムシーケンスを生成する可能性はかなり低くなります。

これは明らかに移植性がありませんが、システムに適したAPI /ライブラリを見つけるのはそれほど難しいことではありません(例:Win32のUuidCreateuuid_generateLinux上)。

1

ウィンドウズ

CryptGenRandom()およびRtlGenRandom()を提供します。それらは、シードとして使用できるランダムバイトの配列を提供します。

ドキュメントは msdnpages にあります。

Linux/Unix

OpenSSLのRand_bytes()を使用して、Linuxでランダムなバイト数を取得できます。デフォルトでは/dev/randomを使用します。

それを一緒に入れて:

#ifdef _WIN32
  #include <NTSecAPI.h>
#else
  #include <openssl/Rand.h> 
#endif

uint32_t get_seed(void)
{
  uint32_t seed = 0;

#ifdef _WIN32
  RtlGenRandom(&seed, sizeof(uint32_t) );
#else
  Rand_bytes(&seed, sizeof(uint32_t) ); 
#endif

  return seed;
}

Opensslは、暗号化的に安全なPRNGをデフォルトで提供するため、直接使用できることに注意してください。詳細 ここ

1
brice

一意性が重要な場合は、各ノードが他のノードによって要求されたIDを認識できるように調整する必要があります。 「誰かがIDxを要求しましたか?」と尋ねるプロトコルでこれを行うことができます。または、各ノードが他のノードに割り当てられていないIDを選択できるように事前に調整します。

(GUIDはマシンのMACを使用するため、「事前に手配する」カテゴリに分類されます。)

なんらかの形で合意がないと、2つのノードが同じIDをクライミングするリスクがあります。

0
billpg

適度にPOSIX風のシステムを使用していると仮定すると、clock_gettimeが必要です。これにより、現在の時刻がnanosecondsになります。つまり、すべての実用的な目的で、同じ値を2回取得することは不可能です。 (理論的には、悪い実装では解像度がはるかに低くなる可能性があります。たとえば、ミリ秒に100万を掛けるだけですが、Linuxのような半ばまともなシステムでも実際のナノ秒の結果が得られます。)

0
R..