假设我有这样的代码,用于执行Double
s的补偿(Kahan-Babuška-Neumaier)求和:
-- A type for compensated summation.
data Sum = Sum {-# UNPACK #-} !Double {-# UNPACK #-} !Double
deriving (Eq, Ord, Show)
-- Add a number to the Sum, updating the error value as well.
addSum :: Sum -> Double -> Sum
addSum (Sum s c) x = Sum s' c' where
s' = s + x
d | abs s >= abs x = (s - s') + x
| otherwise = (x - s') + s
c' = c + d
{-# INLINE addSum #-}
-- Get the compensated value from the Sum.
getSum :: Sum -> Double
getSum (Sum s c) = s + c
{-# INLINE getSum #-}
-- Add a whole list (or list-like object) of Doubles.
sumAll :: Foldable f => f Double -> Double
sumAll ls = getSum $ foldl' addSum (Sum 0 0) ls
{-# INLINE sumAll #-}
但是,我还需要一个Monoid
,因为我想并行计算值。我正在寻找的是:
-- Make a Sum from a Double value. It has an error of 0.
mkSum :: Double -> Sum
mkSum x = Sum x 0
{-# INLINE mkSum #-}
instance Monoid Sum where
mempty = Sum 0 0
{-# INLINE mempty #-}
mappend (Sum sa ca) (Sum sb cb) = ...?
我认为它可能有类似的形式:
mappend a@(Sum sa ca) b@(Sum sb cb)
| ??? = addSum a (sb + cb)
| otherwise = addSum b (sa + ca)
我希望它是可交换的,并且至少与正常的Double
添加相关联,如果不是更多的话。
答案 0 :(得分:0)
如果有正确的mappend
可能
mappend :: Sum -> Sum -> Sum
mappend (Sum s c) (Sum x c2) = Sum s' c' where
s' = s + x
d | abs s >= abs x = (s - s') + x
| otherwise = (x - s') + s
c' = (c + c2) + d
来源:预感。
预感是满足财产mappend s1 (Sum x 0) = addSum s1 x
并且可交换的最简单的事情。它的相关性很难检查。
检查关联性很困难,因为添加Double
s并不是关联的。因此mappend
的{{1}}不是关联的;问题是,是否足够关联 。一个可能的标准是,关联Sum
时的错误变化小于或等于将参数转换为mappend
时的错误变化。
对于您的标准,它已经"至少与正常sumAll
添加"相关联,因为它是正常的Double
添加。事实上,它是对正常Double
添加的改进。如果Double
是关联的,则Double
将为d
。添加到总和0
的任何非零d
都是改进关于c1 + c2
添加的关联性,跟踪和纠正非关联性中的错误Double
s。
使用更好的求和Double
的方法可以改善c'
的计算。他们可以分类,la:
Double
或使用Kahan求和自己总结sumThree :: Double -> Double -> Double -> Double
sumThree a b c
| abs a >= abs b && abs a >= abs c = (b + c) + a
| abs b >= abs a && abs b >= abs c = (a + c) + b
| otherwise = (a + b) + c
。如果他们自己使用Kahan求和进行求和,sumAll [c, c2, d]
无法按sumAll
实现。