With F#, there are two ways to solve this: the easy way, and the slightly easier way. The first way, we’ll work entirely with integers, including an implementation of gcd using the Euclid’s algorithm for reducing our final fraction.
gcd and accompanying abs are a sure candidates for inclusion in our Numerics module.
let inline abs (g:G<'a>) n =
if n < g.zero then n*g.negone else n
let inline gcd (g:G<'a>) n m =
if n = g.zero || m = g.zero then
raise (System.ArgumentOutOfRangeException("n and m must non-zero"))
else
let rec gcd n m =
if n = m then n
else
if n > m then gcd (n-m) m
else gcd n (m-n)
gcd (abs g n) (abs g m)
And then our solution is fairly straightforward:
let problem33a =
let unorthodoxPairs =
[for numer = 10 to 99 do
for denom = (numer+1) to 99 do
let reduced = (numer |> float)/(denom |> float)
let digitPair n = //decompose digit list of n into a pair
let nDigits = n |> string |> Seq.map (string >> float) |> Seq.toArray
(nDigits.[0], nDigits.[1])
let (a,b) = digitPair numer
let (c,d) = digitPair denom
let isUnorthodox w x y z = (w/x) = 1. && (y/z) = reduced
if isUnorthodox a c b d ||
isUnorthodox a d b c ||
isUnorthodox b d a c ||
isUnorthodox b c a d then yield (numer, denom)]
let product = unorthodoxPairs |> List.fold (fun (w,x) (y,z) -> (w*y,x*z)) (1,1)
snd product / (gcd (fst product) (snd product))
Next is our very easy solution which takes advantage of BigRational included in the F# PowerPack:
let problem33b =
let unorthodoxPairs =
[for numer = 10 to 99 do
for denom = (numer+1) to 99 do
let reduced = (numer |> float)/(denom |> float)
let digitPair n = //decompose digit list of n into a pair
let nDigits = n |> string |> Seq.map (string >> float) |> Seq.toArray
(nDigits.[0], nDigits.[1])
let (a,b) = digitPair numer
let (c,d) = digitPair denom
let isUnorthodox w x y z = (w/x) = 1. && (y/z) = reduced
if isUnorthodox a c b d ||
isUnorthodox a d b c ||
isUnorthodox b d a c ||
isUnorthodox b c a d then yield BigRational.Parse(sprintf "%d/%d" numer denom)]
let product = unorthodoxPairs |> List.fold (*) 1N
product.Denominator
Here’s a third version which avoids string parsing and the need for conversions between int and float:
let problem33c =
let unorthodoxPairs =
[for numer = 10. to 99. do
for denom = (numer+1.) to 99. do
let digitPair n =
let tensPlace = System.Math.Truncate(n/10.)
(tensPlace, n-tensPlace*10.)
let (a,b) = digitPair numer
let (c,d) = digitPair denom
let isUnorthodox w x y z = (w/x) = 1. && (y/z) = numer/denom
if isUnorthodox a c b d ||
isUnorthodox a d b c ||
isUnorthodox b d a c ||
isUnorthodox b c a d then yield BigRational.Parse(sprintf "%.0f/%.0f" numer denom)]
let product = unorthodoxPairs |> List.fold (*) 1N
product.Denominator