计算一个长列表中随机双精度数的几何平均值

5

今天我在构建一个受限玻尔兹曼机时遇到了一个看似微不足道但很困难的问题。基本上,我将2k个值初始化为0到1之间的随机double。

我想做的是计算这个数据集的几何平均值。我遇到的问题是,由于数据集非常长,将所有值相乘将始终得到零,并且每一步都进行正确的根运算只会得到1。

我可以将列表分成多个小块,但我认为这样做很麻烦。有没有更优雅的方法?

理论上,我希望扩展我的当前RBM代码,让它有接近15k个条目,并能够在多个线程上运行RBM。不幸的是,这排除了apache commons math(几何平均方法未同步),longs。


数字变得非常小,我正在寻找一个更长期的解决方案。我计划尝试类似的东西,使用接近15k的列表,并且我希望这个解决方案也适用于那个。 - Slater Victoroff
1
https://en.wikipedia.org/wiki/Geometric_mean#Calculation - nhahtdh
3个回答

11

哇,使用大十进制类型真的是过度了!

只需取所有数的对数,找到算术平均值,然后取指数即可。


1
Mehrdad的对数解法确实有效。但你可以更快地完成(可能更准确),具体操作如下:
1.计算数字的指数总和,称为“S”。 2.将所有指数归零,使每个数字介于“1/2”和“1”之间。 3.将数字分成最多1000组。 - 对于每个组,计算数字的乘积。这不会下溢。 - 将乘积的指数添加到“S”中,并将指数归零。 4.现在您只剩下大约1/1000的数字。除非只剩下一个数字,否则重复步骤2和3。 5.将唯一剩余的数字称为“T”。几何平均值为T^(1/N)*2^(S/N),其中N是输入的大小。

我对你的过程有点不清楚,你说的是什么指数?此外,将1000个随机数相乘绝对会下溢。大约在10^-300的数量级上。可能还要低几个数量级。 - Slater Victoroff
@SlaterTyranus:“指数”在这里指的是双精度浮点数中的指数部分——不包括符号位或有效数字。在1/2和1之间乘以1000个双精度浮点数不会下溢,因为最小可表示的指数小于-1000;这就是我选择1000的原因。 - tmyklebu
我相信最小的指数实际上是在-300左右的数量级。http://www.cplusplus.com/forum/general/53760/,这会使其面临下溢的危险,但在较小的批次中,它应该是可以的。你有对此进行基准测试吗?这不是一个优雅的解决方案,但如果性能提升显著,我愿意切换。我的直觉是它会更慢,但我对Java内置对数的了解不是很多。 - Slater Victoroff
@SlaterTyranus:不,最小的正常指数是-1022,比-300要小得多。还有一些叫做subnormals的东西。https://en.wikipedia.org/wiki/IEEE_floating_point包含了关于IEEE浮点的一些非常有用的信息。当在C++中适当实现时,这种方法比对数求和快得多(在我的计算机上快了65倍),并且具有更高的数值稳定性。 - tmyklebu
值得一提的是,我的C++实现不能正确处理无穷大、NaN、次正常数或零。 - tmyklebu
作为参考,我发现一个不错的方法是 exp( mean / Long.highestOneBit(mean) << 1) * Long.highestOneBit(mean) << 1 - Johnny V

0

看起来,在进行足够数量的乘法后,双精度已经不再足够。如果您愿意,会有太多前导零。

关于任意精度算术的维基页面展示了一些解决该问题的方法。在Java中,BigDecimal似乎是最好的选择,尽管以速度为代价。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接