【问题标题】:How to optimize this haskell snippet如何优化这个 haskell 片段
【发布时间】:2010-08-18 04:48:34
【问题描述】:

我正在尝试创建一个小模块来进行基于十进制的计算。数字存储为整数尾数,精度值由 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 很明显)。

问题是,这个sn-p的性能很差,特别是n个超过10000位的数字。有没有简单的优化?

【问题讨论】:

  • “前导零”是指“尾随零”吗? (即APNum 12000 5 -> APNum 12 2
  • @KennyTM 这是我的假设,因为整数不能有前导零

标签: optimization haskell integer


【解决方案1】:

您可以使用二分法查找 10 的最大幂,该幂除以 m,而不是尝试所有连续值。

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 包,它提供searchFromTo :: Integral a =&gt; (a -&gt; Bool) -&gt; a -&gt; a -&gt; Maybe a。这应该会给你一个很大的加速。

【讨论】:

  • 顺便说一句...是否有一些懒惰可以关闭以加快速度?
  • 这可以按预期工作并缩短执行时间。真正的问题是,haskell 在执行此操作时会消耗大量空间。我很快就会写一个完整的程序示例。
【解决方案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)

编辑:哎呀,只有有很多零的数字才能更快。否则这种双重转换肯定会更慢。

【讨论】:

  • 我不确定在这里展示和阅读的费用是否值得。这些都必须做很多数学运算才能转换成字符串。
  • 好吧,在我的机器上,我的“主要”功能(见上文)比其他两种 autoPrecision 变体运行得更快(分别为 0.05 秒和 0.84 秒和 4.05 秒)。但是,正如我所指出的,只有尾数包含大量尾随零时才会出现这种情况。我认为这是因为在大整数上重复应用div 非常昂贵,而我认为show 在这里更便宜(需要检查show 的实现)。我并不是说应该使用 show 来处理这类事情,而是说幼稚的 div 可能不是这里的最佳解决方案。
【解决方案3】:

x mod 10 == 0 也意味着 x mod 2 == 0,这样测试成本更低

【讨论】:

  • 那么所有能被 2 整除的数字也能被 10 整除吗?再想一想:-)
  • 不,但您可以先除以 2(非常便宜),然后,如果 n = 0 mod 2 除以 5。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-15
相关资源
最近更新 更多