2025, Sep 25 19:32
Python में 3-खिलाड़ी गेम शेड्यूल: combinations की जगह इंडेक्सिंग से सीधा निर्माण
जानें कैसे Python में चुने खिलाड़ी के लिए 3-खिलाड़ी गेम शेड्यूल सीधे बनाएं: combinations के अनावश्यक आउटपुट से बचें, इंडेक्सिंग अपनाएं और किनारी स्थिति संभालें.
तीन खिलाड़ियों वाली गेम्स का शेड्यूल बनाना सुनने में आसान लगता है, लेकिन इसे प्रोग्रामिंग से तैयार करना उतना सीधा नहीं। यहां लक्ष्य स्पष्ट है: किसी चुने हुए खिलाड़ी के लिए ऐसे गेम्स का सेट बनाना, जहाँ वह हर अन्य प्रतिभागी से ठीक एक बार भिड़े, और हर गेम में कुल तीन खिलाड़ी हों। साधारण combinations तरीका सर्च स्पेस को बेवजह फैलाता है और वास्तव में चाहिए वही पेयरिंग नियम लागू नहीं करता। नीचे एक छोटा-सा तरीका दिया है जो एक केंद्र खिलाड़ी के लिए साफ-सुथरा शेड्यूल तैयार करता है, और उस किनारी स्थिति को भी संभालता है जब खिलाड़ियों की कुल संख्या बराबर-बराबर नहीं बँटती।
समस्या की रूपरेखा
हम चुने हुए खिलाड़ी (मान लें, "a") के लिए 11-खिलाड़ियों के रोस्टर पर ऐसा परिणाम चाहते हैं: game 1 = a, b, c; game 2 = a, d, e; game 3 = a, f, g; game 4 = a, h, i; game 5 = a, j, k. मतलब, "a" हर दूसरे खिलाड़ी से ठीक एक बार मिलता है, हर गेम में दो प्रतिद्वंद्वी।
साधारण तरीका जो जरूरत से ज्यादा देता है
पहला प्रयास यह हो सकता है कि सभी 3-combinations बना लिए जाएं और फिर उनमें से वे चुने जाएं जिनमें केंद्र खिलाड़ी शामिल हो। इससे वैध त्रय तो मिलते हैं, पर यह केंद्र खिलाड़ी के साथ संभव हर तीसरा सदस्य भी लौटा देता है—जो जरूरत से कहीं ज्यादा है।
from itertools import combinations
roster = ['a','b','c','d','e','f','g','h','i','j','k']
group_size = 3
triads = list(combinations(roster, group_size))
games_for_a = []
for pack in triads:
    if 'a' in pack:
        games_for_a.append(pack)
यह केंद्र खिलाड़ी के लिए 45 त्रय दे देता है, क्योंकि शेष 10 खिलाड़ियों से बनने वाले हर अलग जोड़े को "a" के साथ जोड़ देता है। अंतिम शेड्यूल में हर प्रतिद्वंद्वी को सिर्फ एक बार इस्तेमाल करने की बाध्यता यह तरीका लागू नहीं करता।
ऐसा क्यों होता है
combinations कॉल तीन-तीन के सभी संभव समूह गिनती है, कोई चुना-छाँटा शेड्यूल नहीं। "a" की सदस्यता से फ़िल्टर करने पर वे सारे त्रय बचते हैं जिनमें "a" आता है—जो choose(10, 2) = 45 होते हैं—जबकि हमें तो सिर्फ पाँच गेम चाहिए। यानी आप पहले पूरी संभावनाएँ बना लेते हैं और फिर काट-छाँट करते हैं, बजाय इसके कि सीधे ज़रूरी शेड्यूल गढ़ें।
सरल इंडेक्सिंग से सीधा निर्माण
एक अधिक सीधा और नियत तरीका है रोस्टर में (केंद्र खिलाड़ी index 0 को छोड़कर) एक बार में दो-दो कदम चलते हुए आस-पास के प्रतिद्वंद्वियों की जोड़ी को केंद्र खिलाड़ी के साथ जोड़ देना। इससे ठीक वैसा ही शेड्यूल बनता है जैसा चाहिए।
roster = ['a','b','c','d','e','f','g','h','i','j','k']
schedule = {}
for idx in range(1, len(roster), 2):
    schedule[f"game {int((idx + 1) / 2)}"] = ['a', roster[idx], roster[idx + 1]]
