C++ 11は、乱数エンジンとランダム分布の宣言を含むヘッダー_<random>
_を導入しました。それは素晴らしいことです-Rand()
の使用を置き換える時期です。しかし、それを置き換える方法は明らかではありません
_srand(n);
// ...
int r = Rand();
_
宣言に基づいて、均一な分布は次のように構築できるようです:
_std::default_random_engine engine;
engine.seed(n);
std::uniform_int_distribution<> distribution;
auto Rand = [&](){ return distribution(engine); }
_
このアプローチはどちらかというと複雑で、srand()
やRand()
の使用とは異なり、確かに覚えていません。私は N4531 を知っていますが、それでもまだかなり関与しているようです。
srand()
とRand()
を置き換えるのに適度に簡単な方法はありますか?
Srand()とRand()を置き換える簡単な方法はありますか?
完全な開示:Rand()
は好きではありません。それは悪いことであり、非常に簡単に乱用されます。
C++ 11ランダムライブラリは、長い間欠けていたボイドを埋めます。高品質のランダムライブラリの問題は、使用が難しいことが多いことです。 C++ 11 <random>
ライブラリは、この点で大きな前進を表しています。数行のコードと私は、非常にうまく動作し、多くの異なる分布からランダムな変量を簡単に生成する非常に良いジェネレーターを持っています。
上記を考えると、あなたへの私の答えは少し異端です。 Rand()
で十分な場合は、それを使用してください。 Rand()
と同じくらい悪い(そして悪い)ので、これを削除すると、C言語で大きな問題が発生します。 Rand()
の悪さが本当にあなたのニーズに十分満足できるものであることを確認してください。
C++ 14はRand()
を廃止しませんでした。 Rand()
を使用するC++ライブラリの非推奨の関数のみです。 C++ 17はRand()
を廃止する可能性がありますが、削除されません。つまり、Rand()
が消えるまでにはさらに数年かかります。 C++委員会が最終的にC++標準ライブラリからRand()
を削除するまでに、退職するか別の言語に切り替える可能性は高いです。
std::vector<int> v(size); std::generate(v.begin(), v.end(), std::Rand);
の行に沿って何かを使用して、std :: sort()のさまざまな実装をベンチマークするためにランダム入力を作成しています
暗号的に安全なPRNGは必要ありません。MersenneTwisterも必要ありません。この特定のケースでは、おそらくRand()
で十分です。
更新
C++ 11ランダムライブラリstd::minstd_Rand
には、Rand()
とsrand()
に代わる素敵な単純な置換があります。
#include <random>
#include <iostream>
int main ()
{
std:: minstd_Rand simple_Rand;
// Use simple_Rand.seed() instead of srand():
simple_Rand.seed(42);
// Use simple_Rand() instead of Rand():
for (int ii = 0; ii < 10; ++ii)
{
std::cout << simple_Rand() << '\n';
}
}
関数std::minstd_Rand::operator()()
はstd::uint_fast32_t
を返します。ただし、アルゴリズムは結果を1〜2に制限します31-2、包括的。つまり、結果は常にstd::int_fast32_t
(またはint
が32ビット以上の場合はint
に)に安全に変換されます。
randutils
pcg-random.org のMelissa O'Neillはどうですか?
導入ブログの投稿 から:
randutils::mt19937_rng rng;
std::cout << "Greetings from Office #" << rng.uniform(1,17)
<< " (where we think PI = " << rng.uniform(3.1,3.2) << ")\n\n"
<< "Our office morale is " << rng.uniform('A','D') << " grade\n";
CスタイルのRand
関数とsrand
関数の振る舞い(それらの奇妙さを含む)の動作が必要だと仮定しますが、ランダム性が良いので、これは私が得ることができる最も近いものです。
#include <random>
#include <cstdlib> // Rand_MAX (might be removed soon?)
#include <climits> // INT_MAX (use as replacement?)
namespace replacement
{
constexpr int Rand_max {
#ifdef Rand_MAX
Rand_MAX
#else
INT_MAX
#endif
};
namespace detail
{
inline std::default_random_engine&
get_engine() noexcept
{
// Seeding with 1 is silly, but required behavior
static thread_local auto rndeng = std::default_random_engine(1);
return rndeng;
}
inline std::uniform_int_distribution<int>&
get_distribution() noexcept
{
static thread_local auto rnddst = std::uniform_int_distribution<int> {0, Rand_max};
return rnddst;
}
} // namespace detail
inline int
Rand() noexcept
{
return detail::get_distribution()(detail::get_engine());
}
inline void
srand(const unsigned seed) noexcept
{
detail::get_engine().seed(seed);
detail::get_distribution().reset();
}
inline void
srand()
{
std::random_device rnddev {};
srand(rnddev());
}
} // namespace replacement
replacement::*
関数は、std::*
の<cstdlib>
関数とまったく同じように使用できます。引数をとらず、std::random_device
から取得した「実際の」乱数をエンジンにシードするsrand
オーバーロードを追加しました。もちろん、そのランダム性がどの程度「本物」であるかは、実装によって定義されます。
エンジンとディストリビューションはthread_local
static
インスタンスとして保持されるため、複数の呼び出しにわたって状態を伝達しますが、異なるスレッドが予測可能なシーケンスを監視できるようにします。 (エンジンを再構築したり、ロックを使用したり、他の人の現金を無駄にする可能性がないため、パフォーマンスも向上します。)
std::default_random_engine
を使ったのはあなたが使ったからですが、あまり好きではありません。メルセンヌツイスターエンジン(std::mt19937
およびstd::mt19937_64
)は、はるかに優れた「ランダムさ」を生み出し、驚くべきことに より高速であることが確認されています 。準拠しているプログラムは、特定の種類の疑似ランダムエンジンを使用して実装されているstd::Rand
に依存する必要があるとは思いません。 (そうであったとしても、実装はstd::default_random_engine
を自由に定義できるため、確実にstd::minstd_Rand
のようなものを使用する必要があります。)
_<random>
_で定義されているすべてのエンジンにはoperator()()
があり、これを使用して、次の生成された値を取得したり、エンジンの内部状態を進めることができます。
_std::mt19937 Rand (seed); // or an engine of your choosing
_
_for (int i = 0; i < 10; ++i) {
unsigned int x = Rand ();
std::cout << x << std::endl;
}
_
ただし、すべてのエンジンはunsigned整数型の値を返すことに注意してください。つまり、signed積分がオーバーフローする可能性があります(これはundefined-behaviorにつながります)。
新しい値を取得するすべての場所でunsigned値を使用することに問題がない場合、上記は_std::srand
_ + _std::Rand
_の使用を置き換える簡単な方法です。
注:上記の説明を使用すると、の_
result_type
_が原因で、一部の値が他の値よりも返される可能性が高くなる可能性がありますengine
宛先タイプに格納できる最大値の偶数倍の最大値がない。
これまでにこれまで心配していなかった場合-Rand()%low+high
のようなものを使用する場合-今は心配する必要はありません。
注:_
std::engine-type::result_type
_が少なくとも希望する値の範囲と同じであることを確認する必要があります(_std::mt19937::result_type
_は_uint_fast32_t
_)。
最初にdefault-construct_std::default_random_engine
_(これは、によって選択されたいくつかのエンジンのtypedefである必要はありません) implementation)、そして後でそれにシードを割り当てる;これは、random-engineの適切なコンストラクタを使用して、一度に行うことができます。
_std::random-engine-type engine (seed);
_
ただし、エンジンを再シードする必要がある場合は、_std::random-engine::seed
_を使用する方法です。
あなたが投稿したコードが少し複雑に見えても、それを書くのは一度だけです。
copy + pasteコード内のいくつかの場所に書き込んだものだけに誘惑される状況に陥った場合は、いつもcopy + pasting;ヘルパー関数を導入します。
_Intentionally left blank, see other posts for example implementations.
_
次のような単純な関数を作成できます。
#include <random>
#include <iostream>
int modernRand(int n) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, n);
return dis(gen);
}
後で次のように使用します。
int myRandValue = modernRand(n);
前述のとおり ここ