Project Euler 8 – Haskell

Project Euler 8 - Haskell

通过ProjectEuler,我将我的解决方案与这里的解决方案进行了比较。

对于问题8,我的代码生成正确的答案(通过网站上的校验和确认)23514624000。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module Main where

import Data.List

main = do
    print $ last (sort eulerEight)


eulerEight = doCalc [ x | x <- toDigits 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450]
    where
        doCalc (_:[]) = []
        doCalc (x:xs) = if 0 `notElem` take 13 (x:xs) && length (x:xs) > 12
                then product (take 13 (x:xs)) : doCalc xs
                else doCalc xs

toDigits n
 | n < 1 = []
 | otherwise = toDigits (n `div` 10) ++ [n `mod` 10]

我意识到这可能会更好,所以检查了这里的解决方案,它似乎不正确。

1
2
3
4
5
6
7
8
import Data.Char (digitToInt)
import Data.List

problem_8 = do
        str <- readFile"number.txt"
        -- This line just converts our str(ing) to a list of 1000 Ints
        let number = map digitToInt (concat $ lines str)
        print $ maximum $ map (product . take 13) (tails number)

根据项目Euler网站上的问题,我将take 5改为take 13,但是上面的代码产生了2091059712的错误答案。我已经检查了number.txt中的数字是否正确,并且两个示例中的数字都是1000位。有人能解释为什么输出是不同的吗?(我想可能是因为它使用了tails,而不是tail,但我不确定)


发生的情况是,digitToInt返回一个Int,在32位系统上,当5增加到13时,它太短,无法保存测试数字。改为(fromIntegral . digitToInt),工作正常。


该问题已被标识为int溢出,但wiki代码本身不正确。它没有正确地截断列表,可能会产生不正确的结果,这取决于输入数据(也就是说,它在这里幸运地产生了正确的结果)。

假设一个以0结尾的数字串,后跟12个9s。在计算最大值时,代码会错误地考虑9^12。更简单的是,对于1000个零,它将产生1作为答案。

由于压缩的特性,我们可以实现自动截断:

1
2
3
4
5
6
7
8
9
import Data.Char
import Data.List

euler_8 = do
   str <- readFile"numbers.txt"
   print . maximum . map product
         . foldr (zipWith (:)) (repeat [])   -- here
         . take 13 . tails . map (fromIntegral . digitToInt)
         . concat . lines $ str

虽然您的代码是正确的,但有一些问题:

  • [x | x <- xs]只是xs而已。
  • last (sort xs)只是maximum xs更快
  • 在递归调用的右侧附加是一个已知的低效率源。最好是在左侧附加,最后反转,但下面的转换更像是haskellian:
1
2
3
toDigits n xs  -- to be called as `toDigits n []`
 | n < 1 = xs
 | otherwise = toDigits (n `div` 10) ((n `mod` 10) : xs)