Life Goes On

まあまあだけど楽しんでる方です

86問目

http://projecteuler.net/index.php?section=problems&id=86
6 × 5 × 3 の部屋を隅から反対の隅まで壁伝いに移動するとき、最短距離は 10 で整数となる。m × m × m 以下の大きさの部屋について、最短距離が整数となる部屋の数を考えると、M=100 のとき 2060 となり、初めて 2000 を超える。
この数が初めて百万を超えるのは M がいくつのときか求める。
ピタゴラス数(a, b, c)を生成して、条件(a ≦ m, b ≦ 2a)を満たすものを抽出していきます。

import Data.List

main = print euler086

euler086 :: Integer
euler086 = fst $ head $ dropWhile ((< 1000000) . snd) $ zip sizes paths
    where ts = rightAngles 10000
          sizes = map (fst . head) ts
          paths = scanl1 (+) $ map (sum . map integerPath) ts

integerPath :: (Integer, Integer) -> Integer
integerPath (a, b) = (div b 2) + (min (a-b+1) 0)

rightAngles :: Integer -> [[(Integer, Integer)]]
rightAngles m = groupBy (\ a b -> fst a == fst b) $ sort $ concat $
    map (\ (a, b) -> [ (a*n, b*n) | n <- [1..(div m a)] ]) $
    filter (\ (a, b) -> a <= m && b <= 2*a) $
    concat [ [(x^2-y^2, 2*x*y), (2*x*y, x^2-y^2)] |
    x <- [1..m], y <- [1..min (div m x) (x-1)], gcd x y == 1, mod x 2 /= mod y 2]