120

CまたはC++の正規分布に従って乱数を簡単に生成するにはどうすればよいですか?

Boostは使いたくありません。

Knuthがこれについて詳しく話していることは知っていますが、現在彼の本は手元にありません。

4

18 に答える 18

96

通常のRNGからガウス分布の数値を生成する方法はたくさんあります。

Box-Muller変換が一般的に使用されます。正規分布の値を正しく生成します。数学は簡単です。2つの(均一な)乱数を生成し、それらに数式を適用することにより、2つの正規分布の乱数を取得します。1つを返し、もう1つを保存して、次の乱数要求に備えます。

于 2010-02-24T11:24:31.483 に答える
51

C ++ 11

C ++ 11はstd::normal_distribution、私が今日行く方法であるを提供します。

C以前のC++

複雑さの昇順でいくつかの解決策を次に示します。

  1. 0から1までの12個の均一な乱数を加算し、6を減算します。これは、正規変数の平均と標準偏差に一致します。明らかな欠点は、真の正規分布とは異なり、範囲が±6に制限されていることです。

  2. ボックスミュラー変換。これは上にリストされており、実装は比較的簡単です。ただし、非常に正確なサンプルが必要な場合は、ボックスミュラー変換をいくつかの均一なジェネレーターと組み合わせると、ニーブ効果1と呼ばれる異常が発生することに注意してください。

  3. 最高の精度を得るには、ユニフォームを描画し、逆累積正規分布を適用して正規分布の変量に到達することをお勧めします。これは、逆累積正規分布の非常に優れたアルゴリズムです。

1. HR Neave、「乗法合同疑似乱数生成器でのボックスミュラー変換の使用について」、Applied Statistics、22、92-97、1973

于 2010-02-24T12:29:46.653 に答える
30

すばやく簡単な方法は、均等に分散された乱数の数を合計し、それらの平均を取ることです。これが機能する理由の完全な説明については、中心極限定理を参照してください。

于 2010-02-24T11:20:33.047 に答える
26

正規分布の乱数生成ベンチマーク用のC++オープンソースプロジェクトを作成しました。

これは、以下を含むいくつかのアルゴリズムを比較します

  • 中心極限定理法
  • ボックスミュラー変換
  • マルサグリア極法
  • ジッグラトアルゴリズム
  • 逆変換サンプリング法。
  • cpp11randomC ++ 11std::normal_distributionを使用しますstd::minstd_rand(実際にはclangのBox-Muller変換です)。

floatiMac Corei5-3330S@2.70GHz、clang 6.1、64ビットでの単精度()バージョンの結果:

normaldistf

正確さのために、プログラムはサンプルの平均、標準偏差、歪度、尖度を検証します。4、8、または16の背番号を合計するCLT法は、他の方法のように尖度が良くないことがわかりました。

ジッグラトアルゴリズムは、他のアルゴリズムよりも優れたパフォーマンスを発揮します。ただし、テーブルルックアップとブランチが必要なため、SIMD並列処理には適していません。SSE2 / AVX命令セットを備えたボックスミュラー法は、非SIMDバージョンのジッグラトアルゴリズムよりもはるかに高速です(x1.79、x2.99)。

したがって、SIMD命令セットを使用するアーキテクチャにはBox-Mullerを使用することをお勧めします。それ以外の場合は、ジッグラトになる可能性があります。


PSベンチマークは、最も単純なLCG PRNGを使用して、均一に分散された乱数を生成します。そのため、一部のアプリケーションでは不十分な場合があります。ただし、すべての実装で同じPRNGが使用されるため、パフォーマンスの比較は公平である必要があります。したがって、ベンチマークは主に変換のパフォーマンスをテストします。

于 2015-07-10T05:21:45.670 に答える
14

これは、いくつかの参照に基づいたC++の例です。これは迅速で汚いので、Boostライブラリを再発明して使用しない方がよいでしょう。

#include "math.h" // for RAND, and rand
double sampleNormal() {
    double u = ((double) rand() / (RAND_MAX)) * 2 - 1;
    double v = ((double) rand() / (RAND_MAX)) * 2 - 1;
    double r = u * u + v * v;
    if (r == 0 || r > 1) return sampleNormal();
    double c = sqrt(-2 * log(r) / r);
    return u * c;
}

QQプロットを使用して結果を調べ、それが実際の正規分布にどれだけ近似しているかを確認できます(サンプルを1..xにランク付けし、ランクをxの総数の比率に変換します。つまり、サンプルの数、z値を取得します)上向きの直線が望ましい結果です)。

