已知问题

1.同一个父节点的策略组合的情况下,如果出现了判断博弈人的得益时出现了多个得益中对应位置的值均相等的情况,自动取其中一个得益作为父节点的最佳得益。

2.没有考虑如下情况:第一个参与人X有多个选择时,若选择了A作为策略时,则下一个博弈人为Y;若选择了B作为策略时,则下一个博弈人为Z

没有博弈树的绘制。

实验代码

import re
import itertools
import numpy as np

Order = input('输入参与博弈的多个参与人进行决策的顺序(以逗号分隔):')  # 输入参与人的决策次序
Order = re.split('[,,]', Order)  # 按逗号分开
Players = []  # 参加博弈的参与人列表
for item in Order:  # 遍历决策次序集
    if item not in Players:  # 将参与人的名称不重复地加入到参与人列表中
        Players.append(item)
print('参与博弈的参与人:', Players)  # 输出所有参与人
print('博弈的决策的顺序:', Order)  # 输出参与人的决策顺序

DecisionSet = []  # 总策略集
DecisionCount = []  # 各参与人的决策个数列表
for i in Order:  # 按照决策次序,输入各参与人的策略并形成策略集
    SubDecisionSet = input('参与人 {0} 的策略(以逗号分隔):'.format(i))  # 参与人i的策略
    SubDecisionSet = re.split('[,,]', SubDecisionSet)  # 按逗号分开
    DecisionSet.append(SubDecisionSet)  # 将各参与人的策略集加入到总策略集中
    DecisionCount.append(len(SubDecisionSet))  # 将各参与人的决策个数加入到决策个数列表中

TDSets = list(itertools.product(*DecisionSet, repeat=1))  # 将各策略集中的策略进行排列组合
TreeDecisionSet = []  # 策略组合集
for item in TDSets:  # 将排列组合完成的策略数据格式转化为列表
    TreeDecisionSet.append(list(item))
print('生成的博弈树中所有的策略组合:')
for i in range(len(TreeDecisionSet)):  # 输出带编号的所有策略组合
    print(i + 1, TreeDecisionSet[i])


def Copy(List):
    NewList = []
    for item in List:
        NewList.append(item)
    return NewList


def DeleteDecision(DataSet, IndexSet):
    # 读取
    InnerDataSet = Copy(DataSet)
    DelDecSet = []  # 待删除策略的集合
    DelDecIndexSet = []  # 待删除策略的位置
    for i in range(len(InnerDataSet)):
        if i + 1 in IndexSet:  # 删除序号在总策略序号集中
            del InnerDataSet[i][-1]  # 将待删除策略的最后一个策略删除,作为删除项的标记
            DelDecSet.append(InnerDataSet[i])  # 将待删除策略加入删除策略集合
            DelDecIndexSet.append(i)  # 将待删除策略的位置加入删除策略位置集合
    # 删除
    Length = len(DelDecSet[0])  # 取待删除策略的长度,此处必然比原策略少1
    for i in DelDecIndexSet:  # 遍历删除位置集合
        for j in range(len(InnerDataSet)):  # 以原策略集长度作为循环的策略下标进行循环
            if j != i:  # 保证原策略集中的策略下标与待删除策略的下标不同
                if InnerDataSet[i] is not None and InnerDataSet[j] is not None:  # 保证两者策略非空
                    if InnerDataSet[i][:] == InnerDataSet[j][:Length]:  # 如果存在短策略是长策略的真子集的情况,则将短策略置空
                        InnerDataSet[i] = None
    for i in range(len(InnerDataSet)):  # 以0为起始遍历整个策略集
        for j in range(i + 1, len(InnerDataSet)):  # 以i为起始遍历整个策略集
            if InnerDataSet[i] is not None and InnerDataSet[j] is not None:  # 保证两者策略非空
                if len(InnerDataSet[i]) < len(InnerDataSet[j]) and InnerDataSet[i][:] == InnerDataSet[j][:Length]:
                    # 如果存在短策略是长策略的真子集的情况,则将短策略置空
                    InnerDataSet[i] = None
    OutputSet = []  # 显示到用户前的策略集
    for i in range(len(InnerDataSet)):  # 以0为起始遍历整个策略集
        if InnerDataSet[i] is None:  # 如果为None,直接跳过
            continue
        else:  # 否则直接将策略复制到OutputSet中
            OutputSet.append(InnerDataSet[i])
    return OutputSet, InnerDataSet


Judge = input('是否删除部分策略组合?(直接回车/输入Y表示删除,输入其他为不删除):')
if Judge in 'Yy' or Judge == '':
    flag = 1
else:
    flag = 0
