我正在尝试创建一个小模块来进行基于十进制的计算。数字存储为整数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个数字。有没有简单的优化?
答案 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,而且测试成本更低