【问题标题】:Updating nested list takes too long in python在 python 中更新嵌套列表需要很长时间
【发布时间】:2017-04-19 03:36:34
【问题描述】:

我正在尝试在 python 中实现棕色聚类算法。

我有cluster = List[List]的数据结构

在任何时候,外部列表长度最大为 40 或 41。

但内部列表包含英文单词,例如“the”、“hello”等

所以我总共有 8000 个单词(词汇),最初前 40 个单词被放入集群。

我将词汇表从 41 迭代到 8000 # 做一些计算,这需要更少的时间。 # 合并列表中的 2 项并从列表中删除一项 # 例如:如果 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]

但是随着我对词汇表的迭代,行 clusters[c1] = clusters[c1] + clusters[c1] 所花费的时间逐渐增加。最初为 41-50 是 1 秒,但词汇中每 20 个项目的时间增加 1 秒。

在我的整个代码中仅注释 clusters[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的新手,如果这里的格式不正确,请见谅。

谢谢

【问题讨论】:

  • 请提供一段独立的代码,没有未提及的依赖项。
  • 首先,您的意思是clusters[c1] = clusters[c1] + clusters[c2]?其次,连接列表很慢。您可以将vocabulary 元素的副本插入clusters 而不是使用原始元素,然后使用+= 而不是+ 来执行可变连接,而不是通过构建新列表。
  • 是的,我的意思是 clusters[c1] = clusters[c1] + clusters[c2]。
  • clusters[c1] = clusters[c1] + clusters[c2] 或 cluster[c1] += cluster[c2] 也会产生任何性能差异。 c2 也可能是词汇表中单词的不同组合,因此我需要从 cluster[c2] 复制项目
  • 我认为您可能需要考虑使用“不相交集”数据结构,而不是列表列表来描述您的语料库。它将为联合 集合成员操作提供近乎恒定的时间(实际上是逆阿克曼)。今天晚些时候我会写一个更详细的回复。

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


【解决方案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 的 Intel 封装,所以我的性能可能比在您的机器上稍快。

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()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-26
    • 1970-01-01
    相关资源
    最近更新 更多