Problem 91
点P(x1, y1)とQ(x1, y2)が格子点上にあり、これらに原点O(0, 0)を加えて△OPQを形成する。
どの座標も0と2の間にある場合、14個の直角三角形がある(中略)。
0 ≤ x_1, y_1, x_2, y_2 ≤ 50のとき、直角三角形はいくつあるか。
http://projecteuler.net/index.php?section=problems&id=91
L=50として、大きさLの正方形内のPとQを単純に生成し(ただし、PはOQの右下)、直角三角形か判定するだけで答えは出ます(Code1)。しかし、これではあまりに雑ですね。計算量もO(L4)です。もうちょっとなんとかしましょう。
Oが直角の三角形はL2個あります。ここから、Pが直角だとして考えます。Pが軸上にあるとき、三角形は2L2個あります。そうでないとき、例えばP(4, 2)とすると、OPに垂直な方向は(1, -2)なので、この方向の格子点にQを取れます。左上には4/1 = 4個、右下には2/2 = 1個取れます。このようにすれば計算量がO(L2logL)(?)になって劇的に速くなります。
もうちょっと速くなると思うのですが。
# Code1 from itertools import ifilter, product def is_right_triangle(P, Q): if P[1] == 0 and Q[0] == 0: return True elif P[0] * (Q[0] - P[0]) == P[1] * (P[1] - Q[1]): return True elif Q[0] * (P[0] - Q[0]) == Q[1] * (Q[1] - P[1]): return True return False def is_right_lower(P, Q): return P[0] * Q[1] - P[1] * Q[0] > 0 L = 50 counter = 0 for P in product(range(L + 1), repeat = 2): for Q in product(range(L + 1), repeat = 2): if is_right_lower(P, Q) and is_right_triangle(P, Q): counter += 1 print counter
# Code2 from itertools import ifilter, product from fractions import gcd L = 1000 counter = L * L * 3 for x, y in product(range(1, L + 1), repeat = 2): d = gcd(x, y) dx = y / d dy = x / d counter += min((L - x) / dx, y / dy) + min(x / dx, (L - y) / dy) print counter