于 2012-05-18T00:04:15.510 に答える
12

を使用しstd::tr1::normal_distributionます。

std::tr1名前空間はboostの一部ではありません。これは、C ++テクニカルレポート1からのライブラリの追加を含む名前空間であり、ブーストとは関係なく、最新のMicrosoftコンパイラおよびgccで使用できます。

于 2010-02-24T11:23:09.797 に答える
12

これは、最新のC++コンパイラでサンプルを生成する方法です。

#include <random>
...
std::mt19937 generator;
double mean = 0.0;
double stddev  = 1.0;
std::normal_distribution<double> normal(mean, stddev);
cerr << "Normal: " << normal(generator) << endl;
于 2012-08-15T22:02:16.190 に答える
5

GSLを使用できます。それを使用する方法を示すために、いくつかの完全な例が示されています。

于 2011-11-05T12:57:15.153 に答える
4

http://www.cplusplus.com/reference/random/normal_distribution/をご覧ください。これは、正規分布を生成する最も簡単な方法です。

于 2012-12-16T04:28:53.010 に答える
4

C ++ 11を使用している場合は、次を使用できますstd::normal_distribution

#include <random>

std::default_random_engine generator;
std::normal_distribution<double> distribution(/*mean=*/0.0, /*stddev=*/1.0);

double randomNumber = distribution(generator);

乱数エンジンの出力を変換するために使用できる他の多くの分布があります。

于 2013-04-21T12:09:55.300 に答える
3

私はhttp://www.mathworks.com/help/stats/normal-distribution.htmlで与えられたPDFの定義に従い、これを思いついた:

const double DBL_EPS_COMP = 1 - DBL_EPSILON; // DBL_EPSILON is defined in <limits.h>.
inline double RandU() {
    return DBL_EPSILON + ((double) rand()/RAND_MAX);
}
inline double RandN2(double mu, double sigma) {
    return mu + (rand()%2 ? -1.0 : 1.0)*sigma*pow(-log(DBL_EPS_COMP*RandU()), 0.5);
}
inline double RandN() {
    return RandN2(0, 1.0);
}

最善のアプローチではないかもしれませんが、非常に簡単です。

于 2012-10-26T06:41:18.363 に答える
2

逆累積正規分布にはさまざまなアルゴリズムがあります。定量的ファイナンスで最も人気のあるものは、http://chasethedevil.github.io/post/monte-carlo-inverse-cumulative-normal-distribution/でテストされています。

私の意見では、 WichuraのアルゴリズムAS241以外のものを使用するインセンティブはあまりありません。それは、マシンの精度、信頼性、高速性です。ガウス乱数の生成でボトルネックが発生することはめったにありません。

ここでの一番の答えはBox-Müllerを支持しています。あなたはそれが既知の欠陥を持っていることに注意する必要があります。https://www.sciencedirect.com/science/article/pii/S0895717710005935を引用します:

文献では、主に2つの理由から、ボックスミュラー法がわずかに劣っていると見なされることがあります。まず、ボックスミュラー法を悪い線形合同法からの数に適用すると、変換された数は空間のカバレッジが非常に悪くなります。らせん状の尾を持つ変換された数のプロットは、多くの本、特にこの観察を最初に行ったリプリーの古典的な本で見つけることができます。」

于 2017-01-24T13:17:30.807 に答える
1

comp.lang.c FAQリストは、ガウス分布で乱数を簡単に生成する3つの異なる方法を共有しています。

あなたはそれを見るかもしれません:http://c-faq.com/lib/gaussian.html

于 2016-10-14T09:16:15.860 に答える
1

ボックスミュラー法の実装:

#include <cstdlib>
#include <cmath>
#include <ctime>
#include <iostream>
using namespace std;
 // return a uniformly distributed random number
double RandomGenerator()
{
  return ( (double)(rand()) + 1. )/( (double)(RAND_MAX) + 1. );
}
 // return a normally distributed random number
double normalRandom()
{
  double y1=RandomGenerator();
  double y2=RandomGenerator();
  return cos(2*3.14*y2)*sqrt(-2.*log(y1));
}

int main(){
double sigma = 82.;
double Mi = 40.;
  for(int i=0;i<100;i++){
double x = normalRandom()*sigma+Mi;
    cout << " x = " << x << endl;
  }
  return 0;
}
于 2017-12-03T12:55:37.860 に答える
0

