Как оптимизировать этот фрагмент кода на Haskell - PullRequest
0 голосов
/ 18 августа 2010

Я пытаюсь создать небольшой модуль для выполнения десятичных вычислений.Число сохраняется как целочисленный 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 ]

3 голосов
/ 18 августа 2010

Вы можете использовать бинарный поиск, чтобы найти наибольшую степень 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 => (a -> Bool) -> a -> a -> Maybe a. Это должно дать вам большое ускорение.

2 голосов
/ 18 августа 2010

Похоже, что даже простая строковая операция все еще быстрее:

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)

РЕДАКТИРОВАТЬ: К сожалению, быстрее только для чисел с большим количеством нулей.В противном случае это двойное преобразование определенно медленнее.

0 голосов
/ 18 августа 2010

также x mod 10 == 0 означает x mod 2 == 0, и это дешевле для проверки на

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...