【问题标题】:Fast way to create a binary list C of len(A) given two lists A and B给定两个列表 A 和 B,创建 len(A) 的二进制列表 C 的快速方法
【发布时间】:2021-03-02 06:55:42
【问题描述】:

我有两个列表 AB largelen(A) 大约是 200K,len(B) 大约是 20K)。我想生成一个长度为len(A) 的新列表C,它在i 的所有位置都有1,其中A[i]B 中,否则为0。在 Python 中执行此操作的计算效率最高的方法是什么?换句话说,有没有比下面更有效的方法?

C = []
for x in A:
    if x in B:
        C.append(1)
    else:
        C.append(0)

【问题讨论】:

  • 是的,将B 设为set。否则,这个算法是O(M*N),而如果你先做B = set(B),你会得到O(N)
  • 列表元素中是否有重复值?
  • @MichaelRuth No
  • @juanapa.arrivillaga 啊哈,把B做成一套就行了,那就无所谓了。
  • @MichaelRuth 如果有重复,为什么重要?

标签: python list performance


【解决方案1】:

为了性能,您应该将 B 设为 set 对象。 set 有固定时间的成员资格测试。这将提高您的整体算法的效率,从多项式时间 O(N*M) 到线性时间 O(N):

c = []
B = set(B)
for x in A:
    if x in B:
        C.append(1)
    else:
        C.append(0)

【讨论】:

  • 不错的解决方案,唯一需要注意的是 B 被更改为 set,破坏了排序并删除了可能的重复元素(这不是具体问题的问题,正如我读到的那样上面的 cmets,但可能是通用解决方案)。如果B 仍打算用作下面的原始list,则应为set(B) 使用新名称。
【解决方案2】:

您可以使用列表推导,但请记住,与循环方法相比,它不会在效率方面提供很大的改进:

[1 if x in B else 0 for x in A]

更新:最快的方法是从 B 准备一张地图并将其用于搜索。

B_map = {elt: True for elt in B}
C = [1 if B_map.get(x, False) else 0 for x in A]

【讨论】:

  • 我觉得应该是[1 if x in B else 0 for x in A]
  • 是的,你是对的,我看错了问题。
  • 这不是一种计算效率更高的方法,它完全等效。
  • 这里的问题是搜索,对于 len(A) = m 和 len(B) = n 的每个元素,它是 O(n)。如果您搜索列表,请先对其进行排序并使用 bisect 模块进行二分搜索。该模块提供了一种方法来执行此操作,同时保留列表的原始索引。
  • 最快的方法可能是用B 准备一个字典对象并使用它。我已经更新了我的答案以包含它。
【解决方案3】:

tldr;B 转换为set 并使用列表推导式。 @Krishna Chaurasia 对set(B) 的理解是提出的最快解决方案。


正如其他人所建议的那样,您绝对应该使用set(B) 进行搜索。这提高了大型 B 的性能。

现在提高大型A 的性能,因此提高C 的性能。预分配C。请注意,预分配仅在替换 append() 的情况下才有用,它不会加快理解速度。

from timeit import timeit

setup = '''
A = list(range(1000000))
B = set(range(500000, 1500000))
'''

stmt_for_no_preallocation = '''
C = []
for x in A:
    if x in B:
        C.append(1)
    else:
        C.append(0)
'''

stmt_for_preallocation = '''
C = [0] * len(A)
for i, x in enumerate(A):
    if x in B:
        C[i] = 1
'''

stmt_comp = 'C = [1 if x in B else 0 for x in A]'

stmt_comp_map = '''
B_map = {elt: True for elt in B}
C = [1 if B_map.get(x, False) else 0 for x in A]
'''

stmt_comp_map_alias_get = '''
B_map = {elt: True for elt in B}
get = B_map.get
C = [1 if get(x, False) else 0 for x in A]
'''

n = 10
print('for without preallocating C')
print(timeit(stmt_for_no_preallocation, setup=setup, number=n))
print('for with preallocating C')
print(timeit(stmt_for_preallocation, setup=setup, number=n))
print('list comprehension')
print(timeit(stmt_comp, setup=setup, number=n))
print('list comprehension with map')
print(timeit(stmt_comp_map, setup=setup, number=n))
print('list comprehension with map and alias B_map.get to local scope')
print(timeit(stmt_comp_map_alias_get, setup=setup, number=n))

输出:

对于没有预分配 C 0.900570491 用于预分配 C 0.723135233 列表理解 0.51548745 用地图列出理解 1.5568766299999997 使用 map 和别名 B_map.get 到本地范围的列表理解 1.3334449179999996

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-27
    • 2021-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-09-02
    • 2020-07-15
    • 2018-06-20
    • 2021-11-01
    相关资源
    最近更新 更多