Project Euler 9

http://projecteuler.net/index.php?section=problems&id=9


この手の直角三角形の問題はピタゴラス数の生成式を使うのが定番です。

a = l(m2 - n2) b = 2lmn c = l(m2 + n2)
a + b + c = 2lmn'
n' = m + n
m > n
mnは互いに素

500を3つの積で表してそれが条件に合うか調べればよいです。それには500を素因数分解して、約数を出す処理を2回繰り返します。これらはがんばれば書けます。
さて、タプルについてF#ではこういうシンタックスも可能だったんですね。

let a, b = (1, 2)
printfn "%d %d" a b  // 1 2

これでもOKです。

let c, d = 3, 4
printfn "%d %d" c d  // 3 4

こういうこともできます。

for x, y in [ (1, 2); (3, 4) ] do
   printfn "%d %d" x y
let rec calc_exp n p =
   if n % p = 0 then
      let t = calc_exp (n / p) p
      (fst t + 1, snd t)
   else
      (0, n)

let rec fac n p =
   if n = 1 then
      []
   else if p * p > n then
      [(n, 1)]
   else
      let t = calc_exp n p
      if fst t > 0 then
         (p, fst t) :: (fac (snd t) (p + 1))
      else
         fac n (p + 1)

let factorize n = fac n 2

let rec pow n e = if e = 0 then 1 else
                  let m = pow n (e / 2)
                  if e % 2 = 1 then m * m * n else m * m

let rec value_f f = List.fold (fun x y -> x * (pow (fst y) (snd y))) 1 f

let rec divisors = function
      | [] -> [([], [])]
      | (p,e)::fs -> 
            [ for t in (divisors fs) do
                  for e' in [0..e]
                     -> if e' = 0 then (fst t, (p, e)::(snd t))
                        else if e' = e then ((p, e)::(fst t), snd t)
                        else ((p, e')::(fst t), (p, e - e')::(snd t)) ]

let divs n =
      let f = factorize n
      [ for f1, f2 in divisors f do
            for f3, f4 in divisors f2
                  -> (value_f f1, value_f f3, value_f f4) ]

let rec gcd m n = if m % n = 0 then n else gcd n (m % n)

let is_valid t =
      let l, m, n = t
      m < n && n < m * 2 && n % 2 = 1 && gcd m n = 1

let product t =
      let l, m, n' = t
      let n = n' - m
      2 * l * l * l * m * n * (m * m * m * m - n * n * n * n)

let N = 1000
printfn "%A" (List.map product (List.filter is_valid (divs (N / 2))))