使用对数来避免数值下溢的算术问题(取2)

时间:2010-02-19 02:41:56

标签: logarithm numerical-methods

我有两个分数列表;

A = [ 1/212, 5/212, 3/212, ... ]

B = [ 4/143, 7/143, 2/143, ... ]

如果我们定义A' = a[0] * a[1] * a[2] * ...B' = b[0] * b[1] * b[2] * ...

我想计算A'和B'

的标准化值

即具体为A' / (A'+B')B' / (A'+B')

的值

我的麻烦是A是B都很长并且每个值都很小所以计算产品会导致数值下溢很快...

我理解通过对数将产品转化为总和可以帮助我确定A'或B'中哪一个更大

max( log(a[0])+log(a[1])+..., log(b[0])+log(b[1])+... )

并且使用日志我可以计算A' / B'的值,但我该怎么办A' / A'+B'

我最好的选择是将数字表示保持为分数,即A = [ [1,212], [5,212], [3,212], ... ]并实现我自己的算术,但它变得笨拙而且我感觉有一种(简单的)对数方式我只是缺少....

A和B的分子不是来自序列。对于这个问题,它们也可能是随机的。如果它对A中所有值的分母有所帮助,那么B的所有分母都是如此。

欢迎任何想法!

(ps。我在24小时之前询问similar question关于A'/B'的比例,但实际上是错误的问题。我实际上是在{{1}之后抱歉,我的错误。)

4 个答案:

答案 0 :(得分:5)

我在这里看到的方法很少

首先你可以注意到

A' / (A'+B') = 1 / (1 + B'/A')

你知道如何用对数计算B'/A'

另一种方法是实现你自己的理性算术,但你不需要做很多事情。既然你知道整个数组的分母是相同的,它会立即给你

numerator(A') = numerator(a[0]) * numerator(a[1]) ...
denumerator(A') = denumerator(a[0]) ^ A.length

你现在需要做的就是总结A'和B'这很容易,然后乘以A'1/(A'+B'),这也很容易。这里最难的部分是对结果值进行标准化,这是通过模运算完成的,并且是微不足道的。

或者,由于你最有可能使用一些流行的脚本语言,其中大多数都有内置的理性算术类,Python和Ruby肯定会有它们。

答案 1 :(得分:1)

  

我迄今为止最好的选择是将数字表示保留为分数并实现我自己的算术,但它变得笨拙

您使用的是哪种语言?如果你可以重载运算符,那么构建一个Fraction类应该非常容易,你可以将其视为一个数字,几乎无处不在。

例如,确定分数A / B是否大于C / D基本上是在比较A * D是否大于B * C

答案 2 :(得分:1)

A和B在你提到的每个分数中都有相同的分母。对于列表中的每个术语都是如此吗?如果是这样,为什么不在计算产品时考虑到这一点?分母只是X ^ n,当X是值时,n是列表中术语的数量。

如果你这样做,你会遇到相反的问题:分子溢出。您知道它不能小于max(X)^ n,其中max(X)是分子中的最大值,n是列表中的术语数。如果可以计算,则可以查看计算机是否存在问题。你不能把10磅的东西放在一个5磅的袋子里。

不幸的是,对数属性限制了以下简化:

alt text http://www.equationsheet.com/latexrender/pictures/b3b5dfbe63069b8c97bd916c92457c05.gif

alt text http://www.equationsheet.com/latexrender/pictures/2ccb250980a1ca541434aa832986cd91.gif

所以你坚持:

alt text http://www.equationsheet.com/latexrender/pictures/598c4f3f41feb820b4f690d74309cec3.gif

如果您使用支持无限精确数字的语言(例如,Java BigDecimal),它可能会让您的生活更轻松一些。但是在你计算之前做一些思考仍然是一个很好的论据。为什么在优雅的时候使用暴力?

答案 3 :(得分:0)

嗯......如果你知道A'(A'+B'),那么B'(A'+B')应该是一个减去那个。我个人不会使用对数。我会用实际的分数。我还会使用某种BigInt类来表示分子和分母。你使用哪种语言? Python非常适合。