Am Donnerstag 03 Dezember 2009 06:52:01 schrieb Aditya M:
> Hello
>
> Thanks for all the help!
>
> I only have a couple of questions.
>
> On Thu, Dec 3, 2009 at 03:45, Daniel Fischer <daniel.is.fisc...@web.de> wrote:
> > Am Mittwoch 02 Dezember 2009 22:44:01 schrieb Don Stewart:
> >> aditya87:
> >> > Hi,
> >> >
> >> > I am trying to solve this problem:
> >> > https://www.spoj.pl/problems/LASTDIG/ It is very simple. Given a and
> >> > b, return the last digit of a^b. b could be large, so I used
> >> > logarithmic exponentiation and
> >
> > Just to mention it, you can do something much much faster for this
> > problem. Something in the microsecond range (if IO is fast enough,
> > millisecond otherwise).
>
> I guess you mean that we can find the cycle that the last digits
> follow while multiplying repeatedly by a, and then use that.

Yes. Except there's not much finding to be done, you can pretty much write it 
down 
immediately.

But I underestimated the slowness of String IO, so it's not gaining you 
terribly much 
unless you resort to ByteString IO.
As an indication, for files containing 20.9 million, 2.85 million, 25942 (a,b) 
pairs 
respectively:
ByteString + shortcut: 4.6 seconds, 0.66 seconds,  0.00 seconds
ByteString + modular exponentiation: 49.2 seconds, 6.74 seconds, 0.06 seconds
String + shortcut: 262 seconds, 35.04 seconds, 0.33 seconds
String + modular exponentiation: 303 seconds, 40.73 seconds, 0.38 seconds

(Note: I have tweaked the String IO, using 

main = fmap lines getContents >>= putStr . unlines . (map doit) . tail

and modified doit (returns a String now, is a little stricter, calls words line 
only once; 
also read - for the base, I use (digitToInt . last), it's much faster than read)

1. With "replicateM n getLine" or "sequence $ take n $ repeat getLine", the 
entire input 
must be read in before processing can start. Firstly that's slower and secondly 
it needs a 
lot of memory - more than I have, for the larger files.
2. (putStr . unlines) is faster than mapM_ putStrLn.

It could be tweaked more, but that wouldn't gain nearly as much as switching to 
ByteString.)

So with String IO, in either method the overwhelming part of the time is used 
for IO (and 
read), in comparison, the algorithmic difference pales. With ByteString IO, the 
algorithmic difference stands out.

>
> I'll try that next in Haskell!
>
> >>     {-# LANGUAGE BangPatterns #-}
> >>
> >>     lastdigit :: Int -> Int -> Int -> Int
> >>     lastdigit 0 0 _  = 1
> >>     lastdigit a b !c | even b    = lastdigit ( (a*a) `rem` 10 ) (b
> >> `quot` 2) c
> >>
> >>                      | b == 1    = (a*c) `rem` 10
> >
> > However,
> >
> >   | otherwise = lastdigit ( (a*a) `rem` 10 ) (b `quot` 2) (a*c)
>
> This bang pattern (!c) is giving me pattern match errors.

??? without the LANGUAGE pragma, it would give a parse error, so you can't have 
forgotten 
that. But in

lastdigit :: Int -> Int -> Int -> Int
lastdigit 0 0 _  = 1
lastdigit a b !c
    | even b    = lastdigit ( (a*a) `rem` 10 ) (b `quot` 2) c
    | b == 1    = (a*c) `rem` 10
    | otherwise = lastdigit ( (a*a) `rem` 10 ) (b `quot` 2) ((a*c) `rem` 10)

there is simply no possibility for a pattern match error (other than having one 
argument 
bottom). I'm flabbergasted.

> Is its only effect evaluating c instead of plain substitution?

Yes, it keeps c evaluated instead of building a thunk. You can get pretty much 
the same 
effect by having

lastdigit :: Int -> Int -> Int -> Int
lastdigit 0 0 _  = 1
lastdigit a b c
    | even b    = lastdigit ( (a*a) `rem` 10 ) (b `quot` 2) c
    | b == 1    = (a*c) `rem` 10
    | otherwise = lastdigit ( (a*a) `rem` 10 ) (b `quot` 2) $! ((a*c) `rem` 10)

using strict application on the last argument when it is modified.


_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe

Reply via email to