Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonでサービスセンターの最適な設置場所を見つけるプログラム(三分探索)


複数の家の座標点を含むリストが与えられたとします。(xc, yc) の位置にサービスセンターを設置するとき、すべての点から (xc, yc) までのユークリッド距離の合計が最小になるようにしたいと考えます。つまり、この問題では最小となる距離の合計を求める必要があります。

たとえば、入力が positions = [(10,11),(11,10),(11,12),(12,11)] の場合、出力は 4.0 になります。

Pythonでサービスセンターの最適な設置場所を見つけるプログラム(三分探索)

解決のアプローチ:三分探索(Ternary Search)

この問題は三分探索を用いて効率的に解くことができます。「全点とのユークリッド距離の合計」という目的関数は下に凸な関数であるため、x 座標と y 座標のそれぞれに対して三分探索をネストして適用することで、最適な位置を段階的に絞り込めます。

具体的な手順は以下の通りです。

  • 反復回数 numIter := 50 と設定する

  • 関数 total(cx, cy, positions) を定義する:各点 (x, y) と (cx, cy) のユークリッド距離を合計して返す補助関数です

  • 関数 fy(x, positions) を定義する:x を固定した状態で、y 方向に三分探索を行い、その x における距離合計の最小値を求めます

    • 探索範囲を l := 0、r := 101 と初期化

    • numIter 回にわたり、区間を三等分する2点 y1、y2 での値 t1、t2 を比較

    • t1 < t2 なら最小値は左側にあるので r := y2、そうでなければ l := y1 として範囲を狭める

    • t1 と t2 の小さい方を res として記録し、最後に返す

  • 関数 fx(positions) を定義する:今度は x 方向に三分探索を行い、各区切り点 x1、x2 に対して fy() を呼び出すことで、全体の最小値を求めます

メイン処理では fx(positions) を返すだけで完成です。

実装例

以下のコードで実際の動作を確認してみましょう。

from math import sqrt

def solve(points):
    numIter = 50

    def total(cx, cy, positions):
        total = 0.0
        for p in positions:
            x, y = p
            total += sqrt((cx - x) * (cx - x) + (cy - y) * (cy - y))
        return total

    def fy(x, positions):
        l, r = 0, 101
        res = 0
        for i in range(numIter):
            y1 = l + (r - l) / 3
            y2 = r - (r - l) / 3
            t1 = total(x, y1, positions)
            t2 = total(x, y2, positions)
            res = min(t1, t2)
            if t1 < t2:
                r = y2
            else:
                l = y1
        return res

    def fx(positions):
        l, r = 0, 101
        res = 0
        for i in range(numIter):
            x1 = l + (r - l) / 3
            x2 = r - (r - l) / 3
            t1 = fy(x1, positions)
            t2 = fy(x2, positions)
            res = min(t1, t2)
            if t1 < t2:
                r = x2
            else:
                l = x1
        return res

    return fx(positions)

positions = [(10,11),(11,10),(11,12),(12,11)]
print(solve(positions))

入力

[(10,11),(11,10),(11,12),(12,11)]

出力

4.0

まとめ

この手法では、x 方向・y 方向それぞれに50回ずつの三分探索を適用するため、座標の範囲が固定されていれば反復回数のみで精度が決まり、非常に安定した結果が得られます。幾何学的な最適化問題において、凸性を利用した三分探索は強力なテクニックなので、ぜひ覚えておきましょう。

  1. 【Python】ある数の最大の素因数を求めるプログラムの書き方

    この記事では、「与えられた整数の最大の素因数を求める」という問題に対する解決方法を、具体的なコード例とともにわかりやすく解説します。 問題文 正の整数 n が与えられたとき、その数の最大の素因数を求めます。 例えば n = 15 の場合、15 は 3 × 5 と素因数分解できるため、答えは 5 となります。 解き方のアプローチ 入力された数を、小さい約数から順番に割っていくことで素因数分解します。 割り切れるたびに、その時点での約数(素因数)を「最大値」として更新していきます。 平方根まで調べれば十分なため、計算量を抑えられます。 実装例(サンプルコード) import math def

  2. Pythonで円柱の周囲長を求めるプログラムの書き方

    この記事では、以下の問題をPythonを使って解く方法を解説します。 問題の定義 問題: 直径と高さを入力として受け取り、円柱の周囲長を求める。 ここでいう「周囲長」とは、円柱を横から見たときに現れる長方形の外周のことです。つまり、円柱の側面を展開すると長方形になり、その縦が円柱の高さ、横が円の直径(円周ではありません)に相当します。 したがって、周囲長は次の式で表せます。 周囲長 = 2 × ( 高さ h + 直径 d ) d:円柱の直径 h:円柱の高さ 実装例 それでは、実際のコードを見てみましょう。 # 円柱の周囲長を計算する関数 def perimeter(diameter, he