回答編集履歴

3

d

2019/06/25 03:50

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -36,6 +36,8 @@
36
36
 
37
37
  [Maximum Matching in General Graphs](https://www.slideshare.net/akhayyat/maximum-matching-in-general-graphs)
38
38
 
39
+ [最大重みマッチング問題](http://www.ocw.titech.ac.jp/index.php?module=General&action=DownLoad&file=201702129-632-0-12.pdf&type=cal&JWC=201702129)
40
+
39
41
 
40
42
 
41
43
  できる限り各個人の希望を反映したマッチング ==> 最大重みマッチングです。
@@ -84,22 +86,276 @@
84
86
 
85
87
  for person, wish in wishes.items():
86
88
 
87
- for i, snack in enumerate(wish):
89
+ for snack, weight in zip(wish, range(3, 0, -1)):
88
90
 
89
91
  # (人, 希望するお菓子) という辺を追加する。
90
92
 
91
- # 重みは第1希望: 0, 第2希望: -1, 第3希望: -2 になる。
93
+ # 重みは第1希望: 3, 第2希望: 2, 第3希望: 1 になる。
92
-
94
+
93
- G.add_edge(person, snack, weight=-i)
95
+ G.add_edge(person, snack, weight=weight)
94
-
95
-
96
-
96
+
97
+
98
+
97
- # 「辺の数が最大 (maxcardinality=True)となるマッチング」のうち、「重み最大となるマッチング」を探す。
99
+ # 「重み最大となるマッチング」を探す。
98
-
100
+
99
- matches = nx.max_weight_matching(G, maxcardinality=True)
101
+ matches = nx.max_weight_matching(G)
102
+
100
-
103
+ print(matches)
104
+
101
- # {('ケーキ', 'Bさん'), ('メロン', 'Aさん'),
105
+ # {('ケーキ', 'Bさん'), ('プリン', 'Dさん'), ('いちご', 'Cさん'), ('Aさん', 'メロン')}
102
-
106
+
107
+
108
+
103
- # ('Cさん', 'いちご'), ('Dさん', 'プリン')}
109
+ y = sum([G.edges[e1, e2]["weight"] for e1, e2 in matches])
110
+
111
+ print(f"y = {y}")
104
112
 
105
113
  ```
114
+
115
+
116
+
117
+ ## 追記 整数計画問題として解く方法
118
+
119
+
120
+
121
+ 次のように変数を定義することで整数計画問題としても定式化できます。
122
+
123
+
124
+
125
+ ![イメージ説明](7174e2e50e65a18e4623e68bc26cdf8a.png)
126
+
127
+
128
+
129
+ [PuLP](https://pythonhosted.org/PuLP/) という線形ソルバーで説いたところ、networkx の最大重みマッチングの結果と一致することが確認できました。
130
+
131
+
132
+
133
+ ```python
134
+
135
+ from pulp import *
136
+
137
+
138
+
139
+ # お菓子の一覧
140
+
141
+ snacks = ["いちご", "キャラメル", "ケーキ", "プリン", "メロン"]
142
+
143
+
144
+
145
+ # 子供の希望
146
+
147
+ wishes = {
148
+
149
+ # "名前": [第1希望, 第2希望, 第3希望]
150
+
151
+ "Aさん": ["プリン", "ケーキ", "メロン"],
152
+
153
+ "Bさん": ["ケーキ", "いちご", "メロン"],
154
+
155
+ "Cさん": ["いちご", "プリン", "キャラメル"],
156
+
157
+ "Dさん": ["プリン", "いちご", "メロン"],
158
+
159
+ }
160
+
161
+
162
+
163
+ # モデルを作成する。
164
+
165
+ model = LpProblem(name="max weight matching", sense=LpMaximize)
166
+
167
+
168
+
169
+ # # 変数を作成する。
170
+
171
+ X = [
172
+
173
+ [LpVariable(f"{person}_{snack}", cat=LpBinary) for snack in snacks]
174
+
175
+ for person in wishes
176
+
177
+ ]
178
+
179
+
180
+
181
+ # 目的関数を設定する。
182
+
183
+ weights = range(3, 0, -1) # 重み: 第1希望:3、第2希望:2、第3希望:1
184
+
185
+ snack_to_idx = {name: i for i, name in enumerate(snacks)} # お菓子の名前がキー、インデックスが値の辞書
186
+
187
+
188
+
189
+ y = 0
190
+
191
+ for i, wish in enumerate(wishes.values()):
192
+
193
+ for snack, w in zip(wish, weights):
194
+
195
+ j = snack_to_idx[snack]
196
+
197
+ y += X[i][j] * w
198
+
199
+ model += y
200
+
201
+
202
+
203
+ # 制約を設定する。
204
+
205
+
206
+
207
+ # (1) 一人1つしかお菓子を選べない。
208
+
209
+ for i in range(len(wishes)):
210
+
211
+ model += lpSum(X[i]) == 1
212
+
213
+
214
+
215
+ # (2) 二人以上が同じお菓子を選べない。
216
+
217
+ for j in range(len(snacks)):
218
+
219
+ s = 0
220
+
221
+ for i in range(len(wishes)):
222
+
223
+ s += X[i][j]
224
+
225
+ model += s <= 1
226
+
227
+
228
+
229
+ # モデルを表示する。
230
+
231
+ print(model)
232
+
233
+
234
+
235
+ # 解く。
236
+
237
+ ret = model.solve()
238
+
239
+ print("status", LpStatus[ret]) # status Optimal
240
+
241
+
242
+
243
+ # 解けた場合は結果を表示する。
244
+
245
+ if ret == 1:
246
+
247
+ print(f"y = {model.objective.value()}")
248
+
249
+ for i, person in enumerate(wishes):
250
+
251
+ select_idx = np.argmax([x.value() for x in X[i]]) # 選択したお菓子のインデックス
252
+
253
+ print(f"{person}: {snacks[select_idx]}")
254
+
255
+ ```
256
+
257
+
258
+
259
+ ```output
260
+
261
+ max weight matching:
262
+
263
+ MAXIMIZE
264
+
265
+ 2*Aさん_ケーキ + 3*Aさん_プリン + 1*Aさん_メロン + 2*Bさん_いちご + 3*Bさん_ケーキ + 1*Bさん_メロン + 3*Cさん_いちご + 1*Cさん_キャラメル + 2*Cさん_プリン + 2*Dさん_いちご + 3*Dさん_プリン + 1*Dさん_メロン + 0
266
+
267
+ SUBJECT TO
268
+
269
+ _C1: Aさん_いちご + Aさん_キャラメル + Aさん_ケーキ + Aさん_プリン + Aさん_メロン = 1
270
+
271
+
272
+
273
+ _C2: Bさん_いちご + Bさん_キャラメル + Bさん_ケーキ + Bさん_プリン + Bさん_メロン = 1
274
+
275
+
276
+
277
+ _C3: Cさん_いちご + Cさん_キャラメル + Cさん_ケーキ + Cさん_プリン + Cさん_メロン = 1
278
+
279
+
280
+
281
+ _C4: Dさん_いちご + Dさん_キャラメル + Dさん_ケーキ + Dさん_プリン + Dさん_メロン = 1
282
+
283
+
284
+
285
+ _C5: Aさん_いちご + Bさん_いちご + Cさん_いちご + Dさん_いちご <= 1
286
+
287
+
288
+
289
+ _C6: Aさん_キャラメル + Bさん_キャラメル + Cさん_キャラメル + Dさん_キャラメル <= 1
290
+
291
+
292
+
293
+ _C7: Aさん_ケーキ + Bさん_ケーキ + Cさん_ケーキ + Dさん_ケーキ <= 1
294
+
295
+
296
+
297
+ _C8: Aさん_プリン + Bさん_プリン + Cさん_プリン + Dさん_プリン <= 1
298
+
299
+
300
+
301
+ _C9: Aさん_メロン + Bさん_メロン + Cさん_メロン + Dさん_メロン <= 1
302
+
303
+
304
+
305
+ VARIABLES
306
+
307
+ 0 <= Aさん_いちご <= 1 Integer
308
+
309
+ 0 <= Aさん_キャラメル <= 1 Integer
310
+
311
+ 0 <= Aさん_ケーキ <= 1 Integer
312
+
313
+ 0 <= Aさん_プリン <= 1 Integer
314
+
315
+ 0 <= Aさん_メロン <= 1 Integer
316
+
317
+ 0 <= Bさん_いちご <= 1 Integer
318
+
319
+ 0 <= Bさん_キャラメル <= 1 Integer
320
+
321
+ 0 <= Bさん_ケーキ <= 1 Integer
322
+
323
+ 0 <= Bさん_プリン <= 1 Integer
324
+
325
+ 0 <= Bさん_メロン <= 1 Integer
326
+
327
+ 0 <= Cさん_いちご <= 1 Integer
328
+
329
+ 0 <= Cさん_キャラメル <= 1 Integer
330
+
331
+ 0 <= Cさん_ケーキ <= 1 Integer
332
+
333
+ 0 <= Cさん_プリン <= 1 Integer
334
+
335
+ 0 <= Cさん_メロン <= 1 Integer
336
+
337
+ 0 <= Dさん_いちご <= 1 Integer
338
+
339
+ 0 <= Dさん_キャラメル <= 1 Integer
340
+
341
+ 0 <= Dさん_ケーキ <= 1 Integer
342
+
343
+ 0 <= Dさん_プリン <= 1 Integer
344
+
345
+ 0 <= Dさん_メロン <= 1 Integer
346
+
347
+
348
+
349
+ status Optimal
350
+
351
+ y = 10.0
352
+
353
+ Aさん: メロン
354
+
355
+ Bさん: ケーキ
356
+
357
+ Cさん: いちご
358
+
359
+ Dさん: プリン
360
+
361
+ ```

2

d

2019/06/25 03:50

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -35,6 +35,10 @@
35
35
 
36
36
 
37
37
  [Maximum Matching in General Graphs](https://www.slideshare.net/akhayyat/maximum-matching-in-general-graphs)
38
+
39
+
40
+
41
+ できる限り各個人の希望を反映したマッチング ==> 最大重みマッチングです。
38
42
 
39
43
 
40
44
 

1

d

2019/06/24 05:43

投稿

tiitoi
tiitoi

スコア21956

test CHANGED
@@ -60,6 +60,8 @@
60
60
 
61
61
  wishes = {
62
62
 
63
+ # "名前": [第1希望, 第2希望, 第3希望]
64
+
63
65
  "Aさん": ["プリン", "ケーキ", "メロン"],
64
66
 
65
67
  "Bさん": ["ケーキ", "いちご", "メロン"],