11

C++ 11 標準ライブラリの乱数分布を、分布のパラメーターとジェネレーター インスタンスを引数として取る単純な関数でラップしたいと考えています。例えば:

double normal(double mean, double sd, std::mt19937_64& generator)
{
    static std::normal_distribution<double> dist;
    return dist(generator, std::normal_distribution<double>::param_type(mean, sd));
}

このラッパー関数への各呼び出しが指定された引数のみに依存するように、配布オブジェクト内の非表示の状態を回避したいと考えています。(潜在的に、この関数への各呼び出しは異なるジェネレーター インスタンスを取ることができます。) 理想的には、static constこれを確実にするためにディストリビューション インスタンスを作成します。ただし、分布operator()は定数関数ではないため、これは不可能です。

私の質問は次のとおりです。ディストリビューション内に隠された状態がないことを確認するには、1)reset()ディストリビューションを毎回呼び出す必要があり、2) 十分ですか? 例えば:

double normal(double mean, double sd, std::mt19937_64& generator)
{
    static std::normal_distribution<double> dist;
    dist.reset();
    return dist(generator, std::normal_distribution<double>::param_type(mean, sd));
}

(全体として、ランダム分布の関数の目的について混乱していreset()ます...ジェネレーターを時々リセット/再シードする必要がある理由を理解していますが、なぜ分布オブジェクトをリセットする必要があるのでしょうか?)

4

3 に答える 3

8

ディストリビューション内に隠れた状態がないことを確認するには、1) 必要ですか?

はい。

2) 毎回ディストリビューションで reset() を呼び出すのに十分ですか?

はい。

あなたはおそらくこれをしたくないでしょう。少なくともすべての通話ではありません。はstd::normal_distribution、ディストリビューションが状態を維持できるようにするためのポスター チャイルドです。たとえば、一般的な実装では、Box-Muller 変換を使用して一度に 2 つの乱数を計算しますが、そのうちの 1 つだけを返し、もう 1 つは次に呼び出すときのために取っておきます。次の呼び出しreset()の前に呼び出しを行うと、ディストリビューションがこの既に有効な結果を破棄し、アルゴリズムの効率が半分になります。

于 2013-02-13T16:20:17.567 に答える
2

一部のディストリビューションには内部状態があります。常にリセットして配布の動作を妨げると、適切に配布された結果が得られません。srand()これは、へのすべての呼び出しの前に呼び出すのと同じrand()です。

于 2013-02-13T16:21:05.110 に答える
1

reset()配布オブジェクトを呼び出すとd、次の効果があります。

その後の d の使用は、リセットを呼び出す前にエンジンによって生成された値に依存しません。

(エンジンは、要するに、シード可能なジェネレーターです)。

つまり、ディストリビューション オブジェクトが保存し、以前にエンジンから取得した出力に依存する「キャッシュされた」ランダム データをクリアします。

したがって、それを行いたい場合は、 を呼び出す必要がありますreset()。私が考えることができる主な理由は、反復可能な疑似ランダム結果を生成する目的で、エンジンに既知の値をシードしている場合です。配布オブジェクトからの結果そのシードに基づいて再現可能にしたい場合は、配布オブジェクトをリセットする (または新しいオブジェクトを作成する) 必要があります。

私が考えることができるもう1つの理由は、攻撃者が内部状態の部分的な知識を得る可能性があることを恐れているため、ジェネレーターオブジェクトを防御的に再シードしていることです (たとえば、Fortunaが行うように)。単純化しすぎると、ジェネレーターのデータの品質/セキュリティは時間の経過とともに低下し、再シードによって復元されると想像できます。配布オブジェクトはジェネレーターから任意の量のデータをキャッシュできるため、ジェネレーターの出力の品質/セキュリティを向上させてから、配布オブジェクトの出力の品質/セキュリティを向上させるまでの間に任意の遅延が発生します。通話中reset配布オブジェクトでこの遅延を回避します。しかし、この後者の使用が正しいとは断言しません。なぜなら、専門家による査読済みの作業に頼ることができる場合、何が安全かについて自分で判断したくない領域に入るためです:-)

特にコードに関しては、同じオブジェクトを別のジェネレーター オブジェクトで以前に使用したことに出力を依存させたくない場合はdist、呼び出しreset()がその方法です。resetしかし、ディストリビューション オブジェクトを呼び出し、それを新しいパラメーターで使用することは、それらのパラメーターを使用して新しいディストリビューション オブジェクトを構築するよりも安価になる可能性は低いと思います。したがって、staticローカルオブジェクトは、関数を非スレッドセーフにするメリットがないように思えます。毎回新しいディストリビューションオブジェクトを作成でき、コードはおそらく悪くないでしょう。標準の設計には理由があり、同じジェネレーターで配布オブジェクトを繰り返し使用することが期待されています。あなたが書いた関数は、配布オブジェクトをインターフェイスから切り離して、標準の設計のその部分の利点を破棄します。

于 2013-02-14T09:41:08.270 に答える