I’m learning Haskell and solving some programming problems on spoj.pl.
The idea of the problem is the following: calculate summation of proper divisors of a number.
So my program reads number of numbers in the first line. Then read a number. Factorizes it (a1^p1 * a2^p2) and calculates (a1 ^ (p1 + 1) - 1) / (a1 - 1) * ...
But program works slow. It takes 4 seconds to process 200000 numbers. Same program on c does it in 0.84 seconds. Please help me to optimize it.
Code style criticism is also welcomed.
Here is the source code:
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)
Thanks in advance.
Reading numbers from standard input can be expressed much more clearly using Haskell’s lazy IO.
This will make your code more elegant, but not faster. If the IO and parsing numbers are the bottleneck (you can check it by using the profiler), you should consider using the Data.Text module which is more efficient than using lists of characters (String).
Unfortunately the price you gain for increased efficiency with Data.Text is that the code becomes more verbose and clunky. For example:
(And would be even more clunky if I used the Builder module properly instead of converting a Builder to lazy text for each line)