Проект Эйлер 8 - Haskell

Проходя Project Euler, я сравниваю свои решения с теми, что здесь.

На вопрос 8 мой код выдает правильный ответ (подтвержденный контрольной суммой на сайте) 23514624000.

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]

Я понял, что это может быть намного лучше, поэтому проверил решение здесь, и оно не кажется правильным.

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)

Я изменил значение take 5 на take 13 согласно вопросу на сайте Project Euler, однако приведенный выше код дает неправильный ответ 2091059712. Я проверил число в number.txt правильно, и что оно имеет полные 1000 цифр для обоих примеров. Кто-нибудь может пролить свет на то, почему выходы разные? (я думаю, может быть, связано с тем, что он использует tails и не tail, но я не уверен)

2 ответа

Решение

Что происходит, так это digitToInt возвращает Int, который в 32-битных системах слишком короткий, чтобы содержать тестовые числа, когда 5 увеличивается до 13. Измените его на (fromIntegral . digitToInt) и это работает правильно.

Проблема уже была идентифицирована как переполнение Int, но сам код вики неверен. Он не усекает список должным образом и может привести к неверному результату, в зависимости от входных данных (т. Е. Он дает правильный результат здесь по счастливой случайности).

Вообразите цепочку цифр, которая заканчивается 0 затем 12 9s. Код возьму 9^12 учитывается неверно при расчете максимального значения. Еще проще, для 1000 нулей это даст 1 в качестве ответа.

Мы можем добиться автоматического усечения, благодаря свойствам архивирования:

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, который быстрее
  • добавление справа от рекурсивного вызова является известным источником неэффективности. лучше даже добавить слева и повернуть в конце, но следующая трансформация более хаскеллова:
toDigits n xs  -- to be called as `toDigits n []`
 | n < 1 = xs
 | otherwise = toDigits (n `div` 10) ((n `mod` 10) : xs)
Другие вопросы по тегам