haskell 用组合学改进计数出现的运行时间

brccelvz  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(115)

我想数一下有多少个d位数不包括数字9,因为它可能很大,所以输出它模10^9 + 7。
我运行下面的代码t次,最多10^18位数,我想solve函数应该很容易处理,对吧?所以可能是阅读或打印花费了很多时间。
有什么建议可以加快速度吗?

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)
gwbalxhn

gwbalxhn1#

我认为网站希望看到的是你掌握 * 模乘 * 的概念。它认为:

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

此外,他们并没有随意选择10^9+7 *:据我所知,它是一个巨大的质数,比32位整数所能表示的还要大。因此,我们可以用Int32来做所有的微积分,这比用Integer(具有任意精度)来做要快。

乘法法(效率较低)

因此,我们可以创建自己的mulmod函数:

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

现在,我们可以计算数字模m

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

然后这个问题就可以用下面的公式来解决:

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

对于给定的样本输入,其结果为:

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

根据网站的说法,这是正确的。

动力接近

我们可以定义一个powmod,如下所示:

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)

solve机制为:

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

再次导致:

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

最后一个查询只花了几毫秒的时间,所以我想这已经足够高效了。
15:29:31“我讨厌数字九”接受0.01 s Haskell
因此,计算测试用例仅需0.01秒。

t3irkdon

t3irkdon2#

是的,这是一个简单的算术问题,可以很容易地处理在基地9,因为我们将只下降一个数字字符。如果你不想使用2个数字字符,如我不想9和4,那么你应该去与基地8。所以我的解决方案将是;

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

相关问题