プロジェクトオイラー
http://projecteuler.net/index.php
Q91.
原点と0≤x,y≤50に2点とって、直角三角形を作る。いくつ作れるか。
素直に点を打って、面積が正とある角が直角を条件に数え上げればよい。
from itertools import productdef is_positive_area(x1, y1, x2, y2):
return x1 * y2 - x2 * y1 > 0def is_right_angle(x1, y1, x2, y2):
if x1 * x2 + y1 * y2 == 0:
return True
if x1 * (x2 - x1) + y1 * (y2 - y1) == 0:
return True
if x2 * (x2 - x1) + y2 * (y2 - y1) == 0:
return True
return FalseN = 50
counter = 0
for x1, y1, x2, y2 in product(xrange(N + 1), repeat = 4):
if is_positive_area(x1, y1, x2, y2):
if is_right_angle(x1, y1, x2, y2):
counter += 1
print counter
しかし、これだとO(N4)になってしまう。
原点をはさむ角が直角である場合はN2通り、原点から左回りに考えて、次の1点を決めてそこから直角に曲がる場合を数えて、それと対称形状が必ずあるから2倍する。これだと、O(N2)で済む(最大公約数の計算が入るから厳密には違うが)。
from itertools import product
import fractionsdef calc(x1, y1):
d = fractions.gcd(x1, y1)
dx = -y1 / d
dy = x1 / d
if dx == 0:
return N
else:
return min(-x1 / dx, (N - y1) / dy)N = 50
counter = N * N
for x1, y1 in product(xrange(1, N + 1), xrange(N)):
counter += calc(x1, y1) * 2
print counter