Próbuję napisać rozwiązanie o silnej sile do Project Euler Problem #145 i nie mogę uruchomić rozwiązania w czasie krótszym niż około 1 minuta 30 sekund.Jak zoptymalizować pętlę, która może być w pełni ścisła?
(Jestem świadomy, że istnieją różne skróty, a nawet rozwiązania papier-ołówek, dla celów tego pytania nie biorę pod uwagę tych).
W najlepszej wersji, jaką do tej pory wymyśliłem, profilowanie pokazuje, że większość czasu spędza się w foldDigits
. Ta funkcja wcale nie musi być leniwą, a moim zdaniem powinna być zoptymalizowana do prostej pętli. Jak widać, starałem się, aby różne fragmenty programu były surowe.
Moje pytanie brzmi: bez zmiany ogólnego algorytmu, czy jest jakiś sposób na skrócenie czasu wykonania tego programu do znaku poniżej minuty?
(A jeśli nie, to czy jest jakiś sposób, aby zobaczyć, że kodeks foldDigits
jest jak zoptymalizowane, jak to możliwe?)
-- ghc -O3 -threaded Euler-145.hs && Euler-145.exe +RTS -N4
{-# LANGUAGE BangPatterns #-}
import Control.Parallel.Strategies
foldDigits :: (a -> Int -> a) -> a -> Int -> a
foldDigits f !acc !n
| n < 10 = i
| otherwise = foldDigits f i d
where (d, m) = n `quotRem` 10
!i = f acc m
reverseNumber :: Int -> Int
reverseNumber !n
= foldDigits accumulate 0 n
where accumulate !v !d = v * 10 + d
allDigitsOdd :: Int -> Bool
allDigitsOdd n
= foldDigits andOdd True n
where andOdd !a d = a && isOdd d
isOdd !x = x `rem` 2 /= 0
isReversible :: Int -> Bool
isReversible n
= notDivisibleByTen n && allDigitsOdd (n + rn)
where rn = reverseNumber n
notDivisibleByTen !x = x `rem` 10 /= 0
countRange acc start end
| start > end = acc
| otherwise = countRange (acc + v) (start + 1) end
where v = if isReversible start then 1 else 0
main
= print $ sum $ parMap rseq cr ranges
where max = 1000000000
qmax = max `div` 4
ranges = [(1, qmax), (qmax, qmax * 2), (qmax * 2, qmax * 3), (qmax * 3, max)]
cr (s, e) = countRange 0 s e
Ile rdzeni używasz? – ErikR
to Core-i5-760, czyli cztery rdzenie. Wiem, że sztywne kodowanie zakresów w aplikacji jest trochę nieprzyjemne, ale sprawiło, że paralelizm był nieco jaśniejszy. – stusmith