在python中更新嵌套列表需要太长时间

时间:2017-04-19 03:36:35

标签: python python-2.7 python-3.x

我正在尝试在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的新手,如果此处有任何不正确的格式,请原谅。

谢谢

1 个答案:

答案 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对象实际上必须分配和分配更多内存空间,而不是适当加载因子的列表。此外,它可能插入如此多的项目可能导致整个哈希表的“重新散列”,这是最坏情况下的二次时操作。

然而,练习是我们现在关注的问题,因此我进行了一项快速实验,以确定组合比组合更糟糕。

Set vs List

如果有人感兴趣,执行此测试的代码如下。我正在使用英特尔的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()