如何最有效地防止我的正态分布随机变量为零?

时间:2011-06-18 22:08:10

标签: c++ random tr1 montecarlo normal-distribution

我正在编写一个蒙特卡罗算法,其中我需要将一个随机变量除以一个点。更确切地说:随机变量用作差商的步长,所以我实际上先将某个乘以变量,然后再将其除以该表达式的某个局部线性函数。像

double f(double);

std::tr1::variate_generator<std::tr1::mt19937, std::tr1::normal_distribution<> >
  r( std::tr1::mt19937(time(NULL)),
     std::tr1::normal_distribution<>(0) );

double h = r();
double a = ( f(x+h) - f(x) ) / h;

这在大多数情况下都可以正常工作,但在h=0时失败。在数学上,这不是一个问题,因为在任何有限(或实际上,可数)的正态分布随机变量的选择中,它们都是非零的概率1.但在数字实现中我会遇到h==0每个≈2³²函数调用(无论mersenne twister的周期比宇宙长,它仍然会输出普通的long!)。

在我正在做的那一刻

时,避免这种麻烦非常简单
double h = r();
while (h==0) h=r();

但我不认为这特别优雅。有没有更好的方法?

<小时/> 我正在评估的功能实际上不仅仅是一个简单的ℝ-&gt;ℝ像f那样,而是一个ℝᵐxℝⁿ - &gt; ℝ我在ℝᵐ变量中计算梯度,同时在ℝⁿ变量上进行数值积分。整个功能叠加了不可预测的(但“连贯的”)噪声,有时具有特定(但未知)的突出频率,当我尝试使用h的固定值时,这会让我陷入麻烦。

5 个答案:

答案 0 :(得分:3)

你的方式看起来很优雅,也许有点不同:

do {
    h = r();
} while (h == 0.0);

答案 1 :(得分:3)

两个正态分布随机变量的比率是Cauchy分布。 Cauchy分布是具有无穷方差的那些讨厌分布之一。确实很讨厌。 Cauchy分布会使你的蒙特卡罗实验变得混乱。

在计算两个随机变量的比率的许多情况下,分母不正常。人们经常使用正态分布来近似这个非正态分布的随机变量,因为

  • 正常分布通常很容易使用,
  • 通常有这么好的数学属性,
  • 正常假设似乎或多或少是正确的,
  • 真正的分布是熊。

假设你除以距离。根据定义,距离是半正定的,并且通常作为随机变量是正定的。因此,直接击球距离永远不会正常分布。尽管如此,在平均值远大于标准偏差的情况下,人们通常会假设距离的正态分布。当做出这个正常假设时,您需要防止那些非实际值。一个简单的解决方案是截断法线。

答案 2 :(得分:0)

如果要保留正态分布,则必须排除0或将0分配给新的先前不存在的值。由于第二种很可能在计算机科学的有限范围内是不可能的,所以第一种是我们唯一的选择。

答案 3 :(得分:0)

根据您尝试计算的内容,也许这样的事情会起作用:

double h = r();
double a;
if (h != 0)
    a = ( f(x+h) - f(x) ) / h;
else
    a = 0;

如果f是线性函数,那么(我认为?)在h = 0时保持连续。

您可能还想考虑捕获除零异常以避免分支的成本。 请注意,这可能会或可能不会对性能产生不利影响 - 两种方式的基准!

在Linux上,您需要使用-fnon-call-exceptions构建包含潜在除法的文件,并安装SIGFPE处理程序:

struct fp_exception { };

void sigfpe(int) {
  signal(SIGFPE, sigfpe);
  throw fp_exception();
}

void setup() {
  signal(SIGFPE, sigfpe);
}

// Later...
    try {
        run_one_monte_carlo_trial();
    } catch (fp_exception &) {
        // skip this trial
    }

在Windows上,使用SEH:

__try 
{ 
    run_one_monte_carlo_trial();
} 
__except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ? 
         EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{ 
    // skip this trial
}

这具有可能对快速路径影响较小的优点。虽然可能会对异常处理程序记录进行一些调整,但没有分支。在Linux上,由于编译器为-fnon-call-exceptions生成更保守的代码,因此性能可能会受到很小影响。如果在-fnon-call-exceptions下编译的代码不分配任何自动(堆栈)C ++对象,则这不太可能成为问题。同样值得注意的是,这使得除以的除法发生的情况非常昂贵。

答案 4 :(得分:0)

函数(f(x + h)-f(x))/ h的限制为h-> 0,因此如果遇到h == 0,则应使用该限制。限制将是f'(x),所以如果您知道衍生物,您可以使用它。

如果您实际正在做的是创建离散点的数量,尽管它接近正态分布,并且这对于您的分布来说已经足够好了,那么创建它的方式是它们中没有一个实际上具有值0。