同名字的数值求和插入行_从求和算法了解截断误差对数值计算的影响

机器学习是现在很火的一门技术,很多人一提起机器学习就大谈特谈各种算法。这些算法都有理论基础,主要就是数学公式,然后就是用数值方法求解这些公式。跟机器学习类似,油藏数值模拟器的理论基础也是需要用数值算法求解大量的数学公式。有很多人认为只要有数学公式,那么开发成软件就很简单了。但是现实并不是这样,主要原因是因为使用目前的计算机并不能描述一个无限制的浮点数,它只能描述某个固定长度的一个数值,超出这个固定长度(注意是长度而不是值的大小),数据会被截断。截断其实就是舍弃掉超出这个长度以外的数据。截断所造成的数值误差,称为截断误差。这是目前的计算机中表示数据与我们想象最大的差异,也是最不引常人注意的差异。正是这个差异导致了要编程完美地求解一套数学公式并不像很多人想象的那么简单。我们来看最简单的一个公式的数值求解——累加求和公式(下图):

5c1a7e93c88e6b4a582d862da1dd2a3e.png

很多人会说,求和有什么复杂的,把这些数累加起来就行啊,一个循环就可以搞定。理论上来说这个算法没问题,但是现实却是残酷的,并不是所有的数这么加起来就能得到正确的和。由于上面说的差异,计算机在求解的时候会出现大数吃掉小数的情况。这里以C语言中的float类型为例,float是基础的浮点数类型,长度是32位(4个字节)。它所能描述的浮点数是有效位数是7位(实际能绝对保证的只有6位)。下图是IEEE对浮点数的表示定义。

d3d10ce1785195492f307ec570476330.png

假如有两个float类型的数a和b,a=123456而b=2.189。理论上a+b应该等于123458.189。但是实际由于float的精度只有7位,所以从左至右会保留123458.1这几位数,超过这个长度的8、9两个数字会被舍弃,也就是说,0.089这个数值会被舍去,最后你得到的结果是123458.1(实际上.1可能都会被舍弃)。这就是所谓的大数吃小数,大数a把小数b吃掉了。这个数虽然小,但是如果数据量足够大,积累起来也是一个不小的误差。大家不要觉得大惊小怪,在研究计算机数值算法的人眼里,这是非常正常的情况。

在数值算法中可以使用Kahan求和算法来减小这个误差。Kahan求和算法的精髓就是用一个变量来记住每次被舍弃的数值,再每次迭代循环求和时加在最后的结果上。其算法的伪代码表示如下:

405bc9b85fb48d65e26a982226722830.png

使用C语言实现Kahan求和算法来对比一下直接累加求和和使用Kahan求和算法的差异。代码如下:

fe7fdb8fb0f128c38f00dc82fe9b1a44.png

上述程序编译运行后可以得到下图所示的结果:

7ffea15d31610ad81b41d95760b4920c.png

从结果可以看到明显的差异,直接求和的结果与理论值误差太大,明显错误。而Kahan求和算法得到的结果与理论值一致。可想而知,对于油藏数值模拟中大量网格数据需要求和,如果这些数值相差很大,不采用合理的算法求和,不仅得不到正确得结果,还会造成计算不稳定,收敛性差,浪费时间。这里虽然只以C语言为例,但是这个问题普遍存在于所有编程语言中。它的根源来自于计算机硬件,只能靠算法来弥补。

当然,除了Kahan算法以外,还有一些别的算法来保证数值计算的精度,各位可以查阅相关文献,这里不再赘述。不同的算法适用于不同场合,有时为了正确的求解一套数学方程,需要组合使用多种算法来保证数值计算的精度。开发一套软件,尤其是像油气藏数值模拟器这样的数值计算软件,推导出数学公式只是开发的基础,在编制成软件时还需要设计合理的算法来避免数值截断带来的误差问题。要把文献上的理论公式编成可用的数值模拟软件,需要跨越无数个计算机截断误差带来的坑才行。

7092d3021e176ff9c0af5480921ca0f4.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值