Haskell 程序优化

Haskell 程序优化,haskell,Haskell,我正在学习Haskell并在spoj.pl上解决一些编程问题。 这个问题的思想是:计算一个数的适当因子之和 所以我的程序读取第一行的数字。然后读一个数字。将其分解(a1^p1*a2^p2)并计算(a1^(p1+1)-1)/(a1-1)*…… 但程序运行缓慢。处理200000个数字需要4秒钟。c上的同一程序只需0.84秒。请帮我优化一下。 代码风格的批评也受到欢迎 以下是源代码: main = do nRaw <- getLine let n = (read

我正在学习Haskell并在spoj.pl上解决一些编程问题。 这个问题的思想是:计算一个数的适当因子之和

所以我的程序读取第一行的数字。然后读一个数字。将其分解(
a1^p1*a2^p2
)并计算
(a1^(p1+1)-1)/(a1-1)*……
但程序运行缓慢。处理200000个数字需要4秒钟。c上的同一程序只需0.84秒。请帮我优化一下。 代码风格的批评也受到欢迎

以下是源代码:

main = do
        nRaw <- getLine
        let n = (read nRaw)::Int in 
         loop n (do
                  vS <- getLine
                  let v = (read vS)::Int in
                   putStrLn (show (solve v))
                )

loop 1 a = a
loop n a = do a
              loop (n - 1) a

simples = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

solve n = (subsolve n simples 1 1 n) - n

subsolve n [] ansnum ansden threshold = (ansnum `div` ansden)
subsolve n (x:spls) ansnum ansden threshold | x * x > threshold    = if n > 1 then subsolve n [] (ansnum * (n * n - 1)) (ansden * (n - 1)) threshold
                                                                              else subsolve n [] ansnum ansden threshold
                                            | (n `mod` x) == 0 = (let (a, p) = (getPower n x 1)
                                                                   in (subsolve a spls (ansnum * ((x * p) - 1)) (ansden * (x - 1)) threshold))
                                            | otherwise        = subsolve n spls ansnum ansden threshold

getPower n x ans | n `mod` x == 0 = getPower (n `div` x) x (ans * x)
                 | n `mod` x /= 0 = (n, ans)
main=do
nRaw 1然后亚溶度n[](ansnum*(n*n-1))(ansden*(n-1))阈值
else亚溶度n[]ansnum-ansden阈值
|(n`mod`x)==0=(让(a,p)=(getpowernx1)
in(亚溶a声压级(ansnum*((x*p)-1))(ansden*(x-1))阈值)
|否则=亚溶度n spls ansnum ansden阈值
getPower n x ans | n`mod`x==0=getPower(n`div`x)x(ans*x)
|n`mod`x/=0=(n,ans)

提前感谢。

使用Haskell的lazy IO可以更清楚地表示从标准输入读取的数字

main :: IO ()
main = interact $ unlines . map (show . solve . read) . tail . lines

solve :: Int -> Int
solve ...
这将使您的代码更加优雅,但不会更快。如果IO和解析数是瓶颈(可以使用探查器检查它),那么应该考虑使用比使用字符列表(字符串)更有效的模块。 不幸的是,通过Data.Text提高效率的代价是代码变得更加冗长和笨拙。例如:

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text.Lazy as Text
import qualified Data.Text.Lazy.IO as Text
import qualified Data.Text.Lazy.Read as R
import qualified Data.Text.Lazy.Builder.Int as B
import qualified Data.Text.Lazy.Builder as B

main :: IO ()
main = Text.interact $ 
   Text.unlines . map (showInt . solve . readInt) . tail . Text.lines

readInt x = case R.decimal x of
  Left err -> error err
  Right (i,"") -> i

showInt = B.toLazyText . B.decimal  
(如果我正确地使用生成器模块,而不是将生成器转换为每行的惰性文本,则会更加笨拙)

明显的改进(在使用
ByteString
s或
text
改进输入读数后)是为了避免使用
div
mod
,而是使用
quot
rem
div
mod
在处理负数时具有更好的性能,但比翻译为常用机器除法和余数指令的
QUOTE
rem
慢得多

更严格一些也可能会有所帮助,但由于乐观主义者非常优秀,因此事先不明显的是,如果你的程序没有使用原始机器
Int 35;
s


关于样式,不要让代码走得太右,至少要给所有顶级函数提供类型签名

foo .... | condition     = result1
         | not condition = result2
你可以把它改写成

foo .... | condition     = result1
         | otherwise     = result2

这可能会在运行时节省一些周期。而且它可以帮助编译器生成更好的代码,因为它知道所有的情况都包括在内。请记住,
(a`rem`b)==0 | |(a`rem`b)/=0
这一点对您来说是显而易见的,但在一般情况下,编译器可能真的很难弄清楚这一点。因此,编译器编写人员知道问题通常无法解决,可能决定根本不检查保护完整性

在添加类型签名时,您可能还希望将它们专门化为
Int
s(我假设Int范围足以解决您的问题)。例如,当前
subsolve
的类型被推断为
subsolve::Integral t=>t->[t]->t->t->t->t
。这可能比
subsolve::Int->[Int]->Int->Int->Int->Int->Int
效率低,因为它没有导出,
read
的结果是给定类型
Int
,事实上,在这里所有的东西都得到类型
Int
,至少在经过优化编译时是这样。一般来说,如果
Int
足够且性能很重要,那么坚持使用
Int是个好建议。您使用
getPower
时是否存在错误?您只使用
getpowernx0
调用它一次,但是使用
ans==0
意味着结果总是
(n,ans)
总是以
(n,0)
的形式出现。是的,存在错误。现在修好了。谢谢。首先要检查的是:您是否使用优化(-O2)编译?这有很大的不同。我想你是这样想的,但是问一下也没什么坏处:)我试过这个,但是我的haskell版本没有Builder.Int包和spoj.pl judge。我试图使用ByteString,但我不知道如何将Int转换为ByteString。@维克多德尼索夫:是的,我看到了readInt函数。但是我怎样才能把Int转换成ByteString呢?我用了这一行,这就足够了。也许有更好的解决办法。main=C.interact$C.unlines。map(C.pack.show.solve.fst.M.fromJust.C.readInt)。尾巴。Clines@VictorDenisov:您可以从中尝试packDecimal函数