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

C++ 如何最有效地防止正态分布随机变量为零?,c++,random,tr1,montecarlo,normal-distribution,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::t

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

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;
但是我不认为这特别优雅。还有更好的办法吗?

我要计算的函数实际上不仅仅是一个简单的函数ℝ->ℝ 像
f
是,但是ℝᵐxℝⁿ -> ℝ 其中我计算了ℝᵐ 数值积分过程中的变量ℝⁿ 变量。整个功能与不可预测(但“相干”)噪声叠加,有时具有特定(但未知)的突出频率,这就是当我尝试使用
h

的固定值时遇到的问题。如果要保持正态分布,必须排除0或将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异常生成更保守的代码,可能会对性能造成小的影响。如果在<代码> -fNONE调用异常< /COD>编译的代码不分配任何自动(堆栈)C++对象,那么这就不太可能成为问题。还值得注意的是,这使得用零除的情况非常昂贵。

您的方式似乎足够优雅,可能有点不同:

do {
    h = r();
} while (h == 0.0);
函数(f(x+h)-f(x))/h的极限为h->0,因此如果遇到h==0,则应使用该极限。极限是f’(x),所以如果你知道导数,你可以使用它


如果你实际做的是创建离散点的数量,虽然这近似于正态分布,这对于你的分布来说已经足够好了,那么创建的方式应该是它们中没有一个实际值为0。

两个正态分布随机变量的比率是柯西分布。柯西分布是一种具有无穷方差的恶劣分布。真的很讨厌。柯西分布会把你的蒙特卡罗实验搞得一团糟

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

  • 正态分布通常很容易处理
  • 通常都有很好的数学性质
  • 正常假设似乎或多或少是正确的,并且
  • 真正的分布是一只熊

假设你正在除以距离。根据定义,距离是半正定的,通常作为随机变量是正定的。因此,击球距离永远不可能是正态分布的。尽管如此,在平均值远大于标准差的情况下,人们通常假定距离为正态分布。当做出这种正常假设时,您需要针对这些非真实值进行保护。一个简单的解决方案是截断法线。

(f(x+h)-f(x))/h
看起来很像一个派生。0的派生具有许多属性,其中一些属性在这样的计算中可能是不需要的;我想数学上的问题是,当h=0时,结果应该是什么?当h为0时,或者更确切地说,当你接近极限时,你应该得到导数f'(x)。您上面的代码示例实际上在数学上是不正确的。虽然
a=0
h==0
的情况下是错误的,但我仍然会记住这个“解决方案”:一个不正确的0(与我未经检查得到的不正确的
NAN
不同!)根本没有坏处,在我看来,这段代码可能比我的代码快一点,因为它避免了分支预测失误。求出解析导数,用它来计算非常小的h?如果h非常小但不是零呢?你的计算结果合理吗?@E先生,我希望我能这样做,但是
f
太复杂了。@Alan对于非常小的
h
它会给我越来越大的计算错误,但由于这些数字很少出现,这不是问题。使用对数步长如何?或者Runge-kutta方法?“如果你想保持正态分布,你必须排除0或者将0分配给一个新的以前不存在的值”,这是我更愿意做的。真的不可能吗?因为即使我们有有限的范围,仍然不是所有的值都会出现,如果只是因为32位整数少于-1和1之间的
double
数。我不是在计算两个正态分布变量的比率。这是一个随机变量和这个变量的某个确定性函数的比值。为什么要用下一票?请留下评论,以便改进答案。我留下了评论,但没有得到回复也许这不是决定何时否决的正确方式:这是actu
do {
    h = r();
} while (h == 0.0);