Python 极小极大逻辑工作缓慢
在参加哈佛大学的CS-50课程后,我正在使用带有alpha-beta修剪的minimax算法编程Tic-Tac-Toe。 我的逻辑是完美的,除了minimax的返回时间(特别是在第一次呼叫时,只有一个玩家在玩)。我正在考虑将minimax的额外递归调用替换为max/min值。我想强调的是,对于我来说,minimax的速度相对较慢(大约0.5秒)Python 极小极大逻辑工作缓慢,python,cs50,Python,Cs50,在参加哈佛大学的CS-50课程后,我正在使用带有alpha-beta修剪的minimax算法编程Tic-Tac-Toe。 我的逻辑是完美的,除了minimax的返回时间(特别是在第一次呼叫时,只有一个玩家在玩)。我正在考虑将minimax的额外递归调用替换为max/min值。我想强调的是,对于我来说,minimax的速度相对较慢(大约0.5秒) 导入数学 导入副本 导入时间 X=“X” O=“O” 空=无 def初始_状态(): """ 返回板的开始状态。 """ return[[EMPTY,E
导入数学
导入副本
导入时间
X=“X”
O=“O”
空=无
def初始_状态():
"""
返回板的开始状态。
"""
return[[EMPTY,EMPTY,EMPTY],
[空,空,空],
[空,空,空]]
def播放器(板):
"""
返回棋盘上下一回合的玩家。
"""
如果电路板==初始状态():
返回X
X_计数器=0
O_计数器=0
对于板中的行:
对于行中的单元格:
如果单元格==X:
X_计数器+=1
elif单元==O:
O_计数器+=1
如果X_计数器>O_计数器,则返回O,否则返回X
def行动(委员会):
"""
返回板上可用的所有可能操作(i,j)的集合。
"""
可能的移动=设置()
对于范围(3)中的i:
对于范围(3)内的j:
如果董事会[i][j]为无:
可能的移动。添加((i,j))
返回可能的移动
def结果(董事会、行动):
"""
返回在板上移动(i,j)所产生的板。
"""
新板=复制。深度复制(板)
新手板[动作[0]][动作[1]]=玩家(棋盘)
返回新船
def优胜者(董事会):
"""
返回游戏的赢家(如果有)。
"""
如果板[0][0]==板[0][1]==板[0][2]!=无:#1、2、3
返回板[0][0]
如果板[1][0]==板[1][1]==板[1][2]!=无:#4、5、6
返回板[1][0]
如果板[2][0]==板[2][1]==板[2][2]!=无:#7、8、9
返回板[2][0]
如果板[0][0]==板[1][0]==板[2][0]!=无:#1、4、7
返回板[0][0]
如果板[0][1]==板[1][1]==板[2][1]!=无:#2、5、8
返回板[0][1]
如果板[0][2]==板[1][2]==板[2][2]!=无:#3、6、9
返回板[0][2]
如果板[0][0]==板[1][1]==板[2][2]!=无:#1、4、7
返回板[0][0]
如果板[0][2]==板[1][1]==板[2][0]!=无:#3、5、7
返回板[0][2]
一无所获
def终端(板):
"""
如果游戏结束,则返回True,否则返回False。
"""
def检查图(电路板):
对于板中的行:
对于行中的单元格:
如果单元格为无:
返回错误
返回真值
如果获胜者(棋盘)不是无或勾选抽签(棋盘):#或有人赢了,或有抽签
返回真值
返回错误
def实用程序(板):
"""
如果X赢了游戏,则返回1;如果O赢了,则返回1;否则返回0。
"""
临时=优胜者(董事会)
如果温度==X:
返回1
elif温度==O:
返回-1
返回0
def minimax(电路板):
"""
返回棋盘上当前玩家的最佳动作。
"""
选项=操作(板)
如果玩家(棋盘)=X:
vT=-math.inf
move=set()
关于选项中的操作:
v、 count=minvalue(结果(电路板,动作),-math.inf,math.inf,0)
如果v>vT:
vT=v
移动=动作
其他:
vT=math.inf
move=set()
关于选项中的操作:
v、 count=maxvalue(结果(板,动作),-math.inf,math.inf,0)
如果vβ:
打破
返回v,计数+1
def最小值(电路板、alpha、beta、计数):
"""
递归计算给定电路板的最小值和最大值
"""
如果终端(板):返回实用程序(板),计数+1
v=math.inf
posactions=操作(板)
对于posactions中的操作:
vret,count=maxvalue(结果(板、动作)、alpha、beta、count)
v=最小值(v,vret)
β=最小值(v,β)
如果α>β:
打破
返回v,计数+1
而不是:
def player(board):
"""
Returns player who has the next turn on a board.
"""
if board == initial_state():
return X
X_counter = 0
O_counter = 0
for row in board:
for cell in row:
if cell == X:
X_counter += 1
elif cell == O:
O_counter += 1
return O if X_counter > O_counter else X
您可以通过以下方式获得什么样的性能:
def player(board):
"""
Returns player who has the next turn on a board.
"""
counts = collections.Counter(cell for row in board for cell in row if cell)
return O if counts.get(X, 0) > counts.get(O, 0) else X
一个问题可能会出现,为什么提高球员的表现可能会有很大的不同
为了计算第一步,我计算了每个方法被调用的次数:
[('winner', 53673), ('player', 30442), ('result', 30441), ('terminal', 30441), ('utility', 23232), ('maxvalue', 16248), ('actions', 14419), ('minvalue', 14193), ('minimax', 1)]
我们可以看到,player()
经常被调用。下一个候选对象显然是winner()
,因为它比player()
调用得更频繁。我们可以观察到,winner()
仅由terminal()
和utility()
调用。此外,我们可以观察到,terminal()
和utility()
只能一起调用,如下所示:
if terminal(board): return utility(board), count+1
那么,让我们看看我们是否可以做些什么来组合它们
那么:
def terminal(board):
"""
Returns True if game is over, False otherwise.
"""
is_winner = winner(board)
if is_winner == X:
return 1
if is_winner == O:
return -1
if not actions(board):
return 0
return None
我们可以像这样使用它:
who_wins = terminal(board)
if who_wins is not None: return who_wins, count+1
这可以减轻winner()的压力。
:
这比已经缩短的时间减少了25%
在我的系统上,原始代码(平均)第一步需要2.5秒。通过这些简单的更新,相同的推荐移动平均在0.001秒内生成。与其执行
EMPTY=None
,不如只执行None
?哦,这样就有了样板代码了?你能指出哪些零件是给你的,哪些零件是你手上的吗?有几点效率低下
who_wins = terminal(board)
if who_wins is not None: return who_wins, count+1
[('player', 30442), ('result', 30441), ('terminal', 30441), ('winner', 30441), ('maxvalue', 16248), ('actions', 14419), ('minvalue', 14193), ('minimax', 1)]