summer_tree_home

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

Convex Hull (Ice Base) - 凸型ポリゴン

来週から、1ヶ月ほど海外に行くので、出発までになんとか全問クリアしたかったのだが、ちょっと厳しいかなぁ・・・。

どんな問題?

Convex Hull
http://www.checkio.org/mission/convex-hull/

与えられた点からなる凸型ポリゴンを取得せよ。
最も左側にある一番下の点を開始点として、ポリゴンの頂点を時計回りに求める。

引数は、点の座標が [x,y]形式のリストとして与えられる。点の数は1以上10未満。座標の値は 0<x,y<10 の範囲の整数値となる。
戻り値は、頂点のインデックスをリストとして返す。

例題:

checkio([[7, 6], [8, 4], [7, 2], [3, 2], [1, 6], [1, 8], [4, 9]]) == [4, 5, 6, 0, 1, 2, 3]
checkio([[3, 8], [1, 6], [6, 2], [7, 6], [5, 5], [8, 4], [6, 8]]) == [1, 0, 6, 3, 5, 2]

どうやって解く?

なんか公式がありそうだが、まずは何も見ずに解いてみよう。
Wikipediaに「輪ゴムを掛けたようなもの」という表現があったが、外側から囲むようなイメージで考えれば解けそうだ。

最初の点 P1 は、最も左側にある一番下の点となる。
P2 は、P1より右側にあるわけだから、P1の真下に仮のP0を作って、P0-P1-P2の角度が最大になる(つまり線分P0-P1と線分P1-P2の角度が最小になる)ような点 P2 を探せばいい。

最初の点 P1 と次の点 P2 が確定すれば、同じようにして、P1-P2-P3の角度が最大になる(つまり線分P1-P2と線分P2-P3の角度が最小になる)ような点 P3 を探せばいい。
P4以後はその繰り返し。

角度を求めるには、math.atan2を使えばいいようだ。

def get_angle(pt1, pt2):
    return atan2(pt2[1] - pt1[1], pt2[0] - pt1[0])

開始点から、他の点までの角度を計算し、角度の差が最小のものが次の点となる。
ただし、角度が同じ場合もあるので、そのときは、近い点を優先するようにした。
また、引数の点の数は1個以上なので、1個のときは[0]を返すようにしている。
あれこれ追加して、最終的にこうなった。

まとめ

from math import atan2, hypot, pi
 
def get_angle(pt1, pt2):
    # ベクトル(pt1→pt2)とX軸との角度
    return atan2(pt2[1] - pt1[1], pt2[0] - pt1[0])
 
def get_distance(pt1, pt2):
    # pt1とpt2の距離
    return hypot(pt2[0] - pt1[0], pt2[1] - pt1[0])
 
def checkio(data):
    if len(data) == 1:
        return [0]
    point = min([p for p in data if p[0] == min(data)[0]])  # 一番左の下が開始点
    angle = pi / 2  # 最初の角度は90度とする
    result = [data.index(point)]
    while True:
        # pointから他点への角度を計算
        pts_data = [(i, p, get_angle(point, p)) for i, p in enumerate(data) if p != point]
        # 同じ角度なら距離が近い方を優先するためソートしておく
        pts_data.sort(key=lambda d: get_distance(point, d[1]))
        # 前の角度との差が最小になる点が次の点
        index, point, angle = min(pts_data, key=lambda d: (angle - d[2]) % (pi * 2))
        # すでにresultに含まれている点ならポリゴン完成
        if index in result:
            return result
        result.append(index)

http://www.checkio.org/mission/convex-hull/publications/natsuki/python-3/first/

なんか、ごちゃごちゃしてて見づらくなってしまった。