我到处寻找,但显然我似乎无法找到正确的关键字来搜索合适的解决方案,所以问题出现了:
*
我有一组P元素[A,B .... Y,Z]和一个PxP值矩阵 它代表了每对元素之间的相似性(所以 主对角线为100%,每隔一个单元格的数字在0%和0之间 100%)。我想将此集合划分为N个元素的组,以便 该解决方案倾向于最小化组的平均内部相似性
*
你能告诉我如何做到这一点吗?我已经尝试过研究标准分区算法,但大多数都不适用,因为权重取决于对,而不是个体。
谢谢!
答案 0 :(得分:0)
不幸的是,这个问题是NP难的,这意味着不可能有一个多项式时间算法来解决每个实例的最优性。我将从最大平分减少。在该问题的决策问题变体中,我们给出了图G和数k,并且要求将G的顶点分成两个相等大小的部分,使得两个部分之间的边数至少为k。 These slides通过从更一般的最大割限问题中减少来表明最大二分是NP难的,其中2部分不需要具有相同数量的顶点。
给定图G =(V,E)和数k,减少为:
在此构造的输入上为您的问题运行任何精确算法,并让此算法提供的最优解具有平均相似度y。 y =(y1 + y2)/ 2,其中y1和y2是每组的平均相似度。让我们在第一组z1中调用类似的无序对的数量(即,无序对(i,j),使得X [i] [j] = 1)。由于我们需要处理的唯一相似度得分是1和0,所以y1只是z1除以第一组中无序对的总数,正是(| V | / 2)(| V | / 2-1) )/ 2,所以y1 = 2 * z1 /((| V | / 2)(| V | / 2-1))。同样适用于y2。因此,就z1和z2而言,y =(z1 + z2)/((| V | / 2)(| V | / 2-1))。由于分母是常数,通过最大化组内平均相似度y,您的算法也最大化z1 + z2,也就是说,它最大化组内类似对的总数。
需要注意的关键是,在任何解决方案中,原始图的每个边都必须出现在其中一个组中或两个不同的组之间:即,对于任何解Y,nEdgesWithinAGroup(Y)+ nEdgesBetweenGroups( Y)= | E |,因此最小化组内边缘的数量与最大化组间边缘的数量相同。
由于假设你的问题的算法返回一个具有最小可能y的解,并且我们在上面已经确定这也意味着z1 + z2的最小可能值,而且后者意味着之间的最大可能数量 - 组边缘,它遵循两组之间的边数,| E | - z1 - z2,是最大可能的。因此,解决原始最大二分问题的剩余部分是将该值与给定的k值进行比较,如果是> = k,则返回YES,否则返回NO。
以上暗示,给定任何用于解决问题的多项式时间算法,以及NP-hard最大二分问题的任何实例,我们可以在多项式时间内构造问题的实例,解决问题并转换解决方案解决原始的最大二分问题 - 也就是说,它意味着我们可以在多项式时间内解决NP难问题。这意味着你的问题本身就是NP难的。
答案 1 :(得分:0)
如果我没有完全误解你的问题而你想在这里做一个糟糕的方式,那就是: 蛮力方法:
Python实现:
def getValues(matrix):
values=[]
count=1
while ((len(matrix)- count)>0):
j= count
for i in range(len(matrix)- count ):
values.append(matrix[count-1][j ] )
j+=1
count+=1
return values
def c(arr, curr, end,k ,n , comb=[]):
"""get combinations for list length n and choose k elem"""
if comb is None:
comb = []
elif n ==1 :
comb = []
if ((arr.count(1) is not k) and (curr < end)):
tmparr= [ i for i in arr]
tmparr[curr]= 1
c(tmparr, curr+ 1 , end,k ,n + 1 , comb)
tmparr[curr]= 0
c(tmparr, curr+ 1 , end,k ,n + 1 , comb)
if arr.count(1) ==k :
comb.append(arr)
if n is 1:
return comb
def combos(l, choose):
"""
use this w/ c() to get combinations
"""
arr = [1 for i in l]
return c(arr,0 , len(l), choose,1 )
def getComb(combos, elem):
"""
EX. combos=[0,1,1] elem=["A","B","C"]
return ["B","C"]
"""
result= [ ]
for i in combos:
tmp= ""
for j in range(len(i)):
if i[j] is 1:
tmp+= elem[j]
result.append(tmp)
return result
def subSum(sub,d):
"""
EX. sub = "abc" then return value d["ab"]+d["ac"]+d["bc"]
sub -- list of string elements
d -- dictionary
"""
if( len(sub) is 2):
return d[sub[0]+ sub [1]]
sum=0
for i in range(len(sub)-1) :
sum+=d [ sub[0]+ sub [i+1] ]
return sum+ subSum(sub[1:],d)
def contains(a,b):
for i in a:
if i in b:
return True
return False
#**************INPUT HERE**************#
# elements
e = ["A","B", "C", "D", "E", "F"] # partition set into N
N = 2
matrix =[ [100,2,3,4,5,6],
[ 2, 100,9,16,23 ,30] ,
[ 44,22,100,11,5 ,2] ,
[ 11 ,22,33,100, 44, 55],
[1 ,6,7,13,100, 20 ],
[1 ,1,2,3,5,100 ] ]
#**************************************#
if len(matrix) is len(e):
p = getComb(combos(e,(int)(len( matrix)/N)),e)
q = getComb(combos(e,2),e)
values = getValues(matrix)
# make lookup for subSum()
d = {}
for i in range(len(q)):
d[q[i]]=values[i]
result=[]
for i in range(N):
sums = [subSum(i, d) for i in p]
m = min(sums)
s = p[sums.index(m)]
result.append(s)
for i in p:
if contains(s, i):
p.remove(i)
print(result) # this is the answer