summer_tree_home

Check iOでPython3をマスターするぜっ

The Angles of a Triangle (Scientific Expedition) - 三角形の3辺から角度を計算

どんな問題?

The Angles of a Triangle
http://www.checkio.org/mission/triangle-angles/

三角形の3辺の長さから3つの角度を求めよ。

引数は、3辺の長さ。
戻り値は、3つの角度(単位は度)を整数値に丸めて、小さい順にソートしてリストで返す。
計算できない場合は、[0,0,0] を返す。

例題:

checkio(4, 4, 4) == [60, 60, 60]
checkio(3, 4, 5) == [37, 53, 90]
checkio(2, 2, 5) == [0, 0, 0]

どうやって解く?

いや、だからこれはPythonじゃなくて数学の問題じゃねーか。
なんかこういう定理があった気がするなぁ。またもWikipedia先生の登場だ。

f:id:summer_tree:20140318110022p:plain
余弦定理(Wikipedia)

式が分かれば、あとは簡単。

import math

def get_angle(a, b, c):
    return round(math.degrees(math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b))))

def checkio(a, b, c):
    return sorted(get_angle(*abc) for abc in ((a, b, c), (b, c, a), (c, a, b)))

これで実行。

ValueError: math domain error

あれ、エラー。そうか、計算できない場合は[0,0,0]を返すって書いてあったな。
get_angle()に例外処理を追加。

def get_angle(a, b, c):
    try:
        return round(math.degrees(math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b))))
    except ValueError:
        return 0

これでよし。Run&Check!

Your result: [0,0,180]
Right result: [0,0,0]
Fail: checkio(10,20,30)

あれ、またエラー。3辺の長さが10,20,30ということは、1直線上にある場合か。この場合はValueError例外は出ないのか。

じゃあ、角度に0が含まれているかチェックするようにしよう。

def checkio(a, b, c):
    result = sorted(get_angle(*abc) for abc in ((a, b, c), (b, c, a), (c, a, b)))
    return [0, 0, 0] if 0 in result else result

これでOK

でも、なんか、全体に汚いなぁ。degreeに直すとか、数字を丸めるのは、get_angle()の中でやることではないよな。get_angle()の汎用性を損なっている。
全体に書き直してみた。

まとめ

import math

def get_angle_from_sides(a, b, c):
    return math.acos((a ** 2 + b ** 2 - c ** 2) / (2 * a * b))

def checkio(a, b, c):
    try:
        angles = [get_angle_from_sides(*abc) for abc in ((a, b, c), (b, c, a), (c, a, b))]
        result = sorted(round(math.degrees(a)) for a in angles)
        return [0, 0, 0] if 0 in result else result
    except ValueError:
        return [0, 0, 0]

これで公開しておいた。
http://www.checkio.org/mission/triangle-angles/publications/natsuki/python-3/first/

他の人の答え

最初に辺の長さをチェックして、三角形にならない場合は[0,0,0]を返すようにしている人も多かった。

if (a + b <= c) or (a + c <= b) or (b + c <= a):
    return [0, 0, 0]

最初にチェックしておけば、例外処理は不要になるわけか。