我正在尝试在python中实现棕色聚类算法。
我有cluster = List [List]
的数据结构在任何给定时间,外部列表长度最多为40或41。
但是内部列表包含英语单词,例如'the','hello'等
所以我总共有8000个单词(词汇表),最初将40个单词放入群集中。
我将我的词汇量从41重复到8000 #做一些计算,这需要花费很少的时间。 #合并列表中的2项,并从列表中删除一项 #ex:如果c1和c2是簇的项目,那么
for i in range(41, 8000):
clusters.append(vocabulary[i])
c1 = computation 1
c2 = computation 2
clusters[c1] = clusters[c1] + clusters[c2]
del clusters[c2]
但是,当我迭代我的词汇时,线簇[c1] =簇[c1] +簇[c1]的时间逐渐增长。最初为41-50是1秒,但对于词汇表中的每20个项目,时间增加1秒。
在我的整个代码中仅对群集[c1] = clusters [c1] + clusters [c1]进行注释时,我观察者所有迭代都需要恒定的时间。我不知道如何才能加快这个过程。
for i in range(41, 8000):
clusters.append(vocabulary[i])
c1 = computation 1
c2 = computation 2
#clusters[c1] = clusters[c1] + clusters[c2]
del clusters[c2]
我是stackoverflow的新手,如果此处有任何不正确的格式,请原谅。
谢谢
答案 0 :(得分:1)
您遇到的问题是列表连接是线性时间操作。因此,您的整个循环为O(n^2)
(对于大于1000的n
来说,这个速度非常慢)。这忽略了复制这些大型列表对缓存性能等的影响等等。
我建议的解决方案是使用disjoint set数据结构。这是一个基于树的数据结构,在执行查询时会“自我展平”,从而导致“合并”集群的运行时间非常快。
基本思想是每个单词都以它自己的“单例”树开始,而合并集群包括使一棵树的根成为另一棵树的子。这会重复(需要注意平衡),直到你拥有所需数量的簇。
我写了example implementation(GitHub链接),假设每组的元素都是数字。只要你有从词汇术语到整数的映射,它应该适合你的目的。 (注意:我已经完成了一些初步测试,但我现在在5分钟内写完了所以我建议检查我的工作。;)
)
要在代码中使用,我会执行以下操作:
clusters = DisjointSet(8000)
# some code to merge the first 40 words into clusters
for i in range(41, 8000):
c1 = some_computation() # assuming c1 is a number
c2 = some_computation() # assuming c2 is a number
clusters.join(c1, c2)
# Now, if you want to determine if some word with number k is
# in the same cluster as a word with number j:
print("{} and {} are in the same cluster? {}".format(j, k, clusters.query(j, k))
虽然集合提供比列表更快的访问时间,但它们在复制时实际上具有更差的运行时。这在理论上是有道理的,因为set
对象实际上必须分配和分配更多内存空间,而不是适当加载因子的列表。此外,它可能插入如此多的项目可能导致整个哈希表的“重新散列”,这是最坏情况下的二次时操作。
然而,练习是我们现在关注的问题,因此我进行了一项快速实验,以确定组合比组合更糟糕。
如果有人感兴趣,执行此测试的代码如下。我正在使用英特尔的Python包装,因此我的性能可能比您的机器上的性能略快。
import time
import random
import numpy as np
import matplotlib.pyplot as plt
data = []
for trial in range(5):
trial_data = []
for N in range(0, 20000, 50):
l1 = random.sample(range(1000000), N)
l2 = random.sample(range(1000000), N)
s1 = set(l1)
s2 = set(l2)
# Time to concatenate two lists of length N
start_lst = time.clock()
l3 = l1+l2
stop_lst = time.clock()
# Time to union two sets of length N
start_set = time.clock()
s3 = s1|s2
stop_set = time.clock()
trial_data.append([N, stop_lst - start_lst, stop_set - start_set])
data.append(trial_data)
# average the trials and plot
data_array = np.array(data)
avg_data = np.average(data_array, 0)
fig = plt.figure()
ax = plt.gca()
ax.plot(avg_data[:,0], avg_data[:,1], label='Lists')
ax.plot(avg_data[:,0], avg_data[:,2], label='Sets')
ax.set_xlabel('Length of set or list (N)')
ax.set_ylabel('Seconds to union or concat (s)')
plt.legend(loc=2)
plt.show()