我有一组轨迹,由沿轨迹的点和每个点的坐标组成。我将它们存储在一个3d数组中(轨迹,点,参数)。我想找到r轨迹的集合,这些轨迹具有这些轨迹的可能成对组合之间的最大累积距离。我认为我的第一次尝试是这样的:
max_dist = 0
for h in itertools.combinations ( xrange(num_traj), r):
for (m,l) in itertools.combinations (h, 2):
accum = 0.
for ( i, j ) in itertools.izip ( range(k), range(k) ):
A = [ (my_mat[m, i, z] - my_mat[l, j, z])**2 \
for z in xrange(k) ]
A = numpy.array( numpy.sqrt (A) ).sum()
accum += A
if max_dist < accum:
selected_trajectories = h
这需要永远,因为num_traj可以是大约500-1000,并且r可以是大约5-20。 k是任意的,但通常可以达到50。
尝试超级聪明,我把所有东西放到两个嵌套的列表推导中,大量使用itertools:
chunk = [[ numpy.sqrt((my_mat[m, i, :] - my_mat[l, j, :])**2).sum() \
for ((m,l),i,j) in \
itertools.product ( itertools.combinations(h,2), range(k), range(k)) ]\
for h in itertools.combinations(range(num_traj), r) ]
除了难以辨认(!!!)之外,还需要很长时间。任何人都可以提出任何改进方法吗?
答案 0 :(得分:3)
您可以从计算所有轨迹对之间的距离开始,而不是按需重新计算每对轨迹之间的距离。您可以将它们存储在字典中并根据需要进行查找。
这样你的内循环for (i,j) ...
将被替换为常数时间查找。
答案 1 :(得分:2)
您可以在距离计算上放弃平方根计算...最大和也将具有最大平方和,尽管这只会产生恒定的加速。
答案 2 :(得分:2)
除了其他人提到的内容之外,这里还有一些兴趣点和建议。 (顺便说一句,mathmike建议生成一个查找列表,所有对距离都是你应该立即放置的。它从你的算法复杂性中去掉了一个O(r ^ 2)。)
首先是行
for ( i, j ) in itertools.izip ( range(k), range(k) ):
A = [ (my_mat[m, i, z] - my_mat[l, j, z])**2 \
for z in xrange(k) ]
可以替换为
for i in xrange(k):
A = [ (my_mat[m, i, z] - my_mat[l, i, z])**2 \
for z in xrange(k) ]
因为i和j在每个循环中总是相同的。这里根本不需要使用izip。
第二,关于线
A = numpy.array( numpy.sqrt (A) ).sum()
你确定这是你想要计算它的方式吗?可能是这样,但它让我感到奇怪,因为如果这更像是矢量之间的欧几里德距离那么这条线将是:
A = numpy.sqrt (numpy.array( A ).sum())
或只是
A = numpy.sqrt(sum(A))
因为我认为将A转换为numpy数组以使用numpy的sum函数会比使用内置的Python sum函数慢,但我可能错了。而且,如果它真的是你想要的欧几里德距离,那么你将以这种方式做更少的sqrt。
第三,您是否意识到您可能尝试迭代的潜在组合数量是多少?对于num_traj = 1000和r = 20的最坏情况,根据我的估计,这大约是6.79E42组合。这对你当前的方法来说非常棘手。即使对于num_traj = 500和r = 5的最佳情况,这是1.28E12组合,这是相当多的,但并非不可能。这是你在这里遇到的真正问题,因为通过参考mathmike的建议,我提到的前两点并不是很重要。
那你能做什么?好吧,你需要更聪明一些。我还不清楚用什么方法可以用来做这件事。我猜你需要以某种方式制作算法启发式算法。我有一个想法是尝试使用启发式的动态编程方法。对于每个轨迹,您可以找到每个轨迹与其他轨迹的每个配对的总和或平均值,并将其用作适应度量。在进入三重奏之前,可以放弃一些具有最低适应度的轨迹。然后你可以用三重奏做同样的事情:找到每个轨迹所涉及的所有三重奏(在剩余的可能轨迹中)的累积距离的总和或平均值,并将其用作适合度量以决定在移动之前丢弃哪些四人组。它不能保证最佳解决方案,但它应该非常好,它将大大降低我认为的解决方案的时间复杂性。
答案 3 :(得分:1)
无论如何,这可能会永远消失,因为你的算法需要大约〜O( C( N, r ) * r^2 )
,其中C( N, r )
是N选择r。对于较小的r(或N),这可能没问题,但如果您绝对需要找到最大值,而不是使用近似启发式,则应尝试使用不同策略进行分支绑定。这可能适用于较小的r,它可以为您节省不必要的重新计算。
答案 4 :(得分:1)
这听起来像是一个“加权集团”的问题:例如找到
r =具有最大兼容性/最大C(5,2)对权重的网络中的5个人
Google“加权集团”算法 - “clique percolation”→3k点击。
但我会选择Justin Peel的方法
因为它是可以理解和可控制的
(拿n2最好的对,从他们最好的n3三倍...
调整n2 n3 ...以轻松权衡运行时间/结果质量。)
已添加18,可以在实施后进行切割 @Jose,看看nbest []序列对你有用会很有趣。
#!/usr/bin/env python
""" cliq.py: grow high-weight 2 3 4 5-cliques, taking nbest at each stage
weight ab = dist[a,b] -- a symmetric numpy array, diag << 0
weight abc, abcd ... = sum weight all pairs
C[2] = [ (dist[j,k], (j,k)) ... ] nbest[2] pairs
C[3] = [ (cliqwt(j,k,l), (j,k,l)) ... ] nbest[3] triples
...
run time ~ N * (N + nbest[2] + nbest[3] ...)
keywords: weighted-clique heuristic python
"""
# cf "graph clustering algorithm"
from __future__ import division
import numpy as np
__version__ = "denis 18may 2010"
me = __file__.split('/') [-1]
def cliqdistances( cliq, dist ):
return sorted( [dist[j,k] for j in cliq for k in cliq if j < k], reverse=True )
def maxarray2( a, n ):
""" -> max n [ (a[j,k], (j,k)) ...] j <= k, a symmetric """
jkflat = np.argsort( a, axis=None )[:-2*n:-1]
jks = [np.unravel_index( jk, a.shape ) for jk in jkflat]
return [(a[j,k], (j,k)) for j,k in jks if j <= k] [:n]
def _str( iter, fmt="%.2g" ):
return " ".join( fmt % x for x in iter )
#...............................................................................
def maxweightcliques( dist, nbest, r, verbose=10 ):
def cliqwt( cliq, p ):
return sum( dist[c,p] for c in cliq ) # << 0 if p in c
def growcliqs( cliqs, nbest ):
""" [(cliqweight, n-cliq) ...] -> nbest [(cliqweight, n+1 cliq) ...] """
# heapq the nbest ? here just gen all N * |cliqs|, sort
all = []
dups = set()
for w, c in cliqs:
for p in xrange(N):
# fast gen [sorted c+p ...] with small sorted c ?
cp = c + [p]
cp.sort()
tup = tuple(cp)
if tup in dups: continue
dups.add( tup )
all.append( (w + cliqwt(c, p), cp ))
all.sort( reverse=True )
if verbose:
print "growcliqs: %s" % _str( w for w,c in all[:verbose] ) ,
print " best: %s" % _str( cliqdistances( all[0][1], dist )[:10])
return all[:nbest]
np.fill_diagonal( dist, -1e10 ) # so cliqwt( c, p in c ) << 0
C = (r+1) * [(0, None)] # [(cliqweight, cliq-tuple) ...]
# C[1] = [(0, (p,)) for p in xrange(N)]
C[2] = [(w, list(pair)) for w, pair in maxarray2( dist, nbest[2] )]
for j in range( 3, r+1 ):
C[j] = growcliqs( C[j-1], nbest[j] )
return C
#...............................................................................
if __name__ == "__main__":
import sys
N = 100
r = 5 # max clique size
nbest = 10
verbose = 0
seed = 1
exec "\n".join( sys.argv[1:] ) # N= ...
np.random.seed(seed)
nbest = [0, 0, N//2] + (r - 2) * [nbest] # ?
print "%s N=%d r=%d nbest=%s" % (me, N, r, nbest)
# random graphs w cluster parameters ?
dist = np.random.exponential( 1, (N,N) )
dist = (dist + dist.T) / 2
for j in range( 0, N, r ):
dist[j:j+r, j:j+r] += 2 # see if we get r in a row
# dist = np.ones( (N,N) )
cliqs = maxweightcliques( dist, nbest, r, verbose )[-1] # [ (wt, cliq) ... ]
print "Clique weight, clique, distances within clique"
print 50 * "-"
for w,c in cliqs:
print "%5.3g %s %s" % (
w, _str( c, fmt="%d" ), _str( cliqdistances( c, dist )[:10]))