6

私は最近、完全に理解できない C++ のバグ/機能に出くわしました。ここで C++ の知識が豊富な人が私を正しい方向に向けてくれることを望んでいました。

以下に、モンテカルロ積分を使用してガウス曲線の下の領域を見つけようとする私の試みを示します。レシピは次のとおりです。

  1. 多数の正規分布確率変数 (平均が 0、標準偏差が 1) を生成します。
  2. これらの数を二乗します。
  3. すべての正方形の平均を取ります。平均は、曲線の下の領域の非常に近い推定値になります (ガウスの場合は 1.0)。

以下のコードは、2 つの単純な関数で構成されています。rand_uniは、0 と 1 の間で一様に分布する確率変数を返しますrand_norm。 は、正規分布する確率変数の近似 (やや貧弱ですが、「政府の仕事には十分」) です。

mainループを 10 億回実行し、そのたびに呼び出しrand_norm、それを 2 乗powし、累積変数に追加します。このループの後、累積された結果が実行回数で除算され、ターミナルに として出力されResult=<SOME NUMBER>ます。

問題は、以下のコードの非常に風変わりな動作にあります: 生成された各確率変数が出力されるとcout(そうです、10 億回)、最終結果は、使用するコンパイラに関係なく正しいものになります (1.0015、これは私が欲しいです)。ループの反復ごとに確率変数を出力しないと、と 448314 を下回ってしまいinfます。gccclang

率直に言って、これは頭がおかしくなり、C++ との最初の (-ish) 遭遇であるため、何が問題なのかよくわかりませんpowcout挙動がおかしくなる?

どんなヒントでも大歓迎です!

起源

// Monte Carlo integration of the Gaussian curve

#include <iostream>
#include <cstdlib>
#include <cmath>


using namespace std;


enum {
  no_of_runs = 1000000
};


// uniform random variable
double rand_uni() {
  return ((double) rand() / (RAND_MAX));
};


// approximation of a normaly distributed random variable
double rand_norm() {
  double result;
  for(int i=12; i > 0; --i) {
    result += rand_uni();
  }
  return result - 6;
};


int main(const int argc,
         const char** argv) {

  double result = 0;
  double x;

  for (long i=no_of_runs; i > 0; --i) {
    x = pow(rand_norm(), 2);
    #ifdef DO_THE_WEIRD_THING
    cout << x << endl;      // MAGIC?!
    #endif
    result += x;
  }

  // Prints the end result
  cout << "Result=" 
       << result / no_of_runs
       << endl << endl;

}

メイクファイル

CLANG=clang++
GCC=g++
OUT=normal_mc

default: *.cpp
    $(CLANG) -o $(OUT).clang.a *.cpp
    $(CLANG) -o $(OUT).clang.b -DDO_THE_WEIRD_THING *.cpp
    $(GCC) -o $(OUT).gcc.a *.cpp
    $(GCC) -o $(OUT).gcc.b -DDO_THE_WEIRD_THING *.cpp
4

2 に答える 2

3

rand_norm() 関数の結果は初期化されていません。これがバグです。値を出力したときになぜそれが機能したのか疑問に思っている場合に備えて、それは、初期化された変数がそれが持っているメモリ領域の値を持つためです。コードではそれはスタックであるため、 cout<< を呼び出すとスタック値が変更されました。関数の修正版は次のとおりです。

double rand_norm() {
  double result = 0.0;
  for(int i=12; i > 0; --i) {
    result += rand_uni();
  }
  return result - 6;
};
于 2013-03-31T09:14:17.883 に答える
3

+= ランダムな値を開始する前に、 in は 0 に初期化されませんresultdouble rand_norm()

すべての cout が使用し、後で rand_norm の呼び出しによって使用されるスタック メモリの一部をリセットして、cout を実行するときに問題を「修正」します。

最初の行を に変更しdouble result = 0.0;て修正します。

double rand_norm() {
  double result = 0.0;
  for(int i=12; i > 0; --i) {
    result += rand_uni();
  }
  return result - 6;
};

また、乱数ジェネレーターをシードする必要があります。それがなくても、常にまったく同じ疑似乱数のシーケンスが得られます。

srand(time(NULL));

rand() を最初に呼び出す前に、またはBoost.Randomなどのより優れた乱数ジェネレーターを調べてください。

于 2013-03-31T09:12:58.270 に答える