简体   繁体   中英

Improve runtime of counting appearances with combinatorics

I want to count how many d digit numbers does not include the number 9, and since it can be big, output it modulo 10^9 + 7.

Im running the following code t times, on up to 10^18 digit numbers, which I guess the solve function should take care of easily, right? So then it is perhaps the reading or printing that takes a lot of time.

Any tips how to speed this up?

main = do
    contents <- getContents
    let (t:ds) = (map read . words) contents
    let ans = map solve ds
    sequence_ (map print ans)

solve :: Integer -> Integer
solve ds = mod (8 * 9^(ds - 1)) (10^9 + 7)

I think what the site wants to see, is that you grasp the concept of modulo over multiplication . It holds that:

(a * b) mod c == ((a mod c) * (b mod c)) mod c

Furthermore they did not choose 10^9+7 arbitrarily : it is - as far as I know - a huge prime number than can be represented by an 32-bit integer. As a result we can do all calculus with Int32 , which is faster than working with Integer (that has arbitrary precision).

Multiplication approach (less efficient)

As a result, we can make our own mulmod function:

mulmod :: Int32 -> Int32 -> Int32 -> Int32
mulmod m a b = mod (a*b) m

Now we can calculate the number modulo m with:

solvemod :: Int32 -> Int -> Int32
solvemod m d = foldl (mulmod m) 8 (replicate (d-1) 9)

And then the problem can be solved with:

solve :: Int -> Int32
solve = solvemod (10^9+7)

For the given sample input it results in:

Prelude> solve 1
8
Prelude> solve 2
72
Prelude> solve 100
343393926

Which is - according to the website - correct.

Power approach

Nevertheless it is still inefficient. We can define a powmod like:

powmod :: Integral i => Int64 -> Int64 -> i -> Int64
powmod m = powmod'
    where powmod' _ 0 = 1
          powmod' a i | even i = rec
                      | otherwise = mod (a*rec) m
              where rec = powmod' (mod (a*a) m) (div i 2)

Then the solve mechanism is:

solve :: Integral i => i -> Int64
solve d = mod (8 * powmod m 9 (d-1)) m
    where m = 10^9+7

Again resulting in:

*Main> solve 1
8
*Main> solve 2
72
*Main> solve 100
343393926
*Main> solve (10^18)
303706813

The last query took only a few milliseconds, so I guess this is efficient enough. I submitted the last approach to Kattis, and got:

15:29:31 "I Hate The Number Nine" Accepted 0.01 s Haskell

So it took only 0.01s to calculate the testcases.

Yes this is simply an arithmetic problem which can easily be handled in base 9 since we will drop only one number character. If you don't want to use 2 number characters such as I don't want 9 and 4 then you shall go with base 8. So my solution would be;

solve :: Int -> Integer
solve n = (8*9^(n-1)) `mod` (10^9 + 7)

*Main> solve 1
8
*Main> solve 2
72
*Main> solve 100
343393926

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM