【发布时间】:2014-04-12 16:55:23
【问题描述】:
我正在尝试减少使用两个矩阵执行一系列计算的函数的时间。搜索这个,我听说过 numpy,但我真的不知道如何将它应用于我的问题。另外,我认为让我的功能变慢的原因之一是有很多点运算符(我在this page 中听说过)。
数学对应于二次分配问题的因式分解:
我的代码是:
delta = 0
for k in xrange(self._tam):
if k != r and k != s:
delta +=
self._data.stream_matrix[r][k] \
* (self._data.distance_matrix[sol[s]][sol[k]] - self._data.distance_matrix[sol[r]][sol[k]]) + \
self._data.stream_matrix[s][k] \
* (self._data.distance_matrix[sol[r]][sol[k]] - self._data.distance_matrix[sol[s]][sol[k]]) + \
self._data.stream_matrix[k][r] \
* (self._data.distance_matrix[sol[k]][sol[s]] - self._data.distance_matrix[sol[k]][sol[r]]) + \
self._data.stream_matrix[k][s] \
* (self._data.distance_matrix[sol[k]][sol[r]] - self._data.distance_matrix[sol[k]][sol[s]])
return delta
在大小为 20(20x20 的矩阵)的问题上运行这个大约需要 20 段,瓶颈在这个函数中
ncalls tottime percall cumtime percall filename:lineno(function)
303878 15.712 0.000 15.712 0.000 Heuristic.py:66(deltaC)
我尝试将map 应用于for循环,但由于循环体不是函数调用,所以不可能。
我怎样才能减少时间?
编辑1
回答艾肯伯格的评论:
sol 是一个排列,例如 [1,2,3,4]。当我生成邻居解决方案时调用该函数,因此,[1,2,3,4] 的邻居是 [2,1,3,4]。我只更改原始排列中的两个位置,然后调用deltaC,它计算位置 r,s 交换的解的因式分解(在上面的示例中 r,s = 0,1)。进行这种排列是为了避免计算邻居解决方案的全部成本。我想我可以将sol[k,r,s] 的值存储在一个局部变量中,以避免在每次迭代中查找它的值。 我不知道这是否是你在评论中提出的问题。
编辑2
一个最小的工作示例:
import random
distance_matrix = [[0, 12, 6, 4], [12, 0, 6, 8], [6, 6, 0, 7], [4, 8, 7, 0]]
stream_matrix = [[0, 3, 8, 3], [3, 0, 2, 4], [8, 2, 0, 5], [3, 4, 5, 0]]
def deltaC(r, s, S=None):
'''
Difference between C with values i and j swapped
'''
S = [0,1,2,3]
if S is not None:
sol = S
else:
sol = S
delta = 0
sol_r, sol_s = sol[r], sol[s]
for k in xrange(4):
if k != r and k != s:
delta += (stream_matrix[r][k] \
* (distance_matrix[sol_s][sol[k]] - distance_matrix[sol_r][sol[k]]) + \
stream_matrix[s][k] \
* (distance_matrix[sol_r][sol[k]] - distance_matrix[sol_s][sol[k]]) + \
stream_matrix[k][r] \
* (distance_matrix[sol[k]][sol_s] - distance_matrix[sol[k]][sol_r]) + \
stream_matrix[k][s] \
* (distance_matrix[sol[k]][sol_r] - distance_matrix[sol[k]][sol_s]))
return delta
for _ in xrange(303878):
d = deltaC(random.randint(0,3), random.randint(0,3))
print d
现在我认为更好的选择是使用 NumPy。我尝试使用 Matrix(),但没有提高性能。
找到最佳解决方案
好吧,最后我能够减少更多的时间,结合@TooTone 的解决方案并将索引存储在一个集合中以避免 if。时间已经从大约 18 秒下降到 8 秒。代码如下:
def deltaC(self, r, s, sol=None):
delta = 0
sol = self.S if sol is None else self.S
sol_r, sol_s = sol[r], sol[s]
stream_matrix = self._data.stream_matrix
distance_matrix = self._data.distance_matrix
indexes = set(xrange(self._tam)) - set([r, s])
for k in indexes:
sol_k = sol[k]
delta += \
(stream_matrix[r][k] - stream_matrix[s][k]) \
* (distance_matrix[sol_s][sol_k] - distance_matrix[sol_r][sol_k]) \
+ \
(stream_matrix[k][r] - stream_matrix[k][s]) \
* (distance_matrix[sol_k][sol_s] - distance_matrix[sol_k][sol_r])
return delta
为了进一步减少时间,我认为最好的方法是编写一个模块。
【问题讨论】:
-
你应该看看 numpy 以优化数值计算。它是一个成熟的库,正是为此而设计的。而且他们的代码几乎总是比您自己编写的代码更优化。
-
第一次尝试应该总是尝试向量化你的 numpy 操作。到目前为止,您的代码对于 numpy 来说是次优的:使用 for 循环并查找例如
sol[s]每次迭代,尽管它保持不变。在尝试提出解决方案之前,如果您能告诉我们是否必须对所有r, s执行此操作以及sol是否是索引的固定排列,那就太好了。如果矢量化不起作用(但它应该),那么您可以查看编译数值表达式,使用例如numexpr,但我会在以后保留它 -
另外,你能告诉我们你想在哪个维度上使用它吗?
-
谢谢,我试着回答你编辑我原来的问题。
-
如果
self._data.stream_matrix和self._data.distance_matrix是一个 numpy 矩阵类,它会更有效吗?
标签: python numpy matrix heuristics