Python 在自己的函数中使用所有索引优化列表理解性能
我有一个多维矩阵(6D),我需要迭代以生成一个新的6D矩阵。现在我使用列表理解来使代码尽可能干净,但是它确实很小。我希望有一些内置的numpy函数来帮助我,但是由于列表中使用了自己的函数,所以很难找到这样的函数 我已经尝试了np.fromIter,但这是错误的,因为我使用了多维列表。allReachableCoords(x1,y1,len(Q1),len(Q1[0])返回一组所有周围坐标({(x1,y1),(x1+1,y1),(x1,y1+1)…]),World.amountOfPossibleActions只返回5 算法从Python 在自己的函数中使用所有索引优化列表理解性能,python,numpy,numpy-ndarray,Python,Numpy,Numpy Ndarray,我有一个多维矩阵(6D),我需要迭代以生成一个新的6D矩阵。现在我使用列表理解来使代码尽可能干净,但是它确实很小。我希望有一些内置的numpy函数来帮助我,但是由于列表中使用了自己的函数,所以很难找到这样的函数 我已经尝试了np.fromIter,但这是错误的,因为我使用了多维列表。allReachableCoords(x1,y1,len(Q1),len(Q1[0])返回一组所有周围坐标({(x1,y1),(x1+1,y1),(x1,y1+1)…]),World.amountOfPossible
Q1 = np.zeros((heightWorld, widthWorld, heightWorld, widthWorld, world.amountOfPossibleActions,
world.amountOfPossibleActions))
然后将下面的过程重复几次
Q1 = np.array([[[[[[sum(
world.joinedTransition((x1, y1), sf1, (x2, y2), sf2, action1, action2) *
(world.joinedU((x1, y1), sf1, (x2, y2), sf2, action1, action2, player) +
world.joinedU((x1, y1), sf1, (x2, y2), sf2, action2, action1, otherPlayer) +
gamma * np.amax(Q1[sf1[1]][sf1[0]][sf2[1]][sf2[0]]))
for sf1 in World.allReachableCoords(x1, y1, len(Q1), len(Q1[0]), world)
for sf2 in World.allReachableCoords(x2, y2, len(Q1), len(Q1[0]), world)
)
for action1 in range(world.amountOfPossibleActions)]
for action2 in range(world.amountOfPossibleActions)]
for x1 in range(widthWorld)] for y1 in range(heightWorld)]
for x2 in range(widthWorld)] for y2 in range(heightWorld)])
其中,连接的转换主要是一个if语句字符串:
# Transition function: Returns 0 if the final state is out of bounds, impassable terrain or too far from the
# initial state. If the given action explains the relation between si and sf return 1, otherwise 0.
def standardTransition(self, si, sf, a):
if not (0 <= sf[0] <= len(self.grid[0]) and 0 <= sf[1] <= len(self.grid)):
return 0
if not (0 <= si[0] <= len(self.grid[0]) and 0 <= si[1] <= len(self.grid)):
return 0
if self.grid[sf[1]][sf[0]] == self.i or self.grid[si[1]][si[0]] == self.i:
return 0
if abs(si[0] - sf[0]) > 1 or abs(si[1] - sf[1]) > 1:
return 0
return {
0: 1 if sf[0] == si[0] and sf[1] == si[1] else 0, # Stay
1: 1 if sf[0] == si[0] and sf[1] == si[1] + 1 else 0, # Down
2: 1 if sf[0] == si[0] and sf[1] == si[1] - 1 else 0, # Up
3: 1 if sf[0] == si[0] - 1 and sf[1] == si[1] else 0, # Left
4: 1 if sf[0] == si[0] + 1 and sf[1] == si[1] else 0 # Right
}[a]
def joinedTransition(self, si1, sf1, si2, sf2, a1, a2):
if sf1 == sf2: return 0 # Ending in the same square is impossible.
if si1 == sf2 and si2 == sf1: return 0 # Going through each other is impossible.
# Fighting for the same square.
if si1 == sf1 and performAction(si1, a1) == sf2: # Player 1 loses the fight
return self.standardTransition(si1, sf2, a1) * self.standardTransition(si2, sf2,
a2) * self.chanceForPlayer1ToWinDuel
if si2 == sf2 and performAction(si2, a2) == sf1: # Player 2 loses the fight
return self.standardTransition(si1, sf1, a1) * self.standardTransition(si2, sf1, a2) * (
1 - self.chanceForPlayer1ToWinDuel)
return self.standardTransition(si1, sf1, a1) * self.standardTransition(si2, sf2, a2)
#Transition function:如果最终状态为越界、无法通行的地形或距离目标太远,则返回0
#初始状态。如果给定操作解释了si和sf之间的关系,则返回1,否则返回0。
def标准转换(自身、si、sf、a):
如果不是(0测量值:cProfile/line_profiler
加速一个程序的第一步应该始终是测量:你的时间到底花在了什么地方?总会有更快/更整洁的事情,但是如果速度是你主要关心的,那么你应该首先处理代码中最慢的部分
首先,您可以始终使用Python附带的默认探查器。要获得每行代码的更详细视图,我建议查看。虽然设置更复杂一些,但如果时间主要花在操作而不是函数上,它可以为您提供更好的结果
Timeit实验
考虑到我不知道您的代码的任何评测结果,我还注意到了一些其他的事情。在使用python的内置模块运行了一系列小实验之后,这里有一些明确的建议,可以让您的代码更快、更干净,或者两者兼而有之
Numpy索引
提高性能的初始方法可能只是更改索引。在numpy中对数组进行索引时,它似乎返回一个中间对象。因此,每一组新的方括号都是一个新的\uuuuu getitem\uuuuu
函数调用,以及所有相关的开销。这意味着您的Q1[v][w][x][y]
被(某种程度上)转换为
Q1.__getitem__(v).__getitem__(w).__getitem__(x).__getitem__(y)
Numpy本机支持,无需显式生成元组即可使用:
Q1[v][w][x][y] # This is slow
Q1[(v,w,x,y)] # This is faster
Q1[v,w,x,y] # This does the same thing
通过使用后者,您已经可以节省为正在查找的项目编制索引所需时间的一半
$python -m timeit -s "import numpy as np; Q1 = np.empty((9,9,9,9,9,9)); sf=(3,4)" "q = Q1[sf[0]][sf[1]][sf[0]][sf[1]]"
1000000 loops, best of 3: 0.651 usec per loop
$python -m timeit -s "import numpy as np; Q1 = np.empty((9,9,9,9,9,9)); sf=(3,4)" "q = Q1[sf[0],sf[1],sf[0],sf[1]]"
1000000 loops, best of 3: 0.298 usec per loop
i、 e.将np.amax(Q1[sf1[1]][sf1[0]][sf2[1]][sf2[0]])
替换为np.amax(Q1[sf1[1],sf1[0],sf2[1],sf2[0]])
此外,您可以在for循环中解压sf
变量(对于sf1\u 0,sf1\u 1 in…
),从而节省另一段时间:
$python -m timeit -s "import numpy as np; Q1 = np.empty((9,9,9,9,9,9)); sf_0, sf_1=(3,4)" "q = Q1[sf_0,sf_1,sf_0,sf_1]"
1000000 loops, best of 3: 0.216 usec per loop
给出np.amax(Q1[sf1\u 1,sf1\u 0,sf2\u 1,sf2\u 0])
,我认为这也有点干净:)
迭代:itertools.product
您当前正在手动/显式地在范围上循环,但实际上您只有最内层的循环中有一个计算。这意味着在它之外的五个循环中,您所做的只是创建范围
对象并将其耗尽。就性能而言,这不是一个很大的瓶颈,但它不是很干净。幸运的是,内置的itertools library提供的工具正好可以执行以下任务:
# 6 nested loops
$python -m timeit -n 100 -s "a=0" "for u in range(10):"
" for v in range(10):"
" for w in range(10):"
" for x in range(10):"
" for y in range(10):"
" for z in range(10):"
" a+=1"
100 loops, best of 3: 57.3 msec per loop
# itertools.product
$python -m timeit -n 100 -s "from itertools import product; a=0"
"for u,v,w,x,y,z in product(range(10),range(10),range(10),range(10),range(10),range(10)):"
" a+=1"
100 loops, best of 3: 55.6 msec per loop
在本例中,它节省了5个(!)级别的嵌套,甚至速度稍微快了一点。只要在前面创建一次product()
并在以后使用它,就可以更快一点,因为您一直在重复相同的循环。只需确保显式地获取list()
,如product()
如果您尝试使用两次,将返回空的生成器(有关更多信息,请参阅例如)
缓存
在您的内部循环中,您还调用了World
对象的一组方法。如果这些方法的结果根本不依赖于Q1
,那么您肯定要对同一事物重新计算几次。然后您可以用计算时间换取内存:预先计算所有值一次,并将它们存储在另一个numpy数组中。an数组查找几乎肯定比函数调用的计算速度快(很多)
要决定首先在何处执行此操作,您应该参考分析工作的结果;)Measure:cProfile/line\u profiler
加速计划的第一步应该始终是测量:你的时间到底花在哪里?总会有更快/更整洁的事情发生,但是如果速度是您主要关心的问题,那么您需要首先处理代码中最慢的部分
首先,您可以始终使用Python附带的默认探查器。对于每行代码的更详细的视图,我建议查看。虽然设置要复杂一些,但如果时间主要花在操作上而不是功能上,它可以给您带来更好的结果
Timeit实验
考虑到我不知道您的代码的任何评测结果,我还注意到了一些其他的事情。在使用python的内置模块进行了一系列小实验之后,这里有一些明确的建议,可以使代码更快、更干净,或者两者兼而有之
Numpy索引
提高性能的最初方法可能只是更改索引。在numpy中索引数组时,它似乎返回一个中间对象。因此,每一组新的方括号都是一个新的\uuu getitem\uuu
函数调用,以及所有相关的开销。这意味着您的Q1[v][w][x][y]
被(某种程度上)翻译为
Q1.__getitem__(v).__getitem__(w).__getitem__(x).__getitem__(y)
Numpy本机支持,无需显式生成元组即可使用:
Q1[v][w][x][y] # This is slow
Q1[(v,w,x,y)] # This is faster
Q1[v,w,x,y] # This does the same thing
通过使用后者,您已经可以节省为正在查找的项目编制索引所需时间的一半
$python -m timeit -s "import numpy as np; Q1 = np.empty((9,9,9,9,9,9)); sf=(3,4)" "q = Q1[sf[0]][sf[1]][sf[0]][sf[1]]"
1000000 loops, best of 3: 0.651 usec per loop
$python -m timeit -s "import numpy as np; Q1 = np.empty((9,9,9,9,9,9)); sf=(3,4)" "q = Q1[sf[0],sf[1],sf[0],sf[1]]"
1000000 loops, best of 3: 0.298 usec per loop
i、 e.替换np.amax(Q1[sf1[1]]s