while True:
    if flag == 1:  # 如果进行策略删除操作
        while True:
            DeleteIndex = input('请输入不存在的策略组合的编号(以逗号分隔,每次删除同一层上的策略,输入其他值无效):')
            DeleteIndex = re.split('[,,]', DeleteIndex)
            JudgeSet = []
            for i in range(len(TreeDecisionSet)):  # 将策略集的索引转为字符串形式,便于后续比较
                JudgeSet.append(str(i + 1))
            if set(DeleteIndex).issubset(set(JudgeSet)):  # 如果待删除策略的编号在原策略集中
                break  # 退出本层while循环,继续进行删除操作
            else:  # 否则重新输入待删除策略的编号
                # P.S. 此处的误输入处理也可处理输入其他字符的情况
                print('输入有误,请重新输入。')
        DeleteIndex = list(map(int, DeleteIndex))
        # 进行删除处理,得到删除后的策略集以及 带None的用于后续程序处理 的策略集
        TreeDecisionSet, FullDataSet = DeleteDecision(TreeDecisionSet, DeleteIndex)
        print('删除不存在的策略组合后,博弈树中所有的策略组合:')
        for i in range(len(TreeDecisionSet)):  # 展示删除后的策略集
            print(i + 1, TreeDecisionSet[i])
        flag = 2
        Judge = input('是否继续删除?(回车表示继续删除)(Y/N):')
    elif flag == 0:  # 如果不进行策略删除操作
        FullDataSet = Copy(TreeDecisionSet)  # 直接把策略集复制一份,保存为后续程序处理的策略集
        break
    if Judge in 'Yy' or Judge == '':  # 若继续删除
        flag = 1  # 改变flag的值,继续程序循环
    elif Judge in 'Nn':  # 若停止删除
        break  # 直接退出while循环
    elif Judge not in 'YyNn':  # 判断输入是否合规
        Judge = input('输入有误,请重新输入(Y/N):')

Matrix0 = []  # 得益集
for i in range(len(TreeDecisionSet)):
    while True:
        print('当参与人的决策次序为', Order, ',决策依次为', TreeDecisionSet[i], '时')  # 输出当前待输入得益的对应决策次序以及对应决策
        PayOff = input('按顺序输入各参与人的得益(以逗号分隔):')
        PayOff = re.split('[,,]', PayOff)
        if len(PayOff) != len(Players):  # 判断得益的输入是否与参与得益人数是否相同,若不相同则重新输入
            print('输入得益的长度有误,请重新输入')
        else:
            Matrix0.append(list(map(int, PayOff)))  # 把得益转为int列表并加入到得益集中
            break

for i in range(len(TreeDecisionSet)):  # 输出决策次序、决策以及对应的得益
    print('博弈树中各参与人的决策次序为', Order, ',决策为', TreeDecisionSet[i], '时,得益为', Matrix0[i])

# 存在删除策略情况时的得益处理
FullMatrix = []  # 程序处理得益集
for i in range(len(FullDataSet)):
    TempList = []  # 初始化得益列表,为下文所述的循环中可能出现的 不存在策略的得益的处理 做准备
    if FullDataSet[i] not in TreeDecisionSet:  # 如果程序处理策略集中的策略不在输出策略集中
        for j in range(len(Players)):  # 输入得益有多长,就对应产生几个float(-inf)组成的列表
            # 例:设输入得益为[3,2,-1],则不存在策略项的得益为[-inf, -inf, -inf]
            TempList.append(float('-inf'))
        FullMatrix.append(TempList)
    else:  # 如果程序处理策略集中的策略在输出策略集中
        FullMatrix.append(Matrix0[TreeDecisionSet.index(FullDataSet[i])])  # 直接把输出策略集中策略对应得益复制到程序处理策略集中


