C++ TR1 を使用して C++ で真の乱数を生成しようとしています。ただし、プログラムを再度実行すると、同じ乱数が生成されます。コードは以下のとおりです。
可能な限りランダムに実行するたびに、真の乱数が必要です。
std::tr1::mt19937 eng;
std::tr1::uniform_real<double> unif(0, 1);
unif(eng);
シードを使用してエンジンを初期化する必要があります。そうしないと、デフォルトのシードが使用されます。
eng.seed(static_cast<unsigned int >(time(NULL)));
ただし、真のランダム性は、追加の入力なしでは決定論的マシンでは達成できないものです。すべての疑似乱数ジェネレーターは、何らかの方法で周期的です。これは、非決定論的な数値からは期待できないことです。たとえば、 219937-1回の反復std::mt19937
の期間があります。決定論的ではないと思われるもの(ユーザー入力、大気ノイズ)を監視する必要があるため、真のランダム性を実現するのは困難です。JerryとHandprintの回答を参照してください。
時間ベースのシードが必要ない場合は、 emsrの回答std::random_device
に示されているように使用できます。ジェネレーターとして使用することもできます。これは、標準ライブラリメソッドのみで真のランダム性に最も近いものです。std::random_device
これらは疑似乱数ジェネレータです。真に乱数を生成することはできません。そのためには、通常、特別なハードウェアが必要です(たとえば、通常、サーマルダイオードのノイズの測定や放射線源からの放射など)。
異なる実行で疑似ランダムジェネレーターから差分シーケンスを取得するには、通常、現在の時刻に基づいてジェネレーターをシードします。
ただし、これはかなり予測可能な結果を生成します(つまり、他の誰かがあなたが使用したシードをかなり簡単に理解できます。それを防ぐ必要がある場合、ほとんどのシステムは少なくともかなり乱数のソースを提供します。Linuxでは、/ dev / random、およびWindowsでは、CryptGenRandom
。
ただし、後者はかなり遅い傾向があるため、通常は、すべての乱数を取得するだけでなく、シードとして使用する必要があります。
真のハードウェア乱数が必要な場合は、標準ライブラリがrandom_deviceクラスを介してこれにアクセスできます。
別のジェネレーターをシードするために使用します。
#include <random>
...
std::mt19937_64 re;
std::random_device rd;
re.seed(rd());
...
std::cout << re();
ハードウェアに/dev/urandomまたは/dev/ randomがある場合は、これが使用されます。それ以外の場合、実装はその疑似ランダムジェネレーターの1つを自由に使用できます。G ++では、mt19937がフォールバックとして使用されます。
他の人が指摘したように、tr1にもこれがあると確信しています。この時点でstd C++11ユーティリティを使用するのが最善だと思います。
エド
この回答はウィキです。私は.NETのライブラリと例に取り組んでいます.任意の言語で自由に独自のものを追加してください...
外部の「乱数」入力 (通りの騒音の監視など) がなければ、コンピューターは決定論的マシンとして真の乱数を生成できません:乱数生成.
私たちのほとんどは、特別な機器を利用して混沌とした入力を提供するためのお金と専門知識を持っていないため、OS、タスクスケジューラ、プロセスマネージャー、およびユーザー入力 (マウスの動きなど) のやや予測不可能な性質を利用する方法があります。改善された疑似ランダム性を生成します。
残念ながら、C++ TR1 がこれを実行できるかどうかを知るには、C++ TR1 について十分な知識がありません。
編集
他の人が指摘しているように、RNG にさまざまな入力をシードすることで、さまざまな数列 (最終的には繰り返されるため、真にランダムではない) が得られます。したがって、世代を改善するには2つのオプションがあります。
ある種の無秩序な入力で RNG を定期的に再シードするか、システムの動作方法に基づいて RNG の出力を信頼できないものにします。
前者は、システム環境を調べてシードを明示的に生成するアルゴリズムを作成することで実現できます。これには、いくつかのイベント ハンドラー、デリゲート関数などの設定が必要になる場合があります。
後者は、貧弱な並列コンピューティングの実践によって達成できます。つまり、多くの RNG スレッド/プロセスが「安全でない方法」で競合するように設定して、後続の各乱数 (または数列) を作成します。「GetNext()」タイプのメソッドが呼び出されたときに、毎分のイベントがどのスレッドの出力が最終的に書き込まれ、最終的に読み取られるかに影響を与えるため、これはシステム上のアクティビティの合計から混乱を暗黙のうちに追加します。以下は、.NET 3.5 での大まかな概念実証です。2 つの点に注意してください。1) RNG は毎回同じ数でシードされますが、24 の同一の行は作成されません。2) 乱数生成を改善すると、パフォーマンスが著しく低下し、リソース消費が明らかに増加します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace RandomParallel
{
class RandomParallel
{
static int[] _randomRepository;
static Queue<int> _randomSource = new Queue<int>();
static void Main(string[] args)
{
InitializeRepository(0, 1, 40);
FillSource();
for (int i = 0; i < 24; i++)
{
for (int j = 0; j < 40; j++)
Console.Write(GetNext() + " ");
Console.WriteLine();
}
Console.ReadLine();
}
static void InitializeRepository(int min, int max, int size)
{
_randomRepository = new int[size];
var rand = new Random(1024);
for (int i = 0; i < size; i++)
_randomRepository[i] = rand.Next(min, max + 1);
}
static void FillSource()
{
Thread[] threads = new Thread[Environment.ProcessorCount * 8];
for (int j = 0; j < threads.Length; j++)
{
threads[j] = new Thread((myNum) =>
{
int i = (int)myNum * _randomRepository.Length / threads.Length;
int max = (((int)myNum + 1) * _randomRepository.Length / threads.Length) - 1;
for (int k = i; k <= max; k++)
{
_randomSource.Enqueue(_randomRepository[k]);
}
});
threads[j].Priority = ThreadPriority.Highest;
}
for (int k = 0; k < threads.Length; k++)
threads[k].Start(k);
}
static int GetNext()
{
if (_randomSource.Count > 0)
return _randomSource.Dequeue();
else
{
FillSource();
return _randomSource.Dequeue();
}
}
}
}
生成中にユーザーの入力/相互作用がある限り、この手法は、「乱数」の解読不能で繰り返しのないシーケンスを生成します。このようなシナリオでは、マシンの初期状態を知るだけでは、結果を予測するには不十分です。
エンジンのシードの例を次に示します (TR1 の代わりに C++11 を使用)。
#include <chrono>
#include <random>
#include <iostream>
int main() {
std::mt19937 eng(std::chrono::high_resolution_clock::now()
.time_since_epoch().count());
std::uniform_real_distribution<> unif;
std::cout << unif(eng) << '\n';
}
現在の時刻をシードすることは比較的予測可能であり、おそらく行うべきことではありません。上記は、少なくとも 1 秒あたり 1 つの可能なシードに制限するものではありません。これは非常に予測可能です。
現在の時刻の代わりに /dev/random などからシードしたい場合は、次のようにします。
std::random_device r;
std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()};
std::mt19937 eng(seed);
(これは、標準ライブラリの実装に依存します。たとえば、libc++ はデフォルトで /dev/urandom を使用しますが、VS11 では random_device は決定論的です)
もちろん、mt19937 から得られるものは「真の乱数」の要件を満たすものではなく、真の乱数は本当に必要ないと思います。