इस समस्या से निपटने के लिए, मैं एक पूर्णांक प्रोग्रामिंग ढांचे का उपयोग करेगा और निर्णय चर के तीन सेटों को परिभाषित करेगा:
- x_ij : बाइनरी इंडिकेटर वैरिएबल है कि क्या हम पानी के स्थान पर पुल बनाते हैं (i, j)।
- y_ijbcn : पानी के स्थान (i, j) के लिए एक बाइनरी संकेतक n ^ वें स्थान है जो द्वीप b को द्वीप c से जोड़ता है।
- l_bc : द्वीपसमूह ख और ग के लिए एक द्विआधारी संकेतक चर सीधे जुड़ा हुआ है (उर्फ आप केवल पुल वर्गों पर ख से ग तक चल सकते हैं)।
पुल निर्माण की लागत c_ij के लिए , न्यूनतम करने के लिए उद्देश्य मूल्य है sum_ij c_ij * x_ij
। हमें निम्नलिखित बाधाओं को मॉडल में जोड़ने की आवश्यकता है:
- हमें यह सुनिश्चित करने की आवश्यकता है कि y_ijbcn चर वैध हैं। हम हमेशा केवल एक पानी के वर्ग तक पहुंच सकते हैं यदि हम वहां एक पुल का निर्माण करते हैं, तो
y_ijbcn <= x_ij
हर पानी के स्थान (i, j) के लिए। इसके अलावा, y_ijbc1
बराबर होना चाहिए 0 अगर (i, j) बॉर्डर आइलैंड b नहीं है। अंत में, n> 1 के लिए, y_ijbcn
केवल तभी उपयोग किया जा सकता है जब चरण n-1 में पड़ोसी पानी के स्थान का उपयोग किया गया हो। N(i, j)
पानी के वर्गों को परिभाषित करना पड़ोसी (i, j) है, यह बराबर है y_ijbcn <= sum_{(l, m) in N(i, j)} y_lmbc(n-1)
।
- हमें यह सुनिश्चित करने की आवश्यकता है कि l_bc वैरिएबल केवल सेट किए गए हैं यदि b और c लिंक हैं। यदि हम
I(c)
द्वीप सी की सीमा वाले स्थानों को परिभाषित करते हैं, तो इसे पूरा किया जा सकता है l_bc <= sum_{(i, j) in I(c), n} y_ijbcn
।
- हमें यह सुनिश्चित करने की आवश्यकता है कि सभी द्वीप सीधे या परोक्ष रूप से जुड़े हुए हैं। इसे निम्नलिखित तरीके से पूरा किया जा सकता है: द्वीपों के प्रत्येक गैर-रिक्त उचित उप-समूह के लिए, यह आवश्यक है कि S के कम से कम एक द्वीप S के पूरक में कम से कम एक द्वीप से जुड़ा हो, जिसे हम S कहेंगे। बाधाओं में, हम आकार के हर गैर-खाली सेट के लिए एक बाधा जोड़कर इसे लागू कर सकते हैं <= K / 2 (जहां K द्वीपों की संख्या है)
sum_{b in S} sum_{c in S'} l_bc >= 1
।
के द्वीपों, डब्ल्यू पानी वर्गों और निर्दिष्ट अधिकतम पथ लंबाई एन के साथ एक समस्या उदाहरण के लिए, यह O(K^2WN)
चर और O(K^2WN + 2^K)
बाधाओं के साथ मिश्रित पूर्णांक प्रोग्रामिंग मॉडल है । जाहिर है कि यह असाध्य हो जाएगा क्योंकि समस्या का आकार बड़ा हो जाएगा, लेकिन यह उन आकारों के लिए हल हो सकता है जिनकी आप परवाह करते हैं। स्केलेबिलिटी की भावना पाने के लिए, मैं इसे पल्प पैकेज का उपयोग करके अजगर में लागू करूंगा। आइए सबसे पहले प्रश्न के निचले भाग में 3 द्वीपों वाले छोटे 7 x 9 मानचित्र के साथ शुरू करें:
import itertools
import pulp
water = {(0, 2): 2.0, (0, 3): 1.0, (0, 4): 1.0, (0, 5): 1.0, (0, 6): 2.0,
(1, 0): 2.0, (1, 1): 9.0, (1, 2): 1.0, (1, 3): 9.0, (1, 4): 9.0,
(1, 5): 9.0, (1, 6): 1.0, (1, 7): 9.0, (1, 8): 2.0,
(2, 0): 1.0, (2, 1): 9.0, (2, 2): 9.0, (2, 3): 1.0, (2, 4): 9.0,
(2, 5): 1.0, (2, 6): 9.0, (2, 7): 9.0, (2, 8): 1.0,
(3, 0): 9.0, (3, 1): 1.0, (3, 2): 9.0, (3, 3): 9.0, (3, 4): 5.0,
(3, 5): 9.0, (3, 6): 9.0, (3, 7): 1.0, (3, 8): 9.0,
(4, 0): 9.0, (4, 1): 9.0, (4, 2): 1.0, (4, 3): 9.0, (4, 4): 1.0,
(4, 5): 9.0, (4, 6): 1.0, (4, 7): 9.0, (4, 8): 9.0,
(5, 0): 9.0, (5, 1): 9.0, (5, 2): 9.0, (5, 3): 2.0, (5, 4): 1.0,
(5, 5): 2.0, (5, 6): 9.0, (5, 7): 9.0, (5, 8): 9.0,
(6, 0): 9.0, (6, 1): 9.0, (6, 2): 9.0, (6, 6): 9.0, (6, 7): 9.0,
(6, 8): 9.0}
islands = {0: [(0, 0), (0, 1)], 1: [(0, 7), (0, 8)], 2: [(6, 3), (6, 4), (6, 5)]}
N = 6
# Island borders
iborders = {}
for k in islands:
iborders[k] = {}
for i, j in islands[k]:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
if (i+dx, j+dy) in water:
iborders[k][(i+dx, j+dy)] = True
# Create models with specified variables
x = pulp.LpVariable.dicts("x", water.keys(), lowBound=0, upBound=1, cat=pulp.LpInteger)
pairs = [(b, c) for b in islands for c in islands if b < c]
yvals = []
for i, j in water:
for b, c in pairs:
for n in range(N):
yvals.append((i, j, b, c, n))
y = pulp.LpVariable.dicts("y", yvals, lowBound=0, upBound=1)
l = pulp.LpVariable.dicts("l", pairs, lowBound=0, upBound=1)
mod = pulp.LpProblem("Islands", pulp.LpMinimize)
# Objective
mod += sum([water[k] * x[k] for k in water])
# Valid y
for k in yvals:
i, j, b, c, n = k
mod += y[k] <= x[(i, j)]
if n == 0 and not (i, j) in iborders[b]:
mod += y[k] == 0
elif n > 0:
mod += y[k] <= sum([y[(i+dx, j+dy, b, c, n-1)] for dx in [-1, 0, 1] for dy in [-1, 0, 1] if (i+dx, j+dy) in water])
# Valid l
for b, c in pairs:
mod += l[(b, c)] <= sum([y[(i, j, B, C, n)] for i, j, B, C, n in yvals if (i, j) in iborders[c] and B==b and C==c])
# All islands connected (directly or indirectly)
ikeys = islands.keys()
for size in range(1, len(ikeys)/2+1):
for S in itertools.combinations(ikeys, size):
thisSubset = {m: True for m in S}
Sprime = [m for m in ikeys if not m in thisSubset]
mod += sum([l[(min(b, c), max(b, c))] for b in S for c in Sprime]) >= 1
# Solve and output
mod.solve()
for row in range(min([m[0] for m in water]), max([m[0] for m in water])+1):
for col in range(min([m[1] for m in water]), max([m[1] for m in water])+1):
if (row, col) in water:
if x[(row, col)].value() > 0.999:
print "B",
else:
print "-",
else:
print "I",
print ""
लुगदी पैकेज (सीबीसी सॉल्वर) से डिफ़ॉल्ट सॉल्वर का उपयोग करने के लिए 1.4 सेकंड लगते हैं और सही समाधान का उत्पादन करते हैं:
I I - - - - - I I
- - B - - - B - -
- - - B - B - - -
- - - - B - - - -
- - - - B - - - -
- - - - B - - - -
- - - I I I - - -
अगला, सवाल के शीर्ष पर पूरी समस्या पर विचार करें, जो 7 द्वीपों के साथ एक 13 x 14 ग्रिड है:
water = {(i, j): 1.0 for i in range(13) for j in range(14)}
islands = {0: [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)],
1: [(9, 0), (9, 1), (10, 0), (10, 1), (10, 2), (11, 0), (11, 1),
(11, 2), (12, 0)],
2: [(0, 7), (0, 8), (1, 7), (1, 8), (2, 7)],
3: [(7, 7), (8, 6), (8, 7), (8, 8), (9, 7)],
4: [(0, 11), (0, 12), (0, 13), (1, 12)],
5: [(4, 10), (4, 11), (5, 10), (5, 11)],
6: [(11, 8), (11, 9), (11, 13), (12, 8), (12, 9), (12, 10), (12, 11),
(12, 12), (12, 13)]}
for k in islands:
for i, j in islands[k]:
del water[(i, j)]
for i, j in [(10, 7), (10, 8), (10, 9), (10, 10), (10, 11), (10, 12),
(11, 7), (12, 7)]:
water[(i, j)] = 20.0
N = 7
एमआईपी सॉल्वर अक्सर अपेक्षाकृत जल्दी से अच्छा समाधान प्राप्त करते हैं और फिर समाधान की इष्टतमता साबित करने की कोशिश कर रहे समय के बारे में एक बड़ा खर्च करते हैं। ऊपर के समान सॉल्वर कोड का उपयोग करके, प्रोग्राम 30 मिनट के भीतर पूरा नहीं होता है। हालाँकि, आप सॉल्वर को एक अनुमानित समाधान प्राप्त करने के लिए एक समय-सीमा प्रदान कर सकते हैं:
mod.solve(pulp.solvers.PULP_CBC_CMD(maxSeconds=120))
यह उद्देश्य मूल्य 17 के साथ एक समाधान देता है:
I I - - - - - I I - - I I I
I I - - - - - I I - - - I -
I I - - - - - I - B - B - -
- - B - - - B - - - B - - -
- - - B - B - - - - I I - -
- - - - B - - - - - I I - -
- - - - - B - - - - - B - -
- - - - - B - I - - - - B -
- - - - B - I I I - - B - -
I I - B - - - I - - - - B -
I I I - - - - - - - - - - B
I I I - - - - - I I - - - I
I - - - - - - - I I I I I I
आपके द्वारा प्राप्त किए गए समाधानों की गुणवत्ता में सुधार करने के लिए, आप एक वाणिज्यिक एमआईपी सॉल्वर का उपयोग कर सकते हैं (यह मुफ़्त है यदि आप एक शैक्षणिक संस्थान में हैं और संभवतः अन्यथा मुक्त नहीं हैं)। मिसाल के तौर पर, यहाँ पर गुरूबी ६.०.४ का प्रदर्शन, फिर से २ मिनट की समय सीमा के साथ (हालाँकि समाधान लॉग से हम पढ़ते हैं कि सॉल्वर को solution सेकंड के भीतर वर्तमान सबसे अच्छा समाधान मिला):
mod.solve(pulp.solvers.GUROBI(timeLimit=120))
यह वास्तव में वस्तुनिष्ठ मूल्य 16 का हल ढूंढता है, ओपी से बेहतर एक हाथ से खोजने में सक्षम था!
I I - - - - - I I - - I I I
I I - - - - - I I - - - I -
I I - - - - - I - B - B - -
- - B - - - - - - - B - - -
- - - B - - - - - - I I - -
- - - - B - - - - - I I - -
- - - - - B - - B B - - - -
- - - - - B - I - - B - - -
- - - - B - I I I - - B - -
I I - B - - - I - - - - B -
I I I - - - - - - - - - - B
I I I - - - - - I I - - - I
I - - - - - - - I I I I I I