Files
Sidney Zhang 5fd04305ee feat(Palindromes):添加欧拉项目第36题解决方案
📝 docs(Palindromes):创建README文档说明性能对比
2026-01-06 14:30:51 +08:00

111 lines
3.4 KiB
Haskell

{-# LANGUAGE BangPatterns #-}
import Data.Time (diffUTCTime, getCurrentTime)
import Text.Printf (printf)
import Data.Bits (shiftR, testBit, setBit)
import Control.DeepSeq (force, NFData)
import Control.Exception (evaluate)
-- 计时函数
time :: NFData a => String -> IO a -> IO a
time label action = do
start <- getCurrentTime
result <- action
result' <- evaluate (force result)
end <- getCurrentTime
printf "%s took time: %.6f seconds\n" label (realToFrac (diffUTCTime end start) :: Double)
return result'
-- 反转十进制数字(辅助函数)
reverseDecimal :: Integer -> Integer
reverseDecimal = go 0
where
go acc 0 = acc
go acc x = let (q, r) = quotRem x 10
in go (acc * 10 + r) q
-- 十进制回文检查
isDecimalPalindromic :: Integer -> Bool
isDecimalPalindromic n = n == reverseDecimal n
-- 二进制回文检查(使用位运算)
isBinaryPalindromic :: Integer -> Bool
isBinaryPalindromic n
| n < 0 = False
| otherwise = n == reverseBinary n
where
-- 计算二进制位数
bitLength :: Integer -> Int
bitLength 0 = 1
bitLength x = go 0 x
where
go !len 0 = len
go !len y = go (len + 1) (y `shiftR` 1)
-- 反转二进制位
reverseBinary :: Integer -> Integer
reverseBinary x = go 0 (bitLength x) x
where
go !acc 0 _ = acc
go !acc !len y
| testBit y 0 = go (acc `setBit` (len - 1)) (len - 1) (y `shiftR` 1)
| otherwise = go acc (len - 1) (y `shiftR` 1)
-- 简单方法:遍历所有数字
mainSimple :: Integer -> IO ()
mainSimple limit = do
let nums = [0..limit]
palindromes = filter (\n -> isDecimalPalindromic n && isBinaryPalindromic n) nums
result = sum palindromes
print result
-- 生成指定长度的十进制回文数(优化版)
palindromesOfLength :: Int -> [Integer]
palindromesOfLength 1 = [0..9]
palindromesOfLength len
| even len = buildPalindromes evenPalindrome halfRange
| otherwise = buildPalindromes oddPalindrome halfRange
where
halfLen = (len + 1) `div` 2
start = 10^(halfLen-1)
end = 10^halfLen - 1
halfRange = [start..end]
buildPalindromes :: (Integer -> Integer) -> [Integer] -> [Integer]
buildPalindromes f = map f
evenPalindrome :: Integer -> Integer
evenPalindrome half =
let s = half
reversed = reverseDecimal s
shift = 10^halfLen
in s * shift + reversed
oddPalindrome :: Integer -> Integer
oddPalindrome half =
let s = half
power = halfLen - 1
shift = 10^power
reversed = reverseDecimal (s `quot` 10)
in s * shift + reversed
-- 快速方法:生成回文数而非遍历
mainQuick :: Integer -> IO ()
mainQuick limit = do
let maxLength = length (show (limit - 1))
-- 限制每个长度生成的回文数不超过limit
limitedPalindromes = takeWhile (<= limit) $ concatMap palindromesOfLength [1..maxLength]
validPalindromes = filter isBinaryPalindromic limitedPalindromes
result = sum validPalindromes
print result
-- 主函数
main :: IO ()
main = do
let limit = 1000000
putStrLn "=== mainSimple (朴素遍历法) ==="
time "mainSimple" $ mainSimple limit
putStrLn "\n=== mainQuick (回文数生成法) ==="
time "mainQuick" $ mainQuick limit