如何优化这个haskell片段

时间:2010-08-18 04:48:34

标签: optimization haskell integer

我正在尝试创建一个小模块来进行基于十进制的计算。数字存储为整数mantisse,精度值由int:

指定
data APNum =
  { getMantisse :: Integer
  , getPrecision :: Int }

例如:

APNum 123 0 -> 123
APNum 123 1 -> 1.23
APNum 123 2 -> 12.3
...

(不允许使用负精度)。

现在我写了这个函数,它通过尽可能多的尾随零来自动调整精度:

autoPrecision :: APNum -> APNum
  autoPrecision x@(APNum m p) = if p > maxPrecision
    then autoPrecision $ setPrecision x maxPrecision
    else autoPrecision' m p where
    autoPrecision' m p = let (m',r) = m `divMod` 10 in
      if r /= 0 || p <= 0 then APNum m p else autoPrecision' m' (pred p)

(MaxPrecision和setPrecision很明显,我认为)。

问题是,这个片段的性能非常差,特别是n个数字超过10000个数字。有没有简单的优化?

3 个答案:

答案 0 :(得分:3)

您可以使用二分搜索来找到除以m的最高幂10,而不是尝试所有连续值。

import Numeric.Search.Range
import Data.Maybe

data APNum = APNum{getMantisse :: Integer, getPrecission :: Int} deriving Show

setPrecision (APNum m _) x = APNum m x
maxPrecission = 200000

findDiv x = pred $ fromJust $ searchFromTo (p x) 0 maxPrecission where
    p x n = x `mod` 10^n /= 0

autoPrecision :: APNum -> APNum
autoPrecision x@(APNum m p)
= if p > maxPrecission then
    autoPrecision $ setPrecision x maxPrecission else APNum m' p'
where d = min (findDiv m) p
        p' = p - d
        m' = m `div` 10^d

我在这里使用提供binary-search的{​​{1}}包。这应该会给你一个很大的加速。

答案 1 :(得分:2)

看起来即使是简单的字符串操作仍然更快:

maxPrecision = 2000000

autoPrecision (APNum m p) =
   let p' = min p maxPrecision
       (n',ds) =  genericDropNWhile (=='0') p' $ reverse $ show m
   in APNum (read $ reverse ds) n'
   where
     genericDropNWhile p n (x:xs) | n > 0 && p x = genericDropNWhile p (n-1) xs
     genericDropNWhile _ n xs = (n,xs)

用此测试:

main = print $ autoPrecision $ APNum (10^100000) (100000-3)

编辑:糟糕,仅对具有大量零的数字更快。否则,这种双重转换肯定会变慢。

答案 2 :(得分:0)

同样x mod 10 == 0意味着x mod 2 == 0,而且测试成本更低