├── 3.决策树 ├── data │ ├── 1.png │ ├── 2.png │ ├── 1.csv │ └── 2.csv ├── main.py ├── README.md └── trees.py ├── README.md ├── 1.井字棋 ├── data │ ├── tree1.png │ ├── tree2.png │ └── tree3.png ├── README.md └── game.py ├── 2.推理机 ├── data │ ├── topo.png │ ├── data.txt │ └── topo.xml ├── README.md ├── back.py └── forward.py └── .idea ├── vcs.xml ├── modules.xml ├── misc.xml └── expert-system.iml /3.决策树/data/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/3.决策树/data/1.png -------------------------------------------------------------------------------- /3.决策树/data/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/3.决策树/data/2.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 专家系统 2 | 3 | 1. 井字棋:Minimax算法 + Alpha-Beta算法 4 | 2. 推理机:正向推理机、反向推理机 5 | 3. 决策树:ID3算法 6 | -------------------------------------------------------------------------------- /1.井字棋/data/tree1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/1.井字棋/data/tree1.png -------------------------------------------------------------------------------- /1.井字棋/data/tree2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/1.井字棋/data/tree2.png -------------------------------------------------------------------------------- /1.井字棋/data/tree3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/1.井字棋/data/tree3.png -------------------------------------------------------------------------------- /2.推理机/data/topo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangbincheng1997/expert-system/HEAD/2.推理机/data/topo.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /2.推理机/data/data.txt: -------------------------------------------------------------------------------- 1 | 有毛 哺乳类 2 | 有奶 哺乳类 3 | 有羽毛 鸟类 4 | 会飞 会生蛋 鸟类 5 | 吃肉 哺乳类 肉食类 6 | 犬牙利爪 眼睛向前 肉食类 7 | 反刍食物 哺乳类 偶蹄类 8 | 有蹄 哺乳类 有蹄类 9 | 黄褐色 暗斑点 肉食类 金钱豹 10 | 黄褐色 黑条纹 肉食类 老虎 11 | 长腿 长脖子 黄褐色 暗斑点 有蹄类 长颈鹿 12 | 黑白条纹 有蹄类 斑马 13 | 不会飞 长腿 长脖子 鸟类 鸵鸟 14 | 不会飞 善游泳 黑白色 鸟类 企鹅 15 | 善飞 鸟类 信天翁 16 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /1.井字棋/README.md: -------------------------------------------------------------------------------- 1 | # 井字棋 2 | 3 | ## Minimax算法 4 | 如图所示,最佳移动位于中间,因为最大值位于左侧的第二个节点上。 5 | ![alt text](data/tree1.png "title") 6 | 7 | 简化游戏树: 8 | ![alt text](data/tree2.png "title") 9 | 10 | ## Alpha-Beta算法 11 | Alpha-Beta剪枝优化,它允许我们忽略搜索树中的某些分支,因为他在搜索中剪切了不相关的节点(子树)。 12 | ![alt text](data/tree3.png "title") 13 | 14 | ## 参考 15 | 1. https://github.com/Cledersonbc/tic-tac-toe-minimax 16 | 2. http://www.stratigery.com/ttt_ab.py 17 | -------------------------------------------------------------------------------- /3.决策树/data/1.csv: -------------------------------------------------------------------------------- 1 | DQ,JZZY,YJZJ,YJHF,Class 2 | a2,b1,c1,d1,N 3 | a2,b1,c2,d1,N 4 | a2,b1,c3,d1,N 5 | a1,b1,c1,d1,Y 6 | a1,b1,c3,d1,Y 7 | a3,b2,c1,d1,N 8 | a3,b2,c3,d1,N 9 | a3,b1,c1,d2,Y 10 | a3,b3,c3,d2,N 11 | a3,b1,c2,d2,N 12 | a1,b3,c2,d2,Y 13 | a1,b3,c3,d2,Y 14 | a2,b2,c1,d1,N 15 | a2,b2,c3,d1,N 16 | a2,b3,c1,d2,Y 17 | a2,b3,c3,d2,Y 18 | a3,b2,c1,d2,N 19 | a3,b2,c3,d2,N 20 | a2,b2,c3,d2,Y 21 | a2,b2,c2,d2,Y 22 | a1,b2,c2,d1,Y 23 | a1,b2,c3,d1,Y 24 | a1,b1,c1,d2,Y 25 | a3,b2,c2,d1,N 26 | -------------------------------------------------------------------------------- /3.决策树/data/2.csv: -------------------------------------------------------------------------------- 1 | Education,Sex,Language,Character,Job,Class 2 | undergraduate,man,cet4,a1,b1,Y 3 | undergraduate,woman,cet6,a3,b1,N 4 | undergraduate,man,cet6,a2,b2,Y 5 | undergraduate,man,cet4,a1,b3,Y 6 | undergraduate,woman,cet4,a2,b2,Y 7 | undergraduate,man,no,a3,b3,Y 8 | undergraduate,woman,cet4,a2,b3,N 9 | undergraduate,woman,cet4,a1,b1,Y 10 | college,man,no,a1,b1,N 11 | college,man,cet4,a2,b2,Y 12 | college,woman,cet4,a3,b3,Y 13 | college,woman,no,a3,b3,Y 14 | postgraduate,man,cet6,a2,b2,Y 15 | postgraduate,woman,cet6,a2,b2,Y 16 | postgraduate,man,cet4,a1,b3,N 17 | postgraduate,woman,cet6,a1,b1,Y 18 | 19 | -------------------------------------------------------------------------------- /.idea/expert-system.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /3.决策树/main.py: -------------------------------------------------------------------------------- 1 | import trees 2 | import pandas as pd 3 | 4 | df = pd.read_csv('data/1.csv') 5 | col = df.columns.tolist() 6 | data = df.values.tolist() 7 | tree = trees.createTree(data, col) 8 | print(tree) 9 | 10 | col = df.columns.tolist()[:-1] 11 | input1 = ['a3', 'b1', 'c2', 'd1'] # N 12 | input2 = ['a3', 'b3', 'c1', 'd1'] # N 13 | input3 = ['a2', 'b1', 'c1', 'd2'] # Y 14 | input4 = ['a1', 'b1', 'c1', 'd1'] # Y 15 | print(trees.classify(tree, col, input1)) 16 | print(trees.classify(tree, col, input2)) 17 | print(trees.classify(tree, col, input3)) 18 | print(trees.classify(tree, col, input4)) 19 | 20 | df = pd.read_csv('data/2.csv') 21 | col = df.columns.tolist() 22 | data = df.values.tolist() 23 | tree = trees.createTree(data, col) 24 | print(tree) 25 | 26 | col = df.columns.tolist()[:-1] 27 | input1 = ['undergraduate', 'man', 'cet6', 'a1', 'b1'] # N 28 | input2 = ['undergraduate', 'man', 'cet4', 'a1', 'b1'] # Y 29 | input3 = ['postgraduate', 'man', 'no', 'a1', 'b2'] # Y 30 | input4 = ['undergraduate', 'man', 'no', 'a1', 'b3'] # Y 31 | input5 = ['undergraduate', 'man', 'no', 'a3', 'b3'] # Y 32 | print(trees.classify(tree, col, input1)) 33 | print(trees.classify(tree, col, input2)) 34 | print(trees.classify(tree, col, input3)) 35 | print(trees.classify(tree, col, input4)) 36 | print(trees.classify(tree, col, input5)) 37 | -------------------------------------------------------------------------------- /3.决策树/README.md: -------------------------------------------------------------------------------- 1 | # 决策树 2 | 3 | ## 第一题 4 | 5 | #### 数据集 6 | {DQ, JZZY, YJZJ, YJHF} 7 | 1. 所在地区(DQ) Value(DQ) = {a1, a2, a3} 8 | 2. 机主职业(JZZY) Value(JZZY) = {b1, b2, b3} 9 | 3. 月均主叫次数范围(YJZJ) Value(YJZJ) = {c1, c2, c3} 10 | 4. 月均话费范围(YJHF) Value(YJHF) = {d1, d2} 11 | 5. 分类结果(Class) Value(Class) = {能发展, 不能发展} => {Y, N} 12 | 13 | #### ID3树 14 | ``` 15 | {'DQ': {'a2': {'YJHF': {'d1': 'N', 'd2': 'Y'}}, 'a3': {'JZZY': {'b1': {'YJZJ': {'c2': 'N', 'c1': 'Y'}}, 'b2': 'N', 'b3': 'N'}}, 'a1': 'Y'}} 16 | ``` 17 | 18 | #### 可视化结果 19 | ![alt text](data/1.png "title") 20 | 21 | 22 | ## 第二题 23 | 24 | #### 数据集 25 | {Education, Sex, Language, Character, Job} 26 | 1. 学历(Education) Value(Education) = {研究生, 本科, 专科} => {postgraduate, undergraduate, college} 27 | 2. 性别(Sex) Value(Sex) = {男, 女} => {man, woman} 28 | 3. 外语水平(Language) Value(Language) = {4级以下, 4级, 6级} => {no, cet4, cet6} 29 | 4. 性格特征(Character) Value(Character) = {a1, a2, a3} 30 | 5. 岗位性质(Job) Value(Job) = {b1, b2, b3} 31 | 6. 分类结果(Class) Value(Class) = {称职, 基本称职或不称职} => {Y, N} 32 | 33 | #### ID3树 34 | ``` 35 | {'Job': {'b3': {'Character': {'a2': 'N', 'a3': 'Y', 'a1': {'Education': {'postgraduate': 'N', 'undergraduate': 'Y'}}}}, 'b1': {'Language': {'cet4': 'Y', 'no': 'N', 'cet6': {'Education': {'postgraduate': 'Y', 'undergraduate': 'N'}}}}, 'b2': 'Y'}} 36 | ``` 37 | 38 | #### 可视化结果 39 | ![alt text](data/1.png "title") 40 | -------------------------------------------------------------------------------- /2.推理机/README.md: -------------------------------------------------------------------------------- 1 | # 推理机 2 | 3 | 1. 推理机 4 | https://zh.wikipedia.org/wiki/推理机 5 | 6 | 2. 正向推理机 7 | https://en.wikipedia.org/wiki/Forward_chaining 8 | 9 | 3. 反向推理机 10 | https://en.wikipedia.org/wiki/Backward_chaining 11 | 12 | ## 数据集 13 | ![alt text](data/topo.png "title") 14 | 1. 有毛的动物是哺乳类; 15 | 2. 有奶的动物是哺乳类; 16 | 3. 有羽毛的动物是鸟类; 17 | 4. 若动物会飞且会生蛋,则它是鸟类; 18 | 5. 吃肉的哺乳类是肉食动物; 19 | 6. 犬牙利爪,眼睛向前的是肉食动物; 20 | 7. 反刍食物的哺乳类是偶蹄类; 21 | 8. 有蹄的哺乳类是有蹄类; 22 | 9. 黄褐色有暗斑点的肉食类是金钱豹; 23 | 10. 黄褐色有黑条纹的肉食类是老虎; 24 | 11. 长腿长脖子有黄褐色暗斑的有蹄类是长颈鹿; 25 | 12. 有黑白条纹的有蹄类是斑马; 26 | 13. 不会飞长腿长脖子的鸟是鸵鸟; 27 | 14. 不会飞善游泳黑白色的鸟是企鹅; 28 | 15. 善飞的鸟是信天翁。 29 | 30 | ## 伪代码:课本上的说明,并不适用于Python 31 | ``` 32 | # 1. recall 33 | # (recall fact) 34 | # 判断变量fact中的一个事实是否在表facts中,若是,recall返回值是fact中的事实;否则,返回nil 35 | def recall(fact): 36 | pass 37 | 38 | 39 | # 2. test-if 40 | # (test-if rule) 41 | # 判断变量rule中的一条规则的前件包含的全部事实是否在表facts中,若是,test-if返回t;否则,返回nil 42 | def test_if(rule): 43 | pass 44 | 45 | 46 | # 3. remember 47 | # (remember new) 48 | # 判断变量new中的一个事实是否在表facts中,若是,remember返回nil;否则,将new中的事实添加到表facts的表头, 49 | # 且remember返回new中的事实 50 | def remember(new): 51 | pass 52 | 53 | 54 | # 4. use-then 55 | # (use-then rule) 56 | # 判断变量rule中的一条规则的后件包含的全部结论是否在表facts中,若全部结论都在facts中,则use-then返回nil; 57 | # 否则,将不在facts中的结论逐一添加到表facts中,且use-then返回t 58 | def use_then(rule): 59 | pass 60 | 61 | 62 | # 5. try-rule 63 | # (try-raw rule) 64 | # 判断规则变量rule中的一条规则的前件包含的全部事实是否在表facts中,若全部事实都在facts中,且规则后件有不在facts中的结论, 65 | # 则把不在facts中的结论逐一添加到表facts中,try-rule返回t;否则,try-rule返回nil 66 | def try_rule(rule): 67 | pass 68 | 69 | 70 | # 6. step-forward 71 | # (step-forward rules) 72 | # 逐次扫描规则库rules中的规则,若发现rules中有一条可用规则,即该规则的前件包含的全部事实在表facts中, 73 | # 则把该规则的后件中不在facts中的所有结论添加到facts中,且step-forward返回t; 74 | # 若rules中没有一条可用规则,则step-forward返回nil 75 | def step_forward(rules): 76 | pass 77 | 78 | 79 | # 7. deduce 80 | # (deduce facts) 81 | # 连续不断地从规则库rules中选择可用规则,每选择到一条可用规则,则把该规则的后件中不在facts中的所有结论添加到facts中, 82 | # 对facts扩充,由扩充了的facts来选择下一条可用规则对facts再次扩充,直至没有可用规则可选为止。 83 | # 若曾找到一条可用规则对facts进行过一次扩充,则deduce返回t;否则,deduce返回nil 84 | def deduce(facts): 85 | pass 86 | ``` 87 | -------------------------------------------------------------------------------- /2.推理机/data/topo.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /2.推理机/back.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | class Rule(): 5 | def __init__(self, feature, fact): 6 | self.feature = feature 7 | self.fact = fact 8 | 9 | 10 | # 加载规则 11 | def load_rule(path): 12 | P_list = [] 13 | Q_list = [] 14 | with open(path, 'r') as f: 15 | lines = f.readlines() 16 | for line in lines: 17 | if line: 18 | pro = line.strip().split(' ') 19 | P_list.append(pro[:-1]) 20 | Q_list.append(pro[-1]) 21 | return P_list, Q_list 22 | 23 | 24 | # 添加规则 25 | def add_rule(k, v, rules): 26 | for rule in rules: 27 | if set(k) == set(rule.feature): 28 | print('特征已在数据库') 29 | return 30 | rules.append(Rule(k, v)) 31 | print('特征成功添加 >>> %s ----> %s' % (k, v)) 32 | 33 | 34 | # 删除规则 35 | def del_rule(k, rules): 36 | for rule in rules: 37 | if set(k) == set(rule.feature): 38 | rules.remove(rule) 39 | print('特征成功删除 >>> %s' % k) 40 | return 41 | print('特征不在数据库') 42 | 43 | 44 | # 拓扑排序 45 | def topological(rules): 46 | # 规则 47 | topo_rules = [] 48 | P_list = [rule.feature for rule in rules] 49 | Q_list = [rule.fact for rule in rules] 50 | 51 | # 计算入度 52 | ind_list = [] 53 | for i in P_list: 54 | sum = 0 55 | for x in i: 56 | if Q_list.count(x) > 0: 57 | sum += Q_list.count(x) 58 | ind_list.append(sum) 59 | 60 | # 拓扑排序 61 | while True: 62 | if ind_list.count(-1) == len(ind_list): 63 | break 64 | 65 | for i, ind in enumerate(ind_list): 66 | if ind == 0: 67 | topo_rules.append(Rule(P_list[i], Q_list[i])) 68 | ind_list[i] = -1 69 | # 更新入度 70 | for j, P in enumerate(P_list): 71 | if Q_list[i] in P: 72 | ind_list[j] -= 1 73 | return topo_rules 74 | 75 | 76 | # 反向推理机 77 | def infer(facts, topo_rules): 78 | flag = False 79 | for rule in topo_rules: 80 | # 如果所有规则都在输入中 81 | if list_in_set(rule.feature, facts): 82 | # 添加推导出的条件 83 | facts.append(rule.fact) 84 | flag = True 85 | print('{key} ----> {value}\n'.format(key=rule.feature, value=rule.fact)) 86 | 87 | if flag: 88 | print('最终推出 ----> %s' % facts[-1]) 89 | else: 90 | print('无法推出') 91 | 92 | 93 | # 判断list中所有元素是否都在集合set中 94 | def list_in_set(list, set): 95 | for i in list: 96 | if i not in set: 97 | return False 98 | return True 99 | 100 | 101 | if __name__ == '__main__': 102 | rules = [] 103 | P_list, Q_list = load_rule('data/data.txt') 104 | for k, v in zip(P_list, Q_list): 105 | add_rule(k, v, rules) 106 | topo_rules = topological(rules) 107 | 108 | while True: 109 | choice = input("请输入选择:'test/add/del/show/exit',默认'test' >>> ") 110 | if choice == 'add': 111 | k = input('请输入添加的特征组合:').strip().split(' ') 112 | v = input('请输入添加的结论:') 113 | add_rule(k, v, rules) 114 | topo_rules = topological(rules) # 与正向不同,每次都要更新 115 | 116 | elif choice == 'del': 117 | k = input('请输入删除的特征组合:').strip().split(' ') 118 | del_rule(k, rules) 119 | topo_rules = topological(rules) # 与正向不同,每次都要更新 120 | 121 | elif choice == 'show': 122 | for rule in rules: 123 | print('%s ----> %s' % (rule.feature, rule.fact)) 124 | 125 | elif choice == 'exit': 126 | exit(0) 127 | 128 | else: 129 | k = input('请输入测试的特征组合:').strip().split(' ') 130 | infer(k, topo_rules) 131 | -------------------------------------------------------------------------------- /3.决策树/trees.py: -------------------------------------------------------------------------------- 1 | from math import log 2 | import operator 3 | 4 | 5 | # 计算信息熵 6 | def calcShannonEnt(dataSet): 7 | numEntries = len(dataSet) 8 | labelCounts = {} 9 | for featVec in dataSet: 10 | currentLabel = featVec[-1] 11 | if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 12 | labelCounts[currentLabel] += 1 13 | shannonEnt = 0.0 14 | for key in labelCounts: 15 | prob = float(labelCounts[key]) / numEntries 16 | shannonEnt -= prob * log(prob, 2) # H=−∑p(xi)log(p(xi)) 17 | return shannonEnt 18 | 19 | # 按照给定特征划分数据集,选择所占列中等于选择值的项 20 | def splitDataSet(dataSet, axis, value): 21 | retDataSet = [] 22 | for featVec in dataSet: 23 | if featVec[axis] == value: 24 | reducedFeatVec = featVec[:axis] # 左边一个 25 | reducedFeatVec.extend(featVec[axis + 1:]) # 右边一个 26 | retDataSet.append(reducedFeatVec) 27 | return retDataSet 28 | 29 | 30 | # 寻找最佳特征,用来划分数据集 31 | def chooseBestFeatureToSplit(dataSet): 32 | numFeatures = len(dataSet[0]) - 1 # 获取特征数量 33 | baseEntropy = calcShannonEnt(dataSet) # 计算数据集的熵 34 | bestInfoGain = 0.0 35 | bestFeature = -1 36 | for i in range(numFeatures): # 遍历所有特征 37 | featList = [example[i] for example in dataSet] # 获取当前特征列表 38 | uniqueVals = set(featList) 39 | newEntropy = 0.0 40 | for value in uniqueVals: # 遍历当前特征列表 41 | subDataSet = splitDataSet(dataSet, i, value) 42 | prob = len(subDataSet) / float(len(dataSet)) 43 | newEntropy += prob * calcShannonEnt(subDataSet) # 计算特征对数据集的熵 44 | infoGain = baseEntropy - newEntropy # 计算信息增益 45 | if infoGain > bestInfoGain: 46 | bestInfoGain = infoGain # 选择信息增益最大的特征 47 | bestFeature = i 48 | return bestFeature 49 | 50 | 51 | # 辅助函数:采用多数判决的方法决定该节点的标签 52 | def majorityCnt(classList): 53 | classCount = {} 54 | for vote in classList: 55 | if vote not in classCount.keys(): classCount[vote] = 0 56 | classCount[vote] += 1 57 | sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) 58 | return sortedClassCount[0][0] 59 | 60 | 61 | # ID3 62 | def createTree(dataSet, labels): 63 | classList = [example[-1] for example in dataSet] # 获取标签列表 64 | if classList.count(classList[0]) == len(classList): # 若数据集中所有实例属于同一类Ck,则为单节点树,并将Ck作为该节点的类标记 65 | return classList[0] 66 | if len(dataSet[0]) == 0: # 若特征集为空集,则为单节点树,并将数据集中实例数最大的类Ck作为该节点的类标记 67 | return majorityCnt(classList) # 返回占多数的标签 68 | bestFeat = chooseBestFeatureToSplit(dataSet) # 寻找最佳特征,用来划分数据集 69 | bestFeatLabel = labels[bestFeat] 70 | myTree = {bestFeatLabel: {}} 71 | del (labels[bestFeat]) 72 | featValues = [example[bestFeat] for example in dataSet] # 获取最佳特征列表 73 | uniqueVals = set(featValues) # 特征去重 74 | for value in uniqueVals: # 对每个最佳特征,分割成若干个非空子集 75 | subLabels = labels[:] 76 | 77 | """ 78 | [a1, b1, c2] 79 | [a2, b1, c3] 80 | 81 | [a3, b2, c1] 82 | [a1, b2, c2] 83 | 84 | [a2, b3, c3] 85 | [a3, b3, c1] 86 | 87 | 假设b为最佳特征,对每个最佳特征,分割成若干个非空子集====> 88 | 89 | b1: [a1, c2], [a2, c3] 90 | b2: [a3, c1], [a1, c2] 91 | b3: [a2, c3], [a3, c1] 92 | """ 93 | 94 | myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels) # 递归创建子树 95 | return myTree 96 | 97 | 98 | # 分类 99 | def classify(inputTree, featLabels, testVec): 100 | firstStr = list(inputTree.keys())[0] 101 | secondDict = inputTree[firstStr] 102 | featIndex = featLabels.index(firstStr) 103 | key = testVec[featIndex] 104 | valueOfFeat = secondDict[key] 105 | if isinstance(valueOfFeat, dict): # 递归遍历子树 106 | classLabel = classify(valueOfFeat, featLabels, testVec) 107 | else: # 直到叶子节点 108 | classLabel = valueOfFeat 109 | return classLabel 110 | -------------------------------------------------------------------------------- /2.推理机/forward.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | """ 5 | 正向推理(数据驱动),其基本思想是: 6 | 1. 从问题已有的事实(初始证据)出发,正向使用规则, 7 | 2. 当规则的条件部分与已有的事实匹配时,就把该规则作为可用规则放入候选规则队列中, 8 | 3. 然后通过冲突消解,在候选队列中选择一条规则作为启用规则进行推理,并将其结论放入数据库中,作为下一步推理时的证据。 9 | 4. 如此重复这个过程,直到再无可用规则可被选用或者求得了所要求的解为止。 10 | 11 | 冲突消解: 12 | 1. 优先度排序。事先给知识库中每条规则设定优先度参数,优先度高的规则先执行。 13 | 2. 规则的条件详细度排序。条件较多、较详细的规则,其结论一般更接近于目标,优先执行。 14 | 3. 匹配度排序。事先给知识库中每条规则设定匹配度参数,匹配度高的规则先执行。 15 | 4. 根据领域问题的特点排序。根据领域知识可以知道的某些特点,事先设定知识库中的规则的使用顺序。 16 | """ 17 | 18 | 19 | class Rule(): 20 | def __init__(self, feature, fact): 21 | self.feature = feature 22 | self.fact = fact 23 | 24 | 25 | # 加载规则 26 | def load_rule(path): 27 | P_list = [] 28 | Q_list = [] 29 | with open(path, 'r') as f: 30 | lines = f.readlines() 31 | for line in lines: 32 | if line: 33 | pro = line.strip().split(' ') 34 | P_list.append(pro[:-1]) 35 | Q_list.append(pro[-1]) 36 | return P_list, Q_list 37 | 38 | 39 | # 添加规则 40 | def add_rule(k, v, rules): 41 | for rule in rules: 42 | if set(k) == set(rule.feature): 43 | print('特征已在数据库') 44 | return 45 | rules.append(Rule(k, v)) 46 | print('特征成功添加 >>> %s ----> %s' % (k, v)) 47 | 48 | 49 | # 删除规则 50 | def del_rule(k, rules): 51 | for rule in rules: 52 | if set(k) == set(rule.feature): 53 | rules.remove(rule) 54 | print('特征成功删除 >>> %s' % k) 55 | return 56 | print('特征不在数据库') 57 | 58 | 59 | # 冲突消解 60 | def resolve(candidate_rule, method=1): 61 | if method == 1: # 1. 最长匹配策略 62 | return max(candidate_rule, key=lambda x: len(x.feature)) 63 | elif method == 2: # 2. 最早匹配策略 64 | return candidate_rule[0] 65 | elif method == 3: # 3. 最晚匹配策略 66 | return candidate_rule[-1] 67 | else: 68 | exit(0) 69 | 70 | 71 | # 正向推理机 72 | def infer(facts, rules): 73 | candidate_rule = [] 74 | visited_rule = [] 75 | flag = False 76 | 77 | while True: 78 | candidate_rule.clear() 79 | # 1. 从问题已有的事实(初始证据)出发,正向使用规则 80 | for rule in rules: 81 | # 2. 当规则的条件部分与已有的事实匹配时,就把该规则作为可用规则放入候选规则队列中 82 | if list_in_set(rule.feature, facts) and rule not in visited_rule: 83 | candidate_rule.append(rule) 84 | visited_rule.append(rule) 85 | print('规约:{key} ----> {value}'.format(key=rule.feature, value=rule.fact)) 86 | 87 | # 3. 然后通过冲突消解,在候选队列中选择一条规则作为启用规则进行推理,并将其结论放入数据库中,作为下一步推理时的证据 88 | if len(candidate_rule) != 0: 89 | result = resolve(candidate_rule) 90 | facts.append(result.fact) 91 | flag = True 92 | print('冲突消解:{key} ====> {value}\n'.format(key=result.feature, value=result.fact)) 93 | # 4. 如此重复这个过程,直到再无可用规则可被选用或者求得了所要求的解为止 94 | else: 95 | break 96 | 97 | if flag: 98 | print('最终推出 ----> %s' % facts[-1]) 99 | else: 100 | print('无法推出') 101 | 102 | 103 | # 判断list中所有元素是否都在集合set中 104 | def list_in_set(list, set): 105 | for i in list: 106 | if i not in set: 107 | return False 108 | return True 109 | 110 | 111 | if __name__ == '__main__': 112 | rules = [] 113 | P_list, Q_list = load_rule('data/data.txt') 114 | for k, v in zip(P_list, Q_list): 115 | add_rule(k, v, rules) 116 | 117 | while True: 118 | choice = input("请输入选择:'test/add/del/show/exit',默认'test' >>> ") 119 | if choice == 'add': 120 | k = input('请输入添加的特征组合:').strip().split(' ') 121 | v = input('请输入添加的结论:') 122 | add_rule(k, v, rules) 123 | 124 | elif choice == 'del': 125 | k = input('请输入删除的特征组合:').strip().split(' ') 126 | del_rule(k, rules) 127 | 128 | elif choice == 'show': 129 | for rule in rules: 130 | print('%s ----> %s' % (rule.feature, rule.fact)) 131 | 132 | elif choice == 'exit': 133 | exit(0) 134 | 135 | else: 136 | k = input('请输入测试的特征组合:').strip().split(' ') 137 | infer(k, rules) 138 | -------------------------------------------------------------------------------- /1.井字棋/game.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import random 5 | from abc import ABCMeta, abstractmethod 6 | 7 | ROW = COL = 3 # 棋盘大小 8 | SPACE = '-' # 空格标签 9 | HUMAN = 'HUMAN' # 人类棋子标签 10 | COMPUTER = 'COMPUTER' # 电脑棋子标签 11 | 12 | 13 | # 棋盘是否有空位 14 | def empty(board): 15 | if board.count(SPACE) == 0: 16 | return False 17 | else: 18 | return True 19 | 20 | 21 | # 判断player是否胜利 22 | def winner(board, player): 23 | """ 24 | -------------- 25 | | 0 || 1 || 2 | 26 | | 3 || 4 || 5 | 27 | | 6 || 7 || 8 | 28 | -------------- 29 | 获胜的算法: 30 | 012 345 678 31 | 036 147 258 32 | 048 246 33 | """ 34 | wins = [[board[0], board[1], board[2]], [board[3], board[4], board[5]], [board[6], board[7], board[8]], 35 | [board[0], board[3], board[6]], [board[1], board[4], board[7]], [board[2], board[5], board[8]], 36 | [board[0], board[4], board[8]], [board[2], board[4], board[6]]] 37 | state = [player, player, player] 38 | if state in wins: 39 | return True 40 | else: 41 | return False 42 | 43 | 44 | # 定义抽象基类 45 | class Player(metaclass=ABCMeta): 46 | 47 | def __init__(self, chess): 48 | self.chess = chess 49 | 50 | @abstractmethod 51 | def move(self): 52 | pass 53 | 54 | 55 | class Computer(Player): 56 | 57 | def __init__(self, chess='O'): 58 | Player.__init__(self, chess) 59 | 60 | def minimax(self, board, player, next_player, alpha=-2, beta=2): 61 | if winner(board, COMPUTER): # 电脑胜利 62 | return +1 63 | if winner(board, HUMAN): # 人类胜利 64 | return -1 65 | elif not empty(board): 66 | return 0 # 平局 67 | 68 | for move in range(ROW * COL): 69 | if board[move] == SPACE: # 尝试下棋 70 | board[move] = player # 记录 71 | val = self.minimax(board, next_player, player, alpha, beta) # 继续思考对手怎么下棋 72 | board[move] = SPACE # 重置 73 | 74 | if player == COMPUTER: # 极大 max value 75 | if val > alpha: 76 | alpha = val 77 | if alpha >= beta: # 剪枝 78 | return beta 79 | else: # 极小 min value 80 | if val < beta: 81 | beta = val 82 | if beta <= alpha: # 剪枝 83 | return alpha 84 | 85 | if player == COMPUTER: 86 | return alpha 87 | else: 88 | return beta 89 | 90 | def move(self, board): 91 | best = -2 92 | my_moves = [] 93 | for move in range(ROW * COL): 94 | if board[move] == SPACE: # 尝试下棋 95 | board[move] = COMPUTER # 记录 96 | score = self.minimax(board, HUMAN, COMPUTER) # 思考对手怎么下棋 97 | board[move] = SPACE # 重置 98 | 99 | if score > best: # 找到更优的位置 100 | best = score 101 | my_moves = [move] 102 | if score == best: # 一样优秀的位置 103 | my_moves.append(move) 104 | 105 | pos = random.choice(my_moves) # 随机挑出一个位置 106 | board[pos] = COMPUTER 107 | 108 | 109 | class Human(Player): 110 | 111 | def __init__(self, chess='X'): 112 | Player.__init__(self, chess) 113 | 114 | def move(self, board): 115 | looping = True 116 | while looping: 117 | try: 118 | inp = input("请输入下棋位置[1-9]:") 119 | pos = int(inp) - 1 # 输入的下标从1开始 120 | if 0 <= pos <= 8: 121 | if board[pos] == SPACE: 122 | looping = False 123 | else: 124 | print("此处不允许下棋") 125 | else: 126 | print("输入不合法:[1-9]") 127 | except: 128 | print("输入不合法:Error") 129 | 130 | board[pos] = HUMAN 131 | 132 | 133 | class Game: 134 | 135 | def __init__(self): 136 | # 初始化游戏 137 | self.board = [SPACE] * (ROW * COL) 138 | self.computer = Computer() 139 | self.human = Human() 140 | 141 | choice = input("请选择棋子类型:[X/O],默认'X' >>> ") 142 | if choice == 'O': 143 | self.computer.chess = 'X' 144 | self.human.chess = 'O' 145 | else: 146 | self.computer.chess = 'O' 147 | self.human.chess = 'X' 148 | 149 | choice = input("请选择是否先手:[T/F],默认'T' >>> ") 150 | if choice == 'F': 151 | self.current_player = self.computer 152 | else: 153 | self.current_player = self.human 154 | 155 | # 切换玩家 156 | def switch(self): 157 | if self.current_player == self.computer: 158 | self.current_player = self.human 159 | else: 160 | self.current_player = self.computer 161 | 162 | # 渲染游戏 163 | def render(self): 164 | print('--------------') 165 | for i in range(ROW): 166 | for j in range(COL): 167 | k = i * ROW + j 168 | if self.board[k] == HUMAN: 169 | print('|', self.human.chess, '|', end='') 170 | elif self.board[k] == COMPUTER: 171 | print('|', self.computer.chess, '|', end='') 172 | else: 173 | print('|', self.board[k], '|', end='') 174 | print() 175 | print('--------------') 176 | 177 | # 开始游戏 178 | def start(self): 179 | # 渲染游戏 180 | self.render() 181 | 182 | # 游戏状态机 183 | while True: 184 | self.current_player.move(self.board) 185 | self.render() 186 | 187 | if winner(self.board, HUMAN): 188 | print("人类胜利!!!") 189 | exit(0) 190 | elif winner(self.board, COMPUTER): 191 | print("电脑胜利!!!") 192 | exit(0) 193 | elif not empty(self.board): 194 | print("平局!!!") 195 | exit(0) 196 | 197 | # 切换玩家 198 | self.switch() 199 | 200 | 201 | if __name__ == '__main__': 202 | Game().start() 203 | --------------------------------------------------------------------------------