summer_tree_home

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

Ore In The Desert (Scientific Expedition) - 砂漠の中の鉱石

どんな問題?

Ore In The Desert
http://www.checkio.org/mission/ore-in-the-desert/

砂漠の中から、鉱石を探し出せ。
探査機を指定の座標に送ると、鉱石までの距離(整数値)がわかる。
4機の探査機を使って、鉱石の座標を見つけよ。

砂漠は10x10マスで座標は [0,0]~[9,9] となる。[y,x] 形式なので注意。
例えば、鉱石が [2,6] にあるとき、探査機を [5,3] に送ると、2点の距離は 4.24... なので、丸めた 4 が得られる。これを4回繰り返して、鉱石の座標を見つければよい。

今回は、checkio()関数が繰り返し呼ばれるタイプの問題。
引数は、今までの探査機の座標と、鉱石までの距離の履歴が list of list で渡される。探査機の座標が [5,3] で距離 4 なら、[5,3,4] となる。
戻り値は、次の探査機の座標を [y,x] 形式で返す。[y,x]が鉱石の座標であれば終了。

例題:

checkio([])
checkio([[5, 3, 4]])
checkio([[5, 3, 4], [2, 9, 3]])

どうやって解く?

前に苦労した、Bulls and Cowsと似ている。全部で10x10=100マスしかないので、前よりも簡単かな。

まず、2点間の距離を計算する関数を作る。

    def get_dist(r, c, ore):
        return round(((r - ore[0]) ** 2 + (c - ore[1]) ** 2) ** 0.5)

あらかじめエディタに記載されているセルフチェック用の関数 check_solution() のものを使った。
平方根(ルート)を求めるのに、math.sqrt() を使わず、 ** 0.5 にしてあるあたりが面白い。

後はBulls and Cowsと同じように、全100マスの中から条件に一致するマスを候補として残していく。

    candidates = [(row, col) for row in range(10) for col in range(10)]
    for row, col, dist in previous:
        candidates = [ore for ore in candidates if get_dist(row, col, ore) == dist]
    return candidates[0]

これで本当に4ステップ以内に正解が見つかるか、確認してみた。
step数を集計するために書いたコードはこちら。

import collections

MAX_STEP = 10

def test():
    def get_step(ore):
        recent_data = []
        for step in range(1, MAX_STEP):
            row, col = checkio([d[:] for d in recent_data])
            if not (0 <= row < 10 and 0 <= col < 10):
                raise IndexError
            if (row, col) == ore:
                return step
            dist = round(((row - ore[0]) ** 2 + (col - ore[1]) ** 2) ** 0.5)
            recent_data.append([row, col, dist])
        return -1

    ores = [(row, col) for row in range(10) for col in range(10)]
    steps = collections.defaultdict(int)
    for ore in ores:
        step = get_step(ore)
        steps[step] += 1

    print('|*step|*count|')
    for step in sorted(steps):
        print('|{}|{}|'.format(step, steps[step]))

結果はこちら。

step count
1 1
2 13
3 69
4 17

大丈夫みたいだ。

まとめ

def checkio(previous):
    def get_dist(r, c, ore):
        return round(((r - ore[0]) ** 2 + (c - ore[1]) ** 2) ** 0.5)
 
    if not previous:  # previous == []
        return [0, 0]
 
    candidates = [(row, col) for row in range(10) for col in range(10)]
    for row, col, dist in previous:
        candidates = [ore for ore in candidates if get_dist(row, col, ore) == dist]
    return candidates[0]

http://www.checkio.org/mission/ore-in-the-desert/publications/natsuki/python-3/first/
公開しておく。

他の人の答え

あ、しまった。ここいらんかった。

    if not previous:
        return [0, 0]

ちょっと気になったのがこれ。

if round(hypot(x - i, y - j), 0) == d

http://www.checkio.org/mission/ore-in-the-desert/publications/bryukh/python-3/second/

hypotってなんだろ?

math.hypot(x, y)
ユークリッドノルム(sqrt(x*x + y*y))を返します。これは原点から点 (x, y) のベクトルの長さです。

ほほう。