我有一个我想要聚集的字符串:
s = 'AAABBCCCCC'
我事先并不知道我会得到多少个集群。我所拥有的只是一个成本函数,可以进行聚类并给它一个分数。
群集大小也存在约束:它们必须在[a,b]
的范围内在我的例子中,对于a=3
和b=4
,所有可能的聚类都是:
[
['AAA', 'BBC', 'CCCC'],
['AAA', 'BBCC', 'CCC'],
['AAAB', 'BCC', 'CCC'],
]
每个群集的连接必须提供字符串s
费用函数是这样的
cost(clustering) = alpha*l + beta*e + gamma*d
其中:
l = variance(cluster_lengths)
e = mean(clusters_entropies)
d = 1 - nb_characters_in_b_that_are_not_in_a)/size_of_b
(适用于b
连续的a
)群集alpha
,beta
,gamma
是权重此成本函数为最佳情况提供低成本(0):
理论上,解决方案是计算此字符串的所有可能compositions的成本并选择最低值。但这需要太多时间。
是否有任何聚类算法可以在合理的时间内根据此成本函数找到最佳聚类?
答案 0 :(得分:7)
动态编程方法应该适用于此。
想象一下,首先,cost(clustering)
等于构成聚类的所有群集的cost(cluster)
之和。
然后,简单的DP功能定义如下:
F[i] = minimal cost of clustering the substring s[0:i]
并按以下方式计算:
for i = 0..length(s)-1:
for j = a..b:
last_cluster = s[i-j..i]
F[i] = min(F[i], F[i - j] + cost(last_cluster))
当然,首先必须将F的值初始化为某些无限值或空值才能正确应用min函数。
要实际恢复答案,您可以存储其他值P[i]
,其中包含最后一个群集的长度,并具有字符串s [0..i]的最佳聚类。
更新F [i]时,还会更新P[i]
。
然后,恢复答案有点麻烦:
current_pos = length(s) - 1
while (current_pos >= 0):
current_cluster_length = P[current_pos]
current_cluster = s[(current_pos - current_cluster_length + 1)..current_pos]
// grab current_cluster to the answer
current_pos -= current_cluster_length
请注意,在这种方法中,您将以相反的顺序获取clsuters,这意味着从最后一个群集一直到第一个群集。
现在让我们将这个想法应用于最初的问题。 我们想要的是使成本(聚类)或多或少是线性的,这样我们就可以逐个簇地计算它,而不是为整个聚类计算它。
我们的DP函数F
的第一个参数将像以前一样i
,我们找到了最佳答案的子字符串s[0:i]
中的字符数。
像往常一样,F
函数的含义是给定参数可以实现的最小成本。
成本函数的参数e = mean(clusters_entropies)
已经是线性的,可以逐个簇地计算,所以这不是问题。
参数l = variance(cluster_lengths)
稍微复杂一些。
n
值的方差定义为Sum[(x[i] - mean)^2] / n
。
mean
是预期值,即mean = Sum[x[i]] / n
。
另请注意,Sum[x[i]]
是所有聚类的长度之和,在我们的示例中,它始终是固定的,等于length(s)
。
因此,mean = length(s) / n
。
好的,除了l
参数之外,我们或多或少地将n
成本函数的一部分变为线性。我们将此参数(即所需聚类中的聚类数)添加为F
函数的参数。
我们还将有一个参数cur
,它表示当前在给定状态下组装的集群数。
成本函数的参数d
还需要在我们的DP函数F
中添加其他参数,即j
,sz
,我们最后一个集群的大小分区。
总的来说,我们提出了一个DP函数F[i][n][cur][sz]
,它为我们提供了将字符串s[0:i]
分区为n
集群的最小成本函数,其中cur
当前正在构建最后一个簇的大小等于sz
。当然,我们的责任是确保a<=sz<=b
。
最小成本函数的答案将是DP函数n
的所有可能a<=sz<=b
和F[length(s)-1][n][n][sz]
值中的最小值。
现在请注意,这次我们甚至不需要伴随P
函数来存储最后一个集群的长度,因为我们已将该信息作为最后sz
参数包含在F
函数中。
但是,我们将在P[i][n][cur][sz]
中使用指定的参数在最佳聚类中存储倒数第二个簇的最后一个簇的长度。我们将使用该值来恢复我们的解决方案。
因此,假设参数F
和n=n0
达到sz=sz0
的最小值,我们将能够以下列方式恢复答案:
current_pos = length(s) - 1
current_n = n0
current_cluster_size = sz0
while (current_n > 0):
current_cluster = s[(current_pos - current_cluster_size + 1)..current_pos]
next_cluster_size = P[current_pos][n0][current_n][current_cluster_size]
current_n--;
current_pos -= current_cluster_size;
current_cluster_size = next_cluster_size
现在让我们来计算F
。
我将省略边角情况和范围检查,但仅使用一些无限值初始化F
就足够了。
// initialize for the case of one cluster
// d = 0, l = 0, only have to calculate entropy
for i=0..length(s)-1:
for n=1..length(s):
F[i][n][1][i+1] = cluster_entropy(s[0..i]);
P[i][n][1][i+1] = -1; // initialize with fake value as in this case there is no previous cluster
// general case computation
for i=0..length(s)-1:
for n=1..length(s):
for cur=2..n:
for sz=a..b:
for prev_sz=a..b:
cur_cluster = s[i-sz+1..i]
prev_cluster = s[i-sz-prev_sz+1..i-sz]
F[i][n][cur][sz] = min(F[i][n][cur][sz], F[i-sz][n][cur - 1][prev_sz] + gamma*calc_d(prev_cluster, cur_cluster) + beta*cluster_entropy(cur_cluster)/n + alpha*(sz - s/n)^2)