C++ 这将累积多少浮点错误?

C++ 这将累积多少浮点错误?,c++,optimization,probability,floating-accuracy,C++,Optimization,Probability,Floating Accuracy,我有一个随机进程,当调用它时,它返回一个介于0和K-1之间的随机数,其中K可能相当高。我希望跟踪任何结果发生的次数,并将所有计数标准化为概率分布。每次调用随机过程时,我都要这样做,以便我对随机过程的分布估计尽可能是最新的 一种简单的方法可以是: while ( true ) { int n = randomProcess(); ++totalCount; ++count[n]; update(); do_work_with_updated_prob_v

我有一个随机进程,当调用它时,它返回一个介于0和K-1之间的随机数,其中K可能相当高。我希望跟踪任何结果发生的次数,并将所有计数标准化为概率分布。每次调用随机过程时,我都要这样做,以便我对随机过程的分布估计尽可能是最新的

一种简单的方法可以是:

while ( true ) {
    int n = randomProcess();

    ++totalCount;
    ++count[n];
    update();

    do_work_with_updated_prob_vector();
}

void update() {
    for ( int i = 0; i < K; ++i )
        prob[i] = count[i] / static_cast<double>(totalCount);
}
while(true){
int n=随机过程();
++总数;
++计数[n];
更新();
使用更新的问题向量()工作吗;
}
无效更新(){
对于(int i=0;i
然而,当K开始变大时,这种方法需要在每次概率更新时读取整个计数向量,这是不可取的,因为缓存未命中和内存访问成本。我已经设计了另一个解决方案,在我有限的测试中,使用K~1000大约可以加快30%。新的更新函数需要知道上次更新元素的索引:

void fastUpdate(int id) {
    if ( totalCount == 1 ) {
        prob[id] = 1.0;
        return;
    }
    double newProb = count[id] / static_cast<double>(totalCount - 1);
    double newProbSum = 1.0 + ( newProb - prob[id] );

    prob[id] = newProb;
    for ( int i = 0; i < K; ++i )
        prob[i] /= newProbSum
}
void快速更新(int-id){
如果(totalCount==1){
prob[id]=1.0;
返回;
}
double newProb=count[id]/static\u cast(totalCount-1);
双newProbSum=1.0+(newProb-prob[id]);
prob[id]=newProb;
对于(int i=0;i
这种方法在理论上是可行的,但我担心由于执行的规范化不完善,浮点精度误差会不断累积。我还是应该偶尔调用一次基本的
update
函数来摆脱它们吗?如果是,多久一次?这个错误会有多大?我对这类问题几乎没有经验,我知道我不需要低估它们

编辑:因为这似乎是一个大问题,我将更好地解释我在这里做的事情,以便我们可以更多地关注我所说的问题。我还更新了顶部的第一个算法,以显示我做得更好的地方

我正在写一系列人工智能算法,需要学习一个最初未知的环境。在这种情况下,通过近似分布来了解环境。在每次迭代中,算法将根据新数据(不仅包括更新的
prob
向量,还包括其他内容)修改其决策。由于这些值不仅被使用,而且可能在一次迭代中被多次使用,因此我想最好先计算一次结果,然后再使用它,这就是我使用update函数所做的


此外,我想补充一点,我是否需要在每次迭代时更新
prob
向量,在这里确实不是问题。函数
fastUpdate
的契约是它将进行快速更新,这就是我的问题的根源。如果我不需要经常更新,我会在每次迭代时不调用该函数。因为现在我确实需要打电话给它,我正在做。我希望这能澄清问题。

举个例子,以python为例:

for i in range(1000000):
    x = rnd.randrange(0,10)
    intar.append(x)
    dblar.append(x/100.0)
intsum = 0
for i in intar:
    intsum += i
dblsum = 0.0
for d in dblar:
    dblsum += d
print("int: %f, dbl: %f, diff: %f" % ((intsum/100.0), dblsum, ((intsum/100.0)-dblsum)))
收益率:

int: 45012.230000, dbl: 45012.200000, diff: 0.030000

现在,我强制使用一个除数来确保有一致的舍入误差。我猜输入数据分布的性质对于确定错误累积量至关重要;虽然我从来没有听说过,也没有忘记推导答案所需的数学知识。基于编译器选项已知浮点数学的确切行为,因此在了解输入数据的情况下,应该可以推导出错误的范围。

而不是更新
prob
在添加项时,在需要读取概率时更新它。使用布尔标志指示在读取前是否需要更新
prob

while ( true ) {
    int n = randomProcess();

    ++totalCount;
    ++count[n];
    dirty = true;
}

void updateBeforeRead() {
    if(dirty) {
        for ( int i = 0; i < K; ++i )
            prob[i] = count[i] / static_cast<double>(totalCount);
        }
        dirty = false;
    }
}
while(true){
int n=随机过程();
++总数;
++计数[n];
肮脏=真实;
}
void updatebeforead(){
如果(脏的){
对于(int i=0;i
如果您的使用在大量样本之间切换,然后根据概率进行大量计算,那么这应该是有效的,同时限制舍入问题。

确定。第二次尝试

鉴于您的测试表明更新
prob
直接提高了算法的性能,您可以通过定期重置来最小化舍入误差

void快速更新(int-id){
如果(totalCount==1){
prob[id]=1.0;
fastUpdates=0;
返回;
}

如果(fastUpdates最快的操作是您不执行的操作。即使您最终使用了每次更新后生成的每个值,如果您不必将更新后的值写回内存,您也可以进行保存。您还可以通过执行乘法而不是除法来节省一些时钟。很可能乘法比除法更快保存的内存访问权限

template<int K>
class Prob
{
private:
    int count[K];
    int totalCount;
    double multiplier;
public:
    update(int id)
    {
        ++count[id];
        ++totalCount;
        multiplier = 1.0 / totalCount;
    }
    double operator[](int id)
    {
        return count[id] * multiplier;
    }
};

Prob<K> prob;
while ( true ) {
    int n = randomProcess();

    prob.update(n);

    // demo
    double sum = 0.0;
    for (int i = 0; i < K; i++)
        sum += prob[i];

    do_work_with_updated_prob_vector(prob);
}
模板
类问题
{
私人:
整数计数[K];
整数总数;
双乘数;
公众:
更新(int-id)
{
++计数[id];
++总数;
乘数=1.0/总计数;
}
双运算符[](整数id)
{
返回计数[id]*乘数;
}
};
Prob-Prob;
while(true){
int n=随机过程();
问题更新(n);
//演示
双和=0.0;
for(int i=0;i
如果我理解正确,您的
count
totalCount
变量是整数,您可以随时从中导出概率,因此浮点精度应该不是问题,假设您使用正常的
update
函数。试着每两周调用一次
update()
template<int K>
class Prob
{
private:
    int count[K];
    int totalCount;
    double multiplier;
public:
    update(int id)
    {
        ++count[id];
        ++totalCount;
        multiplier = 1.0 / totalCount;
    }
    double operator[](int id)
    {
        return count[id] * multiplier;
    }
};

Prob<K> prob;
while ( true ) {
    int n = randomProcess();

    prob.update(n);

    // demo
    double sum = 0.0;
    for (int i = 0; i < K; i++)
        sum += prob[i];

    do_work_with_updated_prob_vector(prob);
}