Python:“体育竞技分析”实例

468 阅读4分钟

以本实例来阐述自顶向下设计自底向上执行为主的基本程序设计方法学

IPO描述

使用的实例抽象于各种球类比赛中的一般规则,约定如下:

  • 两人开始比赛,其中一个球员先发球。
  • 接下来球员交替击球,直到可以判断得分为止(这个过程称为回合-->由此可以将这个思路推广到回合制网游上)
  • 当一名球员未能进行一次合法击球时候,回合结束
    • 未能打中球的球员输掉这个回合
    • 如果输掉这个回合的是发球方,那么发球权交予另一方
    • 如果输掉的是接球方,则仍然由这个回合的发球方继续发球。
  • 总之,每回合结束,由赢得该回合的一方发球。
  • 首先达到15分的球员赢得一局比赛

输入和输出

  • 输入:两个球员A、B的能力概率以及模拟比赛的场次
  • 输出:球员A、分别赢得比赛的概率

设计步骤:

  1. 打印程序的介绍性信息式;printInfo()
  2. 获得程序运行参数:proA,proB,n;getInputs()
  3. 利用球员A和B的能力值,模拟n局比赛;simNGames()
  4. 输出球员A和B获胜比赛的场次及概率;printSummary()

由此问题被划分成4个独立的函数,这些函数的名称、输出参数和预期返回值都以及被确定

image.png 自顶向下的设计过程(可以看作是发现功能并抽象功能的过程)

总体思路:确定输入输出,向下每一层从算法描述逐步细化成代码,细节被函数封装

具体步骤

  1. 将算法表达为一系列小问题
  2. 为每个小问题设计接口
  3. 通过将算法表达为接口关联的多个小问题来细化算法
  4. 为每个小问题重复上述过程

代码

import random
def printIntro():
    print("这个程序模拟量个选手A和B的某种竞技比赛")
    print("程序运行需要A和B的能力值(以0到1之间的小数表示)")
 
def getInputs():
    a = eval(input("请输入选手A的能力值(0-1): "))
    b = eval(input("请输入选手B的能力值(0-1): "))
    n = eval(input("模拟比赛的场次: "))
    return a, b, n
 
def printSummary(winsA, winsB):
    n = winsA + winsB
    print("竞技分析开始, 共模拟{}场比赛".format(n))
    print("选手A获胜{}场比赛, 占比{:0.1%}".format(winsA, winsA/n))
    print("选手B获胜{}场比赛, 占比{:0.1%}".format(winsB, winsB/n))
 
def gameOver(a, b):
    return a == 15 or b == 15
    
def simOneGame(probA, probB):
    scoreA, scoreB = 0, 0
    serving = "A"
    while not gameOver(scoreA, scoreB):
        if serving == "A":
            if random.random() < probA:
                scoreA += 1
            else:
                serving = "B"
        else:
            if random.random() < probB:
                scoreB += 1
            else:
                serving = "A"
    return scoreA, scoreB
 
def simNGames(n ,probA, probB):
    winsA, winsB = 0, 0
    for i in range(n):
        scoreA, scoreB = simOneGame(probA, probB)
        if scoreA > scoreB:
            winsA += 1
        else:
            winsB += 1
    return winsA, winsB
 
def main():
    printIntro()
    probA, probB, n = getInputs()
    winsA, winsB = simNGames(n, probA, probB)
    printSummary(winsA, winsB)
main()

对代码进行注释

#使用保留字对random库进行引用
import random

#输出打印必要说明
def printIntro():
    print("这个程序模拟量个选手A和B的某种竞技比赛")
    print("程序运行需要A和B的能力值(以0到1之间的小数表示)")

#获得用户输入,封装或隐藏输入提示语句和输入格式等细节,调用可获得A、B能力值和场次
def getInputs():
    a = eval(input("请输入选手A的能力值(0-1): "))#eval()限制用户输入数字,这里可以用try:-except:做异常处理
    b = eval(input("请输入选手B的能力值(0-1): "))
    n = eval(input("模拟比赛的场次: "))
    return a, b, n

#输出结果
def printSummary(winsA, winsB):
    n = winsA + winsB
    print("竞技分析开始, 共模拟{}场比赛".format(n))#fomat()将数字转化插入字符串
    print("选手A获胜{}场比赛, 占比{:0.1%}".format(winsA, winsA/n))
    print("选手B获胜{}场比赛, 占比{:0.1%}".format(winsB, winsB/n))

#终止比赛条件,封装该函数有利于修改不同规则适合不同比赛复用
def gameOver(a, b):
    return a == 15 or b == 15

#根据规则模拟每一场比赛(核心代码块)
def simOneGame(probA, probB):
    scoreA, scoreB = 0, 0
    serving = "A"#默认A持球
    while not gameOver(scoreA, scoreB):
        if serving == "A":
            if random.random() < probA:#random.random()用于生成一个0到1的随机符点数: 0 <= n < 1.0
                scoreA += 1
            else:
                serving = "B"
        else:
            if random.random() < probB:#这个改成if random()<probB/(probA+probB):更合理
                scoreB += 1
            else:
                serving = "A"
    return scoreA, scoreB

#计数,统计每场比赛结果
def simNGames(n ,probA, probB):
    winsA, winsB = 0, 0
    for i in range(n):
        scoreA, scoreB = simOneGame(probA, probB)
        if scoreA > scoreB:
            winsA += 1
        else:
            winsB += 1
    return winsA, winsB
 
#main()函数称谓顶层结构,由此建立程序框架,通过函数将细节封装和隐藏
def main():
    printIntro()
    probA, probB, n = getInputs()
    winsA, winsB = simNGames(n, probA, probB)
    printSummary(winsA, winsB)
main()

运行结果

这个程序模拟量个选手A和B的某种竞技比赛
程序运行需要A和B的能力值(以01之间的小数表示)
请输入选手A的能力值(0-1): 0.5
请输入选手B的能力值(0-1): 0.4
模拟比赛的场次: 25
竞技分析开始, 共模拟25场比赛
选手A获胜17场比赛, 占比68.0%
选手B获胜8场比赛, 占比32.0%

操作截图

image.png

image.png