社区讨论

30pts Haskell 有无优化空间

P5137 polynomial参与者 2已保存回复 1

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
1 条
当前快照
1 份
快照标识符
@lo27uac4
此快照首次捕获于
2023/10/23 09:25
2 年前
此快照最后确认于
2023/11/03 09:39
2 年前
查看原帖
退役之后稍微学了些 Haskell,感觉函数式编程写这种数学题应该没有大问题,但是两版代码一个 WA 30pts 一个 TLE 30pts,前者用了 Int(只有 64 位,会爆),后者用了 Integer(高精,会 T)。然后我搜了搜,好像 Haskell 还没有类似 C++ 的那种 long double 可以用来写快速乘。过来请教洛谷大佬们有无优化空间,能把这个代码改成 AC 的。
下面这份代码是我说的 TLE 的代码,供各位查阅:
HASKELL
{-# OPTIONS_GHC -O2 #-}
{-# LANGUAGE Strict, MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies, BangPatterns #-}
import qualified Data.ByteString.Char8 as C
import Control.Applicative
import Data.Array
import Data.Bits

int :: C.ByteString -> Int
int s = let Just (n, _) = C.readInt s in n

readInt :: IO Int
readInt = int <$> C.getLine

integer :: C.ByteString -> Integer
integer s = let Just (n, _) = C.readInteger s in n 
 
readIntegers :: IO [Integer]
readIntegers = map integer . C.words <$> C.getLine

replicateTransitionM :: Monad m => Int -> a -> (a -> m a) -> m a
replicateTransitionM 0 x _ = return x
replicateTransitionM n x f = f x >>= \y -> replicateTransitionM (n - 1) y f

replicateTransitionM_ :: Monad m => Int -> a -> (a -> m a) -> m ()
replicateTransitionM_ n x f = replicateTransitionM n x f >> return ()

-- Z3 a = {alpha, beta, gamma, p}
data Z3 a = MatrixSimplified a a a a

infixl 5 >*< 
(>*<) :: (Integral a) => Z3 a -> Z3 a -> Z3 a
f0@(MatrixSimplified alpha0 beta0 gamma0 p0) >*< f1@(MatrixSimplified alpha1 beta1 gamma1 p1) 
  | p0 /= p1 = error "Failed to calculate the product of Z3's with different modules"
  | otherwise = MatrixSimplified alpha beta gamma p
  where 
    alpha = mod (alpha0 * alpha1) p0
    beta' = (mod (beta0 * alpha1) p0) + (mod (gamma0 * beta1) p0)
    beta = if beta' >= p0 then beta' - p0 else beta'
    gamma = mod (gamma0 * gamma1) p0
    p = p0

infixl 5 >!<
(>!<) :: (Integral a) => Z3 a -> Int -> a
f0@(MatrixSimplified alpha beta gamma p) >!< t
  | t == 0 = alpha
  | t == 1 = beta
  | t == 2 = gamma
  | otherwise = error "Failed to fetch an element of Z3"

calcExp :: (Integral a) => Z3 a -> Z3 a -> Integer -> a
calcExp f0 f1 t = (calcExp' f0 f1 t) >!< 1
  where
    calcExp' _ f1 0 = f1
    calcExp' f0 f1 t
      | even t = calcExp' (f0 >*< f0) f1 (shiftR t 1)
      | otherwise = calcExp' (f0 >*< f0) (f1 >*< f0) (shiftR t 1)

main :: IO ()
main = do
  t <- readInt
  replicateTransitionM_ t 0 $ \x -> do
    [n, a, b, p] <- readIntegers
    let [f0, f1] = [MatrixSimplified a (toInteger 1) b p, MatrixSimplified (toInteger 0) (toInteger 1) b p]
    (putStrLn $ show (calcExp f0 f1 n)) >> (return 0)
  return ()
我知道 Haskell 本身性能是不如 C++ 的,但是感觉性能差距大概也就两倍吧,我用了题解的 Memory_of_winter 的矩阵快速幂方法写了 C++ 代码,最慢的点跑了不到 380ms,×2 就是不到 760ms,感觉 Haskell 也有戏……

回复

1 条回复,欢迎继续交流。

正在加载回复...