अजगर जोखिम खेल

Sep 12 2020

मैंने एक जोखिम खेल प्रश्न हल किया (http://www.pyschools.com/quiz/view_question/s3-q12) दो तरीकों से, लेकिन जैसा कि मैं वर्तमान में पायथन सीख रहा हूं, मैं बस सोच रहा हूं कि इस प्रश्न को हल करने के मेरे तरीकों को कैसे रेट किया जाए। इस प्रश्न को हल करने के लिए इनमें से कौन सा तरीका वास्तव में एक बेहतर, प्रभावी और कुशल तरीका है? या हो सकता है कि मैंने जो किया है, उससे बेहतर तरीका क्या हो?

यह गेम इंस्ट्रक्शन है: "रिस्क बोर्ड गेम में, ऐसी स्थिति होती है, जिसमें हमलावर 3 पासा चलाता है, जबकि डिफेंडर 2 पासा चलाता है। परिणाम का निर्धारण करने के लिए, प्रत्येक खिलाड़ी की उच्चतम मृत्यु की तुलना की जाती है, उसके बाद अगले उच्चतम मर जाते हैं।" प्रत्येक मामले के लिए, हमलावर की मृत्यु जीतने वाले के बचावकर्ता से अधिक होनी चाहिए। हारने वाला प्रत्येक मामले में 1 सेना खो देगा। "

यह फ़ंक्शन और रिटर्न स्टेटमेंट का नमूना है:

    >>> RiskGame([6,2,6], [6, 6])
    'Attacker loses 2 armies.'
    >>> RiskGame([1,4,1], [1, 2])
    'Attacker loses 1 army and defender loses 1 army.' 

विधि 1

def RiskGame(attacker, defender):
    a_score = 0
    a_loose = 0
    d_score = 0
    d_loose = 0
    for e in range(len(defender)):
        a= max(attacker)
        d= max(defender)
        
        if a>d:
            a_score +=1
            d_loose +=1
        
        else:
            d_score +=1
            a_loose +=1
        attacker.remove(a)
        defender.remove(d)
    if a_loose == 0:
        return 'Defender Loses %i armies.' %d_loose
    elif d_loose == 0:
        return 'Attacker loses %i armies.' %a_loose
    else:
        return 'Attacker loses %i army and defender loses %i army.' %(a_loose, d_loose)
            
RiskGame([1,2,6], [1, 5])
RiskGame([1,4,1], [1, 2])
RiskGame([6,2,6], [6, 6])

विधि 2

def RiskGame(attacker, defender):
    a = sorted(attacker, reverse=True)
    b = sorted(defender, reverse=True)
    
    a_scr =0
    d_scr =0
    pairs = zip(a,b)
    for i,j in pairs:
        if i>j:
            a_scr +=1
        else:
            d_scr +=1
    if d_scr == 0:
        return 'Defender loses %i armies.' %a_scr
    elif a_scr == 0:
        return 'Attacker loses %i armies.' %d_scr
    else:
        return 'Attacker loses %i army and defender loses %i army.' %(a_scr, d_scr)

RiskGame([1,2,6], [1, 5])
RiskGame([1,4,1], [1, 2])
RiskGame([6,2,6], [6, 6])

जवाब

13 Marc Sep 12 2020 at 18:40

दोनों तरीकों के लिए अच्छा कार्यान्वयन, कुछ सुझाव:

  • ऊंट का मामला या अंडरस्कोर। समारोह RiskGameऊंट मामले संकेतन का उपयोग करता है, लेकिन चर अंडरस्कोर नोटेशन ( a_score) का उपयोग करते हैं । केवल एक अंकन का उपयोग करने के लिए बेहतर है। आमतौर पर, पायथन में अंडरस्कोर पसंद किए जाते हैं।
  • अप्रयुक्त चर : a_scoreऔर d_scoreविधि 1 में।
  • चर नाम सुधाराa_scr जा सकता है : का नाम बदला जा सकता है attacker_score। यह कथन या इसके समान a=max(attacker)हो सकता है attacker_max_number=max(attacker)। यहां तक ​​कि अगर यह लंबा है, तो कोड को पढ़ना आसान बनाता है।
  • मानव-पठनीय स्ट्रिंग के बजाय परिणाम लौटाएं , पुन: उपयोग और परीक्षण करना आसान है। इसके बजाय उदाहरण के लिए:
    def RiskGame(attacker, defender):
        #...
        if d_scr == 0:
            return 'Defender loses %i armies.' %a_scr
        elif a_scr == 0:
            return 'Attacker loses %i armies.' %d_scr
        else:
            return 'Attacker loses %i army and defender loses %i army.' %(a_scr, d_scr)
    
    सीधे परिणाम लौटाएं:
    def RiskGame(attacker, defender):
        #...
        return attacker_score, defender_score
    
    attacker_score, defender_score = RiskGame([1,2,6], [1, 5])
    if defender_score == 0:
        print('Defender Loses %i armies.' %attacker_score)
    #...
    

कौन सी विधि अधिक कुशल है?

इनपुट सूची के लिए अधिकतम तीन तत्वों की आवश्यकताओं को देखते हुए प्रदर्शन के बारे में कोई समस्या नहीं है। वास्तव में, @Jasmijn टिप्पणी में बताते हैं, शर्त यह है कि के रूप में 1 <= len(attacker) <= 3और 1 <= len(defender) <= 2, दोनों तरीकों के समय जटिलता है \$O(1)\$।

यदि इनपुट सूचियों में कई तत्व हैं और हमलावर की सूची रक्षक की सूची से बड़ी है, तो मैं कहूंगा कि विधि 2 तेज है। विधि 1 के समय जटिलता होगा \$O(d*a)\$कहाँ \$d\$डिफेंडर की सूची और \ की लंबाई है$a\$हमलावर की सूची की लंबाई है। भले ही सूचियाँ प्रत्येक पुनरावृत्ति पर सिकुड़ती हों, हम कह सकते हैं कि बड़े आदानों के लिए। विधि 2 \ _ होगी$O(a*log(a))\$यह मानते हुए कि zip()फ़ंक्शन \ _ में चलता है$O(d)\$और छँटाई हमलावर सूची लेता है \$O(a*log(a))\$।

7 FMc Sep 13 2020 at 01:46

आपके पास पहले से कुछ अच्छे उत्तर हैं। आपके कोड के विवरण पर ध्यान केंद्रित करने के बजाय, मैं एक पूर्ण कार्यक्रम के डिजाइन के दृष्टिकोण के बारे में कुछ टिप्पणियां प्रदान करूंगा। मुझे आपकी सटीक स्थिति का पता नहीं है, लेकिन जब आप सीख रहे हैं, तो कमांड-लाइन प्रोग्राम विभिन्न व्यावहारिक कारणों के लिए एक अच्छा केंद्र बिंदु हैं, इसलिए मैं इसका इस्तेमाल एक दृष्टांत के रूप में करूँगा।

कार्यात्मक कोर और अनिवार्य खोल । जितना संभव हो, अपने कार्यक्रम को दो प्रकार के कार्यों में सख्ती से अलग करें: (ए) जो कुछ भी नहीं करते हैं, लेकिन डेटा लेते हैं और डेटा (मूल) वापस करते हैं; और (बी) जिनके साइड-इफेक्ट्स (शेल) हैं। साइड-इफेक्ट के रूप में क्या मायने रखता है? कई चीजें, लेकिन मुद्रण और अपवाद दो सबसे आम हैं। यहां मुख्य विचार यह है कि आपके अधिकांश कार्यक्रम (और विशेष रूप से इसके एल्गोरिथम, कम्प्यूटेशनल विवरण) को कार्यात्मक कोर में रहना चाहिए। मुद्रण और अन्तरक्रियाशीलता से संबंधित बाहरी आवरण जितना संभव हो उतना पतला और गैर-एल्गोरिथम होना चाहिए। नीचे दिए गए उदाहरण में, main()बाहरी शेल है और यह एक एल्गोरिथम दृष्टिकोण से दंडित है - एक तुच्छ से ज्यादा कुछ नहीं if-else। यदि आप इन विचारों के बारे में अधिक जानना चाहते हैं, तो मैंने इस कोर-शेल पृथक्करण पर सबसे अच्छी बात गैरी बर्नहार्ट से की है ।

इसे तोड़ दो । एक बार जब आपके मन में यह मौलिक अलगाव हो जाता है, तो आवश्यक गणनाओं को यथोचित रूप से छोटे भागों में बदलना शुरू कर दें। यह प्रयास भाग कला, भाग विज्ञान है। कितनी दूर आपको अपघटन करना चाहिए संदर्भ पर निर्भर करता है। नीचे दिया गया उदाहरण इसे बहुत दूर ले जाता है। जितना अधिक जटिल कार्यक्रम, और उच्च दांव, उतनी ही गंभीरता से आप अपघटन प्रयास करना चाहते हैं। सरल परिस्थितियों में, विभिन्न शॉर्टकट ठीक हैं। लेकिन जब कार्यक्रम महत्वपूर्ण होता है, तो आपको इसके लिए परीक्षण लिखने की आवश्यकता होती है, और परीक्षण क्षमता की मांग अक्सर आपको अधिक विघटित करने के लिए प्रेरित करेगी (यह उन कार्यों का परीक्षण करना मुश्किल हो सकता है जो एक ही बार में बहुत सी चीजें करते हैं, और यह परीक्षण करने के लिए एक बड़ा सिरदर्द है। ऐसे कार्य जिनके प्रमुख दुष्प्रभाव हैं)।

उस सादगी पर ध्यान दें जो उभरती है । पाठक के लिए एक टिप्पणी में वर्णन करने के लिए कार्य छोटा, समझने में आसान, जल्दी समाप्त होता है। ज्यादातर स्थितियों में उन चीजों का फायदा होता है जो (अक्सर महत्वपूर्ण रूप से) अतिरिक्त चीजों को तोड़ने के लिए अतिरिक्त समय लेने की अतिरिक्त लागत होती हैं।

import sys

def main(args):
    # Command-line usage example: `python risk_game.py 3,4,6 3,5`
    attacker, defender, error = parse_entries(args)
    if error:
        print(error)
        sys.exit(1)
    else:
        message = risk_game(attacker, defender)
        print(message)

def parse_entries(entries):
    # Takes attacker and defender entries. Returns a 3-tuple: (ATTACKER-ROLLS,
    # DEFENDER-ROLLS, ERROR-MESSAGE). There are more featureful and robust ways
    # to handle this; adjust as needed.
    try:
        return (parse_entry(entries[0]), parse_entry(entries[1]), None)
    except Exception as e:
        return (None, None, 'Invalid entry')

def parse_entry(entry):
    # Parses a single entry and returns a list of dice rolls.
    return [int(val) for val in entry.split(',')]

def risk_game(attacker, defender):
    # Takes two lists of dice rolls. Returns a message describing the outcome.
    score = compute_battle_score(attacker, defender)
    return generate_message(attacker, defender, score)

def compute_battle_score(attacker, defender):
    # Takes two lists of dice rolls. Returns a battle score.
    atts = sorted(attacker, reverse = True)
    defs = sorted(defender, reverse = True)
    return sum(1 if a > d else -1 for a, d in zip(atts, defs))

    # Or if you need to know N of victories for each combatant.
    return collections.Counter(a > d for a, d in zip(atts, defs))

def generate_message(attacker, defender, score):
    # Make it as fancy as you want.
    return f'Attacker score: {score}'

if __name__ == '__main__':
    main(sys.argv[1:])
5 superbrain Sep 12 2020 at 21:06

पहला इनपुट बदल देता है, जो तब तक खराब है जब तक इसे माना नहीं जाता है। आप दी गई सूचियों की प्रतियाँ बनाकर और उन पर काम करने के बजाय उसे ठीक कर सकते हैं।

स्ट्रिंग स्वरूपण अनावश्यक, क्योंकि मूल्यों हमेशा से रहे हैं 2, 2या (1, 1)क्रमशः। हमें अच्छी लेखन शैली, अंकों को शब्दों के रूप में लिखने का अवसर देता है, अंक नहीं। हाँ, मुझे पता है कि समस्या सेटर के लिए अंकों की आवश्यकता होती है, इसलिए यह आपकी गलती है, आपकी नहीं। बस केह रहा हू। आप यह तर्क दे सकते हैं कि आपकी स्थिति अधिक सामान्य है, यदि अधिक पासा का इस्तेमाल किया गया था, लेकिन तब आपको एकवचन / बहुवचन का भी उचित रूप से उपयोग करना चाहिए, "हमलावर 5 सेना खो देता है" जैसे कुछ कहने से बचने के लिए।

अंत में, मैं एक पक्ष चुनूंगा। यही है, केवल एक पक्ष के आंकड़ों को गिनें। मैं हमलावर के साथ जाऊंगा, क्योंकि वह "सक्रिय" पार्टी है (रक्षा के विपरीत प्रतिक्रिया के रूप में )।

def RiskGame(attacker, defender):
    _, a1, a2 = sorted(attacker)
    d1, d2 = sorted(defender)
    wins = (a2 > d2) + (a1 > d1)
    if wins == 2:
        return 'Defender loses two armies.'
    elif wins == 0:
        return 'Attacker loses two armies.'
    else:
        return 'Attacker loses one army and defender loses one army.'
2 user1717828 Sep 13 2020 at 08:06

यदि यह एक वास्तविक पीआर होता, तो मैं एक *looseचर को उजागर कर दूं और क्या इसे पढ़ना चाहिएlose ?

मुझे वास्तव में आपका दूसरा तरीका पसंद है। इसे पढ़ते समय, मुझे लगा कि कोड की अगली पंक्ति बिल्कुल वही कर रही थी जो मैं अनुमान लगा रहा था कि यह क्या करेगा, और मुझे इसे पढ़ते समय उतनी मानसिक स्मृति का उपयोग नहीं करना पड़ा, जैसे मैंने आपका पहला दृष्टिकोण किया, उन सभी का ध्यान रखा। चर जो शून्य पर सेट किए गए थे। एक तरीका जिसे आप सुधारने पर विचार कर सकते हैं वह यह है कि शुरुआती और प्रारंभिक गणनाओं को हटा दें a_scrऔर उनकी गणना करने के d_scrलिए एक sum()का उपयोग करें, जैसे:

def RiskGame(attacker, defender):
    a = sorted(attacker, reverse=True)
    b = sorted(defender, reverse=True)
    
    a_scr = sum([i > j for i, j in zip(a,b)])
    d_scr = sum([i < j for i, j in zip(a,b)])

    if d_scr == 0:
        return 'Defender loses %i armies.' %a_scr
    elif a_scr == 0:
        return 'Attacker loses %i armies.' %d_scr
    else:
        return 'Attacker loses %i army and defender loses %i army.' %(a_scr, d_scr)

RiskGame([1,2,6], [1, 5])

मेरे पास फ़ंक्शंस के पास्कल आवरण और मूल्यों के बजाय स्ट्रिंग्स को वापस करने के बारे में समान आरक्षण हैं, लेकिन ये अन्य उत्तरों में अच्छी तरह से कवर किए गए हैं।

संपादित करें: परीक्षणों का उपयोग करने का तरीका प्रदर्शित करें

यह भाग आपके उदाहरण की लड़ाइयों को सुनिश्चित करने का एक और तरीका शामिल करता है जो व्यवस्थित रूप से पाइस्टेस्ट का उपयोग करके सही तरीके से चलता है। यह दिखाता है कि कुछ अन्य उत्तरों के फ़ंक्शन को छपाई से बाहर निकालने के सुझावों के कारण इसे कोड करना आसान हो जाता है।

चरण 1।

अपना मुख्य कार्य अभी लौटें a_scrऔर d_scr, और मुख्य स्क्रिप्ट में प्रिंटिंग लॉजिक डालें:

# riskgame.py
def risk_game(attacker, defender):
    a = sorted(attacker, reverse=True)
    b = sorted(defender, reverse=True)

    a_scr = sum([i > j for i, j in zip(a,b)])
    d_scr = sum([i <= j for i, j in zip(a,b)])

    return a_scr, d_scr

if __name__ == "__main__":
    a_scr, d_scr = risk_game([6,2,6], [6, 6])

    if d_scr == 0:
        print('Defender loses %i armies.' %a_scr)
    elif a_scr == 0:
        print('Attacker loses %i armies.' %d_scr)
    else:
        print('Attacker loses %i army and defender loses %i army.' %(a_scr, d_scr))

जब आप स्क्रिप्ट निष्पादित करते हैं, तो आपको पहले जैसा व्यवहार मिलता है:

$ python riskgame.py 
Attacker loses 2 armies.

चरण 2।

उसी निर्देशिका में test_wins_and_losses.pyकुछ परीक्षण बनाएं और बनाएं:

# test_wins_and_losses.py
from riskgame import risk_game
  
def test_d_wins():
    a_scr, d_scr = risk_game([1,2,6], [1, 5])
    assert a_scr == 2
    assert d_scr == 0

def test_a_wins():
    a_scr, d_scr = risk_game([6,2,6], [6, 6])
    assert a_scr == 0
    assert d_scr == 2

def test_equal():
    a_scr, d_scr = risk_game([1,4,1], [1, 2])
    assert a_scr == 1
    assert d_scr == 1

ध्यान दें कि मैंने आपके द्वारा मूल पोस्ट में डाले गए समान मानों का उपयोग किया था, लेकिन आपके पास कोई भी उदाहरण गेम हो सकता है। आदर्श रूप में, आपके पास बहुत सारे होंगे, जितने उपयोग के मामले हो सकते हैं।

चरण 3।

यदि आप पहले से ही नहीं है, तो स्थापित करें।

$ pip install pytest

चरण 4।

चलाओ!

$ pytest
============================= test session starts ==============================
platform linux -- Python 3.7.4, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: /tmp/risk_game
collected 3 items                                                              

test_wins_and_losses.py ...                                              [100%]

============================== 3 passed in 0.02s ===============================

विचार यह है कि अब आप अपना कोड बदल सकते हैं, और हर बार जब आप करते हैं तो आप pytestकमांड लाइन पर टाइप करके यह पुष्टि कर सकते हैं कि सब कुछ अभी भी आपकी अपेक्षा के अनुरूप है । उदाहरण के लिए, यदि हम पहले की गई गलती करते हैं और लाइन को बदल देते हैं

d_scr = sum([i < j for i, j in zip(a,b)])

और परीक्षण चलाते हैं, हम प्राप्त करते हैं:

$ pytest
==================================================================== test session starts =====================================================================
platform linux -- Python 3.7.4, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: /tmp/risk_game
collected 3 items                                                                                                                                            

test_wins_and_losses.py .FF                                                                                                                            [100%]

========================================================================== FAILURES ==========================================================================
________________________________________________________________________ test_a_wins _________________________________________________________________________

    def test_a_wins():
        a_scr, d_scr = risk_game([6,2,6], [6, 6])
        assert a_scr == 0
>       assert d_scr == 2
E       assert 0 == 2

test_wins_and_losses.py:11: AssertionError
_________________________________________________________________________ test_equal _________________________________________________________________________

    def test_equal():
        a_scr, d_scr = risk_game([1,4,1], [1, 2])
        assert a_scr == 1
>       assert d_scr == 1
E       assert 0 == 1

test_wins_and_losses.py:16: AssertionError
================================================================== short test summary info ===================================================================
FAILED test_wins_and_losses.py::test_a_wins - assert 0 == 2
FAILED test_wins_and_losses.py::test_equal - assert 0 == 1
================================================================ 2 failed, 1 passed in 0.09s ================================================================

खुश परीक्षण!