def FindBalance(DecisionSet, BenefitSet, Order, Players, CountMatrix):
    InnerDecisionSet = Copy(DecisionSet)  # 原始策略集复制,得到内循环策略集
    InnerBenefitSet = Copy(BenefitSet)  # 原始得益集复制,得到内循环得益集
    for i in range(len(Order)):  # 遍历决策次序,决策次序的值是多少就循环多少次
        if i != len(Order) - 1:  # 在不是循环的最后一轮的情况下
            BestBenefitSet = []  # 初始化该层的最优得益集
            BestDecisionSet = []  # 初始化该层的最优决策
        DecSetLen = len(InnerDecisionSet)  # 内循环策略集的长度
        Position = Order[-(i + 1)]  # 倒序取本轮的决策次序,例:次序为1,2,3时,次序取法为3,2,1
        PlayerIndex = Players.index(Position)  # 已知决策次序找决策人,以此来确定最优得益的找法。例:次序为1,2,1时,倒序取到1,在进行比较时取所有策略的第1项得益进行比较
        '''print('DecSetLen,Position,PlayerIndex', DecSetLen, Position, PlayerIndex)'''

        L = CountMatrix[-(i + 1)]  # 该轮循环中分组的最小长度,即决策人的决策个数

        InnDecCount = []  # 获取策略集中的过滤策略的长度(因为上列程序的输入可能存在非完美信息博弈树的情况)
        for item in InnerDecisionSet:  # 遍历内循环策略集
            if item == None:  # 如果不存在策略,则直接跳过
                continue
            else:  # 将策略的长度加入到过滤策略长度集中
                InnDecCount.append(len(item))
        InnDecCount.sort()  # 整理过滤策略长度集,升序排列
        InnDecCount = list(dict.fromkeys(InnDecCount))  # 去除过滤策略长度集中重复的长度
        MinLength = InnDecCount[0]  # 初始化策略集最小长度
        if i + 1 > len(InnDecCount):  # 如果是循环最后一轮,则直接令长度等于最小长度
            FilterLength = MinLength
        else:  # 否则将倒数第二个值作为最小值
            FilterLength = InnDecCount[-(i + 1)]
        '''print('L,FilterLength,MinLength', L, FilterLength, MinLength)'''
        TempDataList = []  # 临时策略集
        TempBenefitList = []  # 临时得益集
        for m in range(DecSetLen):  # 循环内循环集
            if InnerDecisionSet[m] is None:  # 如果有None项,则将None项替换为以最小长度个X的字符串
                InnerDecisionSet[m] = 'X' * MinLength
        Inn = Copy(InnerDecisionSet)  # 复制一份作为下列程序中找最优值的策略集
        InnBe = Copy(InnerBenefitSet)  # 复制一份作为下列程序中找最优值的得益集
        if i != len(Order) - 1:  # 如果不是外层最后一轮循环
            for m in range(DecSetLen):  # 循环内循环集
                if len(Inn[m]) >= FilterLength:  # 若最优策略长度大于过滤长度
                    TempDataList.append(Inn[m])  # 将策略加入临时策略集中
                    TempBenefitList.append(InnBe[m])  # 将得益加入临时得益集中
                    '''print('m,TempDataList,TempBenefitList', m, TempDataList, TempBenefitList)'''
                    if len(TempBenefitList) % L == 0 and len(TempBenefitList) // L > 0:  # 如果临时策略集的长度刚好够分组长度
                        # 取博弈人索引处的一列,并取该列的最大值
                        X = np.array(TempBenefitList)
                        X1 = list(X[:, PlayerIndex])
                        BenefitPos = X1.index(max(X1))  # 取最大值对应策略的索引值
                        '''print('X1,BenefitPos', X1, BenefitPos)'''
                        BestBenefit = TempBenefitList[BenefitPos]  # 令最佳策略索引值对应的得益为最佳得益
                        BestDecision = TempDataList[BenefitPos]  # 令最佳策略索引值对应的策略为最佳策略
                        '''print('BestBenefit,BestDecision', BestBenefit, BestDecision)'''
                        BestBenefitSet.append(BestBenefit)  # 将最佳得益加入到最佳得益集合中
                        BestDecisionSet.append(BestDecision)  # 将最佳策略加入到最佳策略集合中
                        for k in range(len(TempDataList)):  # 遍历临时策略集
                            if TempDataList[k] not in BestDecisionSet:  # 如果满足:临时策略集的策略不在最佳策略集中
                                if TempDataList[k] in Inn:  # 则判断临时策略集的策略是否在最优策略集中
                                    # 同时满足上述两个条件时,删除内循环中 临时策略集中非最优策略在内循环策略集的索引 的项,内循环得益集的操作同理
                                    del InnerDecisionSet[InnerDecisionSet.index(TempDataList[k])]
                                    del InnerBenefitSet[InnerBenefitSet.index(TempBenefitList[k])]
                        TempDataList = []  # 重置临时策略集
                        TempBenefitList = []  # 重置临时得益集
        else:
            # 代码含义与上段代码相同
            X = np.array(BestBenefitSet)
            X1 = list(X[:, PlayerIndex])
            BenefitPos = X1.index(max(X1))
            BestBenefit = BestBenefitSet[BenefitPos]
            BestDecision = BestDecisionSet[BenefitPos]
            BestBenefitSet = []  # 重置最佳得益集
            BestDecisionSet = []  # 重置最佳策略集
            BestBenefitSet.append(BestBenefit)  # 将最终最佳得益加入到最佳得益集合中
            BestDecisionSet.append(BestDecision)  # 将最终最佳策略加入到最佳策略集合中
        '''print('InnerBenefitSet,InnerDecisionSet', InnerBenefitSet, InnerDecisionSet)
        print('BestBenefitSet,BestDecisionSet', BestBenefitSet, BestDecisionSet)'''
    return BestBenefitSet, BestDecisionSet  # 返回最终循环的最佳策略集和最佳得益集


BenefitSet, BenefitDecision = FindBalance(FullDataSet, FullMatrix, Order, Players, DecisionCount)
print('该博弈树的最佳得益为:', BenefitSet)  # 打印最终得益集
print('对应策略为:', BenefitDecision)  # 打印最终策略集