C++ 这将累积多少浮点错误?
我有一个随机进程,当调用它时,它返回一个介于0和K-1之间的随机数,其中K可能相当高。我希望跟踪任何结果发生的次数,并将所有计数标准化为概率分布。每次调用随机过程时,我都要这样做,以便我对随机过程的分布估计尽可能是最新的 一种简单的方法可以是: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
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);
}