First we implement a function for determining whether a number is palindromic. We use an efficient tail recursive algorithm comparing the string converted number by character indices working from either end of the string until we reach the middle.
let isPalindrome n = let rec isPalindrome (nstr:string) i j = if i > j then true elif nstr.[i] <> nstr.[j] then false else isPalindrome nstr (i+1) (j-1) let nstr = n |> string isPalindrome nstr 0 (nstr.Length - 1)
Now we just need to enumerate all products made from two 3-digit (100 through 999) numbers, taking the max of those which are palindromic. Solution (a) uses an imperative approach justifiable in nested loop scenarios while (b) and (c) use sequence expressions and typical function applications.
//fastest, reasonable use of iterative loop + mutable let problem4a = let mutable max = 0 for i in 100..999 do for j in 100..999 do let p = i * j if p > max && p |> isPalindrome then max <- p max //notably slower since has to process isPalindrome for every possibility let problem4b = seq { for i in 100..999 do for j in 100..999 do yield i * j } |> Seq.filter isPalindrome |> Seq.max //still lower than 4a let problem4c = seq { for i in 100..999 do for j in 100..999 do yield i * j } |> Seq.fold (fun acc i -> if i > acc && i |> isPalindrome then i else acc) 0
While (b) is the clearest, I like solution (a) the best since it’s clear enough and notably faster than either (b) or (c).
Good use of a mutable! It's certainly a lot faster than building up a list of the possible answers and getting the maximum at the end.
ReplyDeleteCouple of improvements could be made though, potentially halving the CPU time.
(i) Run the j loop from 100 to i, not 999. That way you avoid checking the same product twice, e.g. 100*101 and 101*100.
(ii) Before checking that p is in fact palindromic, first check that p is greater than 99,999, i.e. that it contains six figures, as it's fair to assume that there'll be at least one six-digit palindromic product. In fact there are 489! As to the five-digit ones, there are 750 of these, so they're worth dispensing with early.