✨ feat(Palindromes):添加欧拉项目第36题解决方案
📝 docs(Palindromes):创建README文档说明性能对比
This commit is contained in:
110
solutions/0036.Palindromes/euler_36.hs
Normal file
110
solutions/0036.Palindromes/euler_36.hs
Normal file
@@ -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
|
||||||
13
solutions/0036.Palindromes/readme.md
Normal file
13
solutions/0036.Palindromes/readme.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# README
|
||||||
|
|
||||||
|
说起来,还是先找到所有回文数字更快,单纯计算查找确实稍慢。
|
||||||
|
另外,就是python3.13比haskell还要快一线……我也是囧了……
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**haskell**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
> ghc euler_36.hs
|
||||||
|
> ./euler_36.exe
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user