3
\$\begingroup\$

Project Euler #4 asks:

Find the largest palindrome made from the product of two 3-digit numbers.

The code is as follows:

module Main where
import Data.List (sortBy)
import Data.Ord (comparing)
palindromes :: [Integer]
palindromes = [calcPalindrome a b c | a <- [1..9], b <- [0..9], c <- [0..9]]
 where
 calcPalindrome a b c = 100001 * a + 10010 * b + 1100 * c
is3Digit :: Integer -> Bool
is3Digit n = n >= 100 && n <= 999
isDiv :: Integer -> Integer -> Bool
isDiv n d = n `mod` d == 0
-- returns all multiples of 2 3-digit numbers
-- for palindromes
isMultiple :: Integer -> Bool
isMultiple n = not $ null threeMultiples
 where
 multiples :: [(Integer, Integer)]
 multiples = do
 d <- [10..99]
 if n `isDiv` (d * 11)
 then return (d * 11, n `div` (d * 11))
 else []
 threeMultiples :: [(Integer, Integer)]
 threeMultiples = filter (\ (m, n) -> is3Digit m && is3Digit n) multiples
problem4 :: Integer -> Integer
problem4 n =
 head $ filter isMultiple $ sortBy (flip compare) $ filter (< n) palindromes
main :: IO ()
main = do
 _ <- getLine
 contents <- getContents
 let cases = map read $ lines contents
 let results = map problem4 cases
 mapM_ print results

I'd appreciate comments on isMultiple and problem4 as they seem rather messy. Any other general comments are welcome too.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Dec 23, 2014 at 18:10
\$\endgroup\$
0

2 Answers 2

4
\$\begingroup\$

Proof technique

I was pleasantly surprised at the speed of this algorithm (generating all 6-digit palindromes, then taking the largest one with two 3-digit factors), as compared to the alternative approach (multiplying all 3-digit pairs, then finding the largest product that is a palindrome). I would have expected the latter to be faster, as factoring is normally a more expensive operation — but you take advantage of a clever shortcut. That, I suppose, is part of the point of Project Euler questions.

On the other hand, I would consider the program to be slightly incomplete. The property that the palindrome is divisible by 11 only holds if it contains an even number of digits. As a counterexample, 101 ∙ 101 = 10201 is a number within the parameters of the question (a palindrome that is the product of two 3-digit numbers) that is not divisible by 11. Your palindromes function only generates 6-digit candidates for consideration. While there is no need for your program to produce a result that requires no human interpretation, it would be desirable to note the assumption (or proof) of the existence of a 6-digit result, at least as a comment.

Implementation

I am puzzled by your boilerplate main function. Reading an input number as an upper bound on the palindrome candidates seems superfluous. An excessively large input won't scale the problem to cover 4-digit ×ばつ 4-digit products, either, as your palindromes function is hard-coded to produce 6-digit candidates only. So, this would have sufficed:

problem4 :: Int 
problem4 = head $ filter isMultiple $ sortBy (flip compare) $ palindromes
main :: IO ()
main = do
 print problem4

Integer is overkill. The numbers involved fit very comfortably under maxBound::Int.

sortBy (flip compare) is just the same as reverse. But why reverse anything at all, when you could just generate the palindrome candidates in descending order in the first place?

palindromes :: [Int]
palindromes = [calcPalindrome a b c | a <- [9,8..1], b <- [9,8..0], c <- [9,8..0]]
 where
 calcPalindrome a b c = 100001 * a + 10010 * b + 1100 * c

is3Digit is fine, though I would find is3Digit n = 100 <= n && n < 1000 more aesthetically pleasing.

isMultiple could be better named, and the do ... return would be better written as a list comprehension. (See Do notation considered harmful.)

isProductOfThreeDigitNumbers :: Int -> Bool
isProductOfThreeDigitNumbers n = not $ null threeDigitFactors
 where
 -- All palindromes with an even number of digits are divisible by 11
 factors = [(d, n `div` d) | d <- [11, 22 .. 99 * 11], n `isDiv` d]
 threeDigitFactors = filter (\ (m, n) -> is3Digit m && is3Digit n) factors

Then, problem4 would just be

problem4 :: Int
problem4 =
 head $ filter isProductOfThreeDigitNumbers $ palindromes
answered Dec 24, 2014 at 4:25
\$\endgroup\$
2
  • \$\begingroup\$ Thanks for the comments! I'll follow the changes you recommended. \$\endgroup\$ Commented Dec 24, 2014 at 6:22
  • \$\begingroup\$ Nice. I went for the other approach. I'm wondering how much faster this is. Maybe I'm wrong but I would have thought the less efficient approach reflects the level of difficulty of Euler # 4.... \$\endgroup\$ Commented Aug 27, 2015 at 8:01
1
\$\begingroup\$

I went for simplicity. Here's my attempt

import Data.List
isPalindromic::Int -> Bool
isPalindromic n = (==) n' (reverse n')
 where n' = show n
euler4::Int
euler4 = maximum [p | x <- [999,998..100], y <- [999,998..100], let p = x * y, isPalindromic p]

As stated by the previous reviewer not as efficient as yours but in my opinion clearer and the right level of complexity :-)

answered Aug 27, 2015 at 8:05
\$\endgroup\$
2
  • 2
    \$\begingroup\$ There is no 'right' level of complexity for Euler, IMO. All questions can be as over-engineered as you want. One prefers, scalability, others prefer speed while a third group would want to keep the complexity down. All are fine. It's Euler after all: practice what YOU want to practice. \$\endgroup\$ Commented Aug 27, 2015 at 8:15
  • 1
    \$\begingroup\$ Usually, I'd write a working program with as little complexity as possible, then try to optimize for time, since writing simple programs gets boring (and unsustainable in later Euler problems). :) \$\endgroup\$ Commented Sep 6, 2015 at 14:04

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.