[Haskell-beginners] What's the difference between those two solution?

Brent Yorgey byorgey at seas.upenn.edu
Tue Dec 13 13:37:09 CET 2011


On Tue, Dec 13, 2011 at 11:49:03AM +0800, Haisheng Wu wrote:
> Hello,
>   I'm trying to solve Euler problem 104 with the solution "My Solution"
> below but it takes quite long time therefore I quite.
>   Then I turn to haskell wiki for better solution which work well but I can
> not figure out why it is better than mine.
>   I'm wondering whether more function call decrease the performance.
> 
>   Could you please help a little?
>   Thank you.
> 
> *-- | My Solution *
> main = print $ snd $ head $ dropWhile (\(x,y) -> (not . bothNinePandigit
> "123456789") x) (zip fibs [1..])
> 
> bothNinePandigit digits n = isFirstNinePandigit digits n &&
> isLastNinePandigit digits n
> 
> isLastNinePandigit  digits n = digits == sort (lastDigits 9 n)
> isFirstNinePandigit digits n = digits == sort (firstDigits 9 n)
> 
> firstDigits k n = take k (show n)
> lastDigits  k n = show (n `mod` 10^k)
> 
> fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
> 
> *-- | From Haskell Wiki *
> fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
> 
> isFibPan n =
>   let a = n `mod` 1000000000
>       b = sort (show a)
>       c = sort $ take 9 $ show n
>   in  b == "123456789" && c == "123456789"
> 
> ex_104 = snd $ head $ dropWhile (\(x,y) -> (not . isFibPan) x) (zip fibs
> [1..])

Aha, this is sneaky!  

Having a bunch of function calls should not make
a difference if you are compiling with -O2 (you are compiling with
-O2, aren't you)?  Nonetheless, even compiling with -O2 I was also
getting the results you mention -- the wiki version was pretty fast
(about 24s) whereas your version took more than 15 minutes.

So I ran your version with profiling to help figure out what was going
on.  I compiled with

  ghc --make -O2 -prof -auto-all -rtsopts PE104.hs

and then ran with

  ./PE104 +RTS -p -RTS

This causes a file "PE104.prof" to be written which has a bunch of
data on execution time and allocation broken down by function. The
results showed that 95% of the program's run time was being spent in
'firstDigits'.

And then it hit me -- the difference is due to the fact that your
version and the wiki version test the first digits and the last digits
in a different order!

'show' on integers is (relatively) very slow.  Your version first
tests the first 9 digits of the number -- note that computing the
first digits of a number requires computing all the digits, even the
ones that don't get shown.  Only if the first 9 digits are "123456789"
does your version go on to test the last nine digits (since (&&) is
lazy).  The wiki version, on the other hand, first tests the last 9
digits (much faster) and only if those are "123456789" does it bother
doing the (expensive) test for the first 9 digits.  Since only 112 out
of the first 329000 or so Fibonacci numbers end in the digits 1..9,
this makes a huge difference.

-Brent



More information about the Beginners mailing list