実現したいこと
数理最適化の一つである最大被覆問題において,198個の経緯度情報(X座標,Y座標)を基にしたポ集合(点)をカバー(円に含む)する入力した値の数だけの集合とそれを中心とした円(需要範囲)が描画される実行結果を導出したい.
発生している問題・分からないこと
経緯度情報を持つ198個の点と入力した値の数だけの点とそれを中心とした円は描画される状態にあるが、ただ入力した値の数だけの点がランダムに配置されてしまう。全ての集合をカバーするような実行結果を算出するためにどうすればいいか知りたい。
該当のソースコード
Python
1import pandas as pd 2import numpy as np 3import matplotlib.pyplot as plt 4import matplotlib.patches as patches 5import random, math, time 6import pulp 7 8 9#1.入力データ生成:住居の設置点を読み込む 10def make_data_ad(): 11 #1-1.住居の位置情報を格納したcsvファイルを読み込む 12 df = pd.read_csv("X_Y_data(rep_housing).csv", encoding="utf-8") 13 #1-2.変数[X]に設置する住居のX座標値(経度)を代入 14 X = df["X_CODE"] 15 #1-3.変数[Y]に設置する住居のY座標値(緯度)を代入 16 Y = df["Y_CODE"] 17 #1-4.変数[X_ad]・[Y_ad],を値として返す 18 return X,Y 19 20#2.入力データ生成:資源回収物拠点の設置可能点を生成 21def make_data_si(sigen, upper_x, upper_y): 22 random.seed(10) 23 #2-1.変数[si_X]に資源回収物拠点の設置可能点を代入 24 si_X = [random.uniform(137.689, upper_x) for i in range(sigen)] 25 #2-2.変数[si_X]に資源回収物拠点の設置可能点を代入 26 si_Y =[random.uniform(34.682, upper_y) for i in range(sigen)] 27 #2-3.[si_X]・[si_Y]を値として返す 28 return [si_X, si_Y] 29 30#3.住居と資源回収物拠点間の直線距離を測定する 31def distance(si): 32 ad1,ad2 = make_data_ad() 33 for X_ad, Y_ad in zip(ad1, ad2): 34 unit_ad = [X_ad, Y_ad] 35 #3-1.変数[dx]に住居のX座標値から資源回収物拠点のX座標値を引いたx軸方向の距離を代入 36 dx = unit_ad[0] - si[0] 37 #3-2.変数[dy]に住居のY座標値から資源回収物拠点のY座標値を引いたy軸方向の距離を代入 38 dy = unit_ad[1] - si[1] 39 #3-3.x軸方向の距離の二乗とy軸方向の距離の二乗の和の平方根を求める 40 return math.sqrt(dx **2 + dy **2) 41 42#4.需要変数(二値変数)行列を生成 43def make_matrix(ps, r): 44 #5.許容領域内に分析対象(住居)が収まっているかを判断する 45 def check(a): 46 #5-1.「もし施設間の距離が許容領域の半径以下なら,要素(資源回収物拠点)を1に、それ以外は0にする」 47 if a <= r: 48 return 1 49 else: 50 return 0 51 #4-1.フィールド上にプロットするためにn次正方行列に整形する用のリスト[unit_list]を作成 52 unit_list = [] 53 #4-2.変数[n,m]に[X]と[Y]の座標値を一つにまとめたものを代入する 54 for n, m in zip(ps[0], ps[1]): 55 #4-3.変数[unit]に変数[n,m]を代入 56 unit = (n, m) 57 #4-4.変数[unit_list]に[unit]を追加 58 unit_list.append(unit) 59 #4‐5.[unit_list]から変数p1,p2にそれぞれX・Y座標値を代入し、変数[distance(p1, p2)]に入っている距離が許容領域の半径に収まるかどうか判断する 60 return [check(distance(p1)) for p1 in unit_list] 61 62#6.グラフ上に結果を描写する 63def draw(ps, fs, r): 64 #6-0.make_data_adを実行 65 ad1,ad2 = make_data_ad() 66 #6-1.変数[fig]にx軸、y軸における描画領域の大きさを代入する 67 plt.figure(figsize=(5, 5)) 68 #6-2.変数[ax]にグラフの設定を保存するための枠を作成 69 ax = plt.axes() 70 #6-3.変数[x]にグラフ軸の数値幅と増幅単位の設定を保存 71 x = np.arange(137.69, 137.76, 0.1) 72 #6-4.住居の位置をグラフ上に描画する 73 plt.scatter(ad1, ad2) 74 #6-5.for文を用いて資源回収物拠点のX・Y座標値を変数[(i, j)]に,変数[k]に住宅の代表点から資源回収物拠点までの距離を代入する 75 for k, (i, j) in zip(fs, zip(ps[0], ps[1])): 76 #6-6.もし資源回収物拠点を設置する場合の描画設定を行う 77 if k.value() == 1: 78 #6-6-1.資源回収物拠点の色は赤色 79 plt.scatter(i, j, s=2, color='red') 80 #6-6-2.許容領域の描画設定を行う 81 c = patches.Circle(xy=(i, j), radius=r, fill=False) 82 #6-6-3.許容領域を描画する 83 ax.add_patch(c) 84 #6-7.資源回収物拠点を設置しない場合の設定を行う 85 else: 86 #6-7-1.passする 87 pass 88 #6-8.縦横比が同じ長さになるように調整。 89 plt.axis('scaled') 90 ax.set_aspect('equal') 91 #6-9.グリッド線を表示 92 plt.grid() 93 #6-10.ⅹ軸のラベルを設定 94 ax.set_xlabel('経度', fontname="MS Gothic") 95 #6-11.y軸のラベルを設定 96 ax.set_ylabel('緯度', fontname="MS Gothic") 97 #6-12.x軸の下限値と上限値とその間隔を設定 98 ax.set_xticks(np.linspace(137, 138, 10)) 99 #6-13.y軸の下限値と上限値とその間隔を設定 100 ax.set_yticks(np.linspace(34, 35, 10)) 101 #6-14.グラフを描画 102 plt.show() 103 104#7.住居と資源回収物拠点における集合被覆問題を解く 105def solver_set(ps, r): 106 #7-1.変数[size]に資源回収物拠点のx座標値の個数を代入 107 size = len(ps[0]) 108 #7-2.変数[cs]に「#3需要変数(二値変数)を作成」の関数を代入 109 cs = make_matrix(ps, r) 110 #7-3.変数[prob]に集合被覆問題を解く命令を代入 111 prob = pulp.LpProblem('max-cover', sense = pulp.LpMaximize) 112 #7-4.変数[fs]に変数[size]に格納されている値を変数[i]に移行し,この問題で解く際に必要となる関数を作成 113 fs = [pulp.LpVariable('f{}'.format(i), lowBound=0, cat = 'Binary') for i in range(size)] 114 #7-5.変数[prob]にも目的関数を追加 115 prob += pulp.lpSum(fs) 116 #7-6.変数[cs]から変数[c]に制約条件を追加 117 for c in cs: 118 prob += pulp.lpDot(c, fs) >= fs[c] 119 #7-7.変数[s]に時間計測の命令を代入 120 s = time.time() 121 #7-8.変数[status]に集合被覆問題が解けたかどうかの確認を行う命令を代入 122 status = prob.solve() 123 #7-9.変数[status]の結果を表示 124 print('Status', pulp.LpStatus[status]) 125 #7-10.設置された資源回収物拠点の数を表示 126 print('z 施設数:', prob.objective.value()) 127 #7-11.集合被覆問題の回答処理に掛かった時間を表示 128 print('処理時間', time.time() - s) 129 #7-12.関数[draw(ps, fs, r)]を実行 130 draw(ps, fs, r) 131 132#許容領域の半径 133r = 0.03 134#資源回収物拠点の個数 135sigen = 10 136#乱数上限 137upper_x = 137.758 138upper_y = 34.778 139#実行 140solver_set(make_data_si(sigen, upper_x, upper_y), r)
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
google上で「数理最適化 最大被覆問題 Python」と検索し、最大被覆問題を取り扱うPythonのプログラムを参照し、変更を加えた。
下記が変更したコードの部分
def solver_set(ps, r, sigen):
#7-1.変数[size]に資源回収物拠点のx座標値の個数を代入
size = len(ps[0])
#7-2.変数[cs]に「#3需要変数(二値変数)を作成」の関数を代入
cs = make_matrix(ps, r)
#7-3.変数[prob]に集合被覆問題を解く命令を代入
prob = pulp.LpProblem('max-cover', sense = pulp.LpMaximize)
#7-4.変数[fs]に変数[size]に格納されている値を変数[i]に移行し,この問題で解く際に必要となる関数を作成
fs = [pulp.LpVariable('f{}'.format(i), lowBound=0, cat = 'Binary') for i in range(size)]
xs = [pulp.LpVariable('x{}'.format(i), lowBound=0, cat = 'Binary') for i in range(size)]
prob += pulp.lpSum(xs) == sigen
#7-6.変数[cs]から変数[c]に制約条件を追加
for i in range(size):
prob += pulp.lpDot(cs[i], fs) >= xs[i]
#7-7.変数[s]に時間計測の命令を代入
s = time.time()
#7-8.変数[status]に集合被覆問題が解けたかどうかの確認を行う命令を代入
status = prob.solve()
#7-9.変数[status]の結果を表示
print('Status', pulp.LpStatus[status])
#7-10.設置された資源回収物拠点の数を表示
print('z 施設数:', prob.objective.value())
#7-11.集合被覆問題の回答処理に掛かった時間を表示
print('処理時間', time.time() - s)
#7-12.関数[draw(ps, fs, r)]を実行
draw(ps, fs, r)
補足
PythonのVersionは2023.22.1であり、ソルバーにはpulpを使用しています。