print(schedule)
आउटपुट:
{'game 1': ['a', 'b', 'c'], 'game 2': ['a', 'd', 'e'], 'game 3': ['a', 'f', 'g'], 'game 4': ['a', 'h', 'i'], 'game 5': ['a', 'j', 'k']}
काम करने का तरीका सीधा है। इटरेशन index 1 से शुरू होकर 2-2 कदम बढ़ता है, तो विरोधियों की जोड़ियाँ [1,2], [3,4], [5,6]... मिलती जाती हैं। हर जोड़ी को केंद्र खिलाड़ी "a" के साथ मिलाकर एक गेम बनता है। गेम नंबर लूप इंडेक्स से सरल गणना से निकलता है।
किनारी स्थिति: एक प्रतिद्वंद्वी बच जाना
अगर रोस्टर के आकार के कारण जोड़ियाँ बनाते समय अंत में एक प्रतिद्वंद्वी अकेला बच जाए, तो भी आप अंतिम प्रविष्टि में केंद्र खिलाड़ी और उसी आखिरी प्रतिद्वंद्वी को रख सकते हैं। try/except से लूप कॉम्पैक्ट रहता है।
roster = ['a','b','c','d','e','f','g','h','i','j','k','l']
schedule = {}
for idx in range(1, len(roster), 2):
    try:
        schedule[f"game {int((idx + 1) / 2)}"] = ['a', roster[idx], roster[idx + 1]]
    except IndexError:
        schedule[f"game {int((idx + 1) / 2)}"] = ['a', roster[idx]]
print(schedule)
आउटपुट:
{'game 1': ['a', 'b', 'c'], 'game 2': ['a', 'd', 'e'], 'game 3': ['a', 'f', 'g'], 'game 4': ['a', 'h', 'i'], 'game 5': ['a', 'j', 'k'], 'game 6': ['a', 'l']}
यह संस्करण वही इटरेशन रणनीति रखता है और जब केंद्र खिलाड़ी को हटाने के बाद शेष संख्या विषम हो, तब अंतिम सिंगलटन को भी सलीके से संभाल लेता है।
व्यवहार्यता पर नोट जिसे ध्यान में रखें
जब आप इस सोच पर जाते हैं कि “हर कोई हर किसी से ठीक एक बार मिले” और हर गेम में तीन खिलाड़ी हों, तो एक व्यापक बाधा दिखती है। 11 खिलाड़ियों में कुल 55 अद्वितीय जोड़ियाँ बनती हैं; हर गेम 3 जोड़ियों को कवर करता है; और 55, 3 से विभाज्य नहीं है। यह अंकगणितीय असंगति बताती है कि ऐसी शर्तों में हर जोड़ी को ठीक एक बार कवर करने वाला वैश्विक शेड्यूल बन पाना संभव नहीं। अगर आप तीन-खिलाड़ी समूहों के साथ पूरे सीजन का डिज़ाइन खंगाल रहे हैं, जहाँ जोड़ी-स्तर पर विशिष्टता चाहिए, तो Steiner Triple System पर शोध यह समझने में मदद करता है कि कब ऐसा शेड्यूल संभव होता है।
यह क्यों मायने रखता है
शेड्यूल प्रोग्रामmatically बनाते समय अक्सर हम कॉम्बिनेटोरियल जेनरेशन और बाद की फ़िल्टरिंग में फँस जाते हैं। इससे वैध समूह तो मिलते हैं, पर वही ढांचा नहीं बनता जिसकी ज़रूरत है—और कई बार व्यवहार्यता की बाधाएँ भी छिप जाती हैं। इंडेक्सिंग के साथ सीधा निर्माण प्रति-खिलाड़ी परिदृश्य में पूर्वानुमेय नतीजा देता है और इम्प्लीमेंटेशन को छोटा और साफ रखता है।
समापन
इरादे की बाधाओं से ही शुरुआत करें और शेड्यूल को सीधे गढ़ें, सभी combinations गिनने के बजाय। प्रति-खिलाड़ी आवश्यकता के लिए, केंद्र खिलाड़ी को लगातार प्रतिद्वंद्वियों की जोड़ियों के साथ जोड़ना हर विरोधी से एक ही मुलाकात सुनिश्चित करता है, और एक छोटा-सा try/except बचे हुए प्रतिद्वंद्वी को आसानी से सँभाल लेता है। अगर इसे पूरे लीग तक बढ़ाना चाहें, जहाँ हर जोड़ी एक बार मिले, तो असंभव विन्यासों का पीछा करने से पहले जोड़ी-गणना की बुनियादी अंकगणित जांच लें।
यह लेख StackOverflow पर प्रश्न (लेखक: postcardfiction) और Aadvik के उत्तर पर आधारित है।