diff --git a/solutions/0036.Palindromes/euler_36.hs b/solutions/0036.Palindromes/euler_36.hs new file mode 100644 index 0000000..3e8cbd1 --- /dev/null +++ b/solutions/0036.Palindromes/euler_36.hs @@ -0,0 +1,110 @@ +{-# 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 diff --git a/solutions/0036.Palindromes/readme.md b/solutions/0036.Palindromes/readme.md new file mode 100644 index 0000000..a77ccd5 --- /dev/null +++ b/solutions/0036.Palindromes/readme.md @@ -0,0 +1,13 @@ +# README + +说起来,还是先找到所有回文数字更快,单纯计算查找确实稍慢。 +另外,就是python3.13比haskell还要快一线……我也是囧了…… + + + +**haskell** + +```bash +> ghc euler_36.hs +> ./euler_36.exe +```