1)ガウス乱数を生成するためのグラフィカルに直感的な方法は、モンテカルロ法に似た方法を使用することです。Cの疑似乱数ジェネレーターを使用して、ガウス曲線の周りのボックスにランダムな点を生成します。その点がガウス分布の内側にあるか下にあるかは、分布の方程式を使用して計算できます。その点がガウス分布の内側にある場合は、点のx値としてガウス乱数を取得しています。

技術的にはガウス曲線が無限大に向かって進み、x次元で無限大に近づくボックスを作成できなかったため、この方法は完全ではありません。しかし、グアシアン曲線はy次元で0にかなり速く近づくので、それについて心配する必要はありません。Cでの変数のサイズの制約は、精度を制限する要因になる可能性があります。

2)別の方法は、独立確率変数が追加されると、それらが正規分布を形成するという中心極限定理を使用することです。この定理を念頭に置いて、大量の独立確率変数を追加することにより、ガウス乱数を概算できます。

これらの方法は最も実用的ではありませんが、既存のライブラリを使用したくない場合に予想されます。この答えは、微積分や統計の経験がほとんどまたはまったくない人からのものであることに注意してください。

于 2018-07-18T14:53:43.887 に答える
0

モンテカルロ法 これを行う最も直感的な方法は、モンテカルロ法を使用することです。適切な範囲-X、+Xを取ります。Xの値を大きくすると、正規分布がより正確になりますが、収束に時間がかかります。a。-XからXの間の乱数zを選択します。b。N(z, mean, variance)Nがガウス分布である確率を維持します。それ以外の場合はドロップして、ステップ(a)に戻ります。

于 2020-05-08T23:10:46.827 に答える
-1

私が見つけたものを見てください。

このライブラリは、ジッグラトアルゴリズムを使用しています。

于 2013-05-19T16:52:38.670 に答える
-3

コンピューターは決定論的な装置です。計算にランダム性はありません。さらに、CPUの算術デバイスは、整数の有限集合(有限体で評価を実行)と実際の有理数の有限集合の合計を評価できます。また、ビット演算も実行しました。数学は、ポイント数が無限の[0.0、1.0]のようなより優れたセットを扱います。

コンピューターの内部でコントローラーを使ってワイヤーを聞くことはできますが、均一に分布しているでしょうか?知らない。しかし、それが信号であると仮定すると、大量の独立確率変数の累積値の結果であると仮定すると、ほぼ正規分布の確率変数を受け取ります(確率論で証明されました)

--pseudorandomgeneratorと呼ばれるアルゴリズムが存在します。私が感じたように、疑似ランダムジェネレータの目的はランダム性をエミュレートすることです。そして、goodnesの基準は次のとおりです。-経験分布は(ある意味で-点ごとに、均一で、L2)理論に収束します-ランダムジェネレーターから受け取る値は独立しているように見えます。もちろん、それは「本当の観点」からは真実ではありませんが、私たちはそれが真実であると仮定します。

人気のある方法の1つ-一様分布で12irvを合計できます...しかし、フーリエ変換、テイラー級数の助けを借りて中心極限定理を導出する際に正直に言うと、n->+infの仮定を数回持つ必要があります。したがって、たとえば理論的には、個人的には、人々が均一な分布で12irvの合計を実行する方法を理解していません。

私は大学で能力理論を持っていました。そして特に私にとって、それは単なる数学の質問です。大学で私は次のモデルを見ました:


double generateUniform(double a, double b)
{
  return uniformGen.generateReal(a, b);
}

double generateRelei(double sigma)
{
  return sigma * sqrt(-2 * log(1.0 - uniformGen.generateReal(0.0, 1.0 -kEps)));
}
double generateNorm(double m, double sigma)
{
  double y2 = generateUniform(0.0, 2 * kPi);
  double y1 = generateRelei(1.0);
  double x1 = y1 * cos(y2);
  return sigma*x1 + m;
}

そのようにそれを行う方法は単なる例でした、私はそれを実装する別の方法が存在すると思います。

それが正しいことの証明は、クリシュチェンコアレクサンドルペトロヴィッチISBN 5-7038-2485-0のこの本「モスクワ、BMSTU、2004年:XVI確率論、例6.12、p.246-247」にあります。

残念ながら、この本の英語への翻訳の存在については知りません。

于 2016-01-10T21:57:10.927 に答える