パフォーマンスの問題は、、、、、および関数の本体carbon()
にhydrogen()
あるnitrogen()
と思われます。ランダムデータをどのように生成するかを示す必要があります。oxygen()
sulfur()
または、コード内にある可能性がありif (sum < threshold) {} else {}
ます。
結果が決定論的ではないようにシードを設定し続けたかった (真にランダムに近い)
の結果をtime(0)
シードとして使用しているため、どちらの方法でも特にランダムな結果は得られません。
を使用する代わりに、ライブラリを見て、ニーズを満たすパフォーマンス/品質特性を持つエンジンを選択する必要がありますsrand()
。実装がサポートしている場合は、 (シードを生成するため、またはエンジンとして)非決定論的なランダム データを取得することもできます。rand()
<random>
std::random_device
さらに、 の結果から必要な分布を手動で計算する平均的なプログラマーの方法よりも優れている可能性が高い、<random>
事前に作成された分布を提供します。std::uniform_real_distribution<double>
rand()
コードから内側のループをなくして大幅に高速化する方法を次に示します (Java または C++ の場合)。
あなたのコード:
double carbon() {
if (rand() % 10000 < 107)
return 13.0033548378;
else
return 12.0;
}
特定の確率で 2 つの値のいずれかを選択します。おそらく、最初の値が 10000 回中約 107 回選択されることを意図していたと思われます (ただし、with を使用%
しrand()
ても、それは実現しません)。これをループで実行し、結果を次のように合計すると:
for (int i = 0; i < composition[0]; i++) sum += carbon();
基本的にsum += X*13.0033548378 + Y*12.0;
、X は乱数がしきい値を下回った回数、Y は (trials-X) であることがわかります。たまたま、一連の試行を実行し、二項分布を使用して成功数を計算することをシミュレートでき、<random>
たまたま二項分布が提供されます。
与えられた関数sum_trials()
std::minstd_rand0 eng; // global random engine
double sum_trials(int trials, double probability, double A, double B) {
std::binomial_distribution<> dist(trials, probability);
int successes = dist(eng);
return successes*A + (trials-successes)*B;
}
carbon()
ループを置き換えることができます:
sum += sum_trials(composition[0], 107.0/10000.0, 13.003354378, 12.0); // carbon trials
使用している実際の値はありませんが、ループ全体は次のようになります。
for (int i = 0; i < 100000000; i++) {
double sum = 0;
sum += sum_trials(composition[0], 107.0/10000.0, 13.003354378, 12.0); // carbon trials
sum += sum_trials(composition[1], 107.0/10000.0, 13.003354378, 12.0); // hydrogen trials
sum += sum_trials(composition[2], 107.0/10000.0, 13.003354378, 12.0); // nitrogen trials
sum += sum_trials(composition[3], 107.0/10000.0, 13.003354378, 12.0); // oxygen trials
sum += sum_trials(composition[4], 107.0/10000.0, 13.003354378, 12.0); // sulfur trials
if (sum > threshold) {
} else {
}
}
ここで注意すべきことは、関数内で同じデータを使用して何度も分布を構築していることです。関数を関数オブジェクトに置き換えることでそれを抽出できsum_trials()
ます。関数オブジェクトは、ループの前に一度適切なデータで構築し、ファンクターを繰り返し使用します。
struct sum_trials {
std::binomial_distribution<> dist;
double A; double B; int trials;
sum_trials(int t, double p, double a, double b) : dist{t, p}, A{a}, B{b}, trials{t} {}
double operator() () {
int successes = dist(eng);
return successes * A + (trials - successes) * B;
}
};
int main() {
int threshold = 5;
int composition[5] = { 10, 10, 10, 10, 10 };
sum_trials carbon = { composition[0], 107.0/10000.0, 13.003354378, 12.0};
sum_trials hydrogen = { composition[1], 107.0/10000.0, 13.003354378, 12.0};
sum_trials nitrogen = { composition[2], 107.0/10000.0, 13.003354378, 12.0};
sum_trials oxygen = { composition[3], 107.0/10000.0, 13.003354378, 12.0};
sum_trials sulfur = { composition[4], 107.0/10000.0, 13.003354378, 12.0};
for (int i = 0; i < 100000000; i++) {
double sum = 0;
sum += carbon();
sum += hydrogen();
sum += nitrogen();
sum += oxygen();
sum += sulfur();
if (sum > threshold) {
} else {
}
}
}
コードの元のバージョンは、私のシステムで約 1 分 30 秒かかりました。ここでの最後のバージョンは 11 秒かかります。
これは、2 つの binomial_distribution を使用して酸素の合計を生成するファンクターです。たぶん、他のディストリビューションの 1 つがこれを一発で実行できるかもしれませんが、私にはわかりません。
struct sum_trials2 {
std::binomial_distribution<> d1;
std::binomial_distribution<> d2;
double A; double B; double C;
int trials;
double probabilty2;
sum_trials2(int t, double p1, double p2, double a, double b, double c)
: d1{t, p1}, A{a}, B{b}, C{c}, trials{t}, probability2{p2} {}
double operator() () {
int X = d1(eng);
d2.param(std::binomial_distribution<>{trials-X, p2}.param());
int Y = d2(eng);
return X*A + Y*B + (trials-X-Y)*C;
}
};
sum_trials2 oxygen{composition[3], 17.0/1000.0, (47.0-17.0)/(1000.0-17.0), 17.9999, 16.999, 15.999};
合計が を下回る確率を計算できれば、これをさらに高速化できますthreshold
。
int main() {
std::minstd_rand0 eng;
std::bernoulli_distribution dist(probability_sum_is_over_threshold);
for (int i=0; i< 100000000; ++i) {
if (dist(eng)) {
} else {
}
}
}
他の要素の値が負になる可能性がない限り、合計が 5 を超える確率は 100% です。その場合、ランダム データを生成する必要さえありません。コードの 'if' ブランチを 100,000,000 回実行します。
int main() {
for (int i=0; i< 100000000; ++i) {
//execute some code
}
}