Python 如何快速比较列表和集合?
假设我有一张清单Python 如何快速比较列表和集合?,python,algorithm,performance,set,low-latency,Python,Algorithm,Performance,Set,Low Latency,假设我有一张清单 l = [1, 1 , 1, 2, 3, 4, 5, 5] 和两个长度相等的不相交的集 a=(1,3)和b=(2,5) 我想得到l中的元素,也就是a和b中的元素,就像 [1,1,1,3]和[2,5,5] 我尝试了列表理解,比如[x代表x,如果x代表x,如果x代表a],但是如果l,a和b的长度是10^5,那么这需要很长时间 编辑:这些集合是长度相等的不相交集合 编辑:我需要做的是计算l中a中常见的元素(有重复项)减去b中l元素(也有重复项)。因此,上面的示例应该输出1。问题是列
l = [1, 1 , 1, 2, 3, 4, 5, 5]
和两个长度相等的不相交的集
a=(1,3)
和b=(2,5)
我想得到l
中的元素,也就是a
和b
中的元素,就像
[1,1,1,3]
和[2,5,5]
我尝试了列表理解,比如[x代表x,如果x代表x,如果x代表a]
,但是如果l
,a
和b
的长度是10^5,那么这需要很长时间
编辑:这些集合是长度相等的不相交集合
编辑:我需要做的是计算l
中a
中常见的元素(有重复项)减去b
中l
元素(也有重复项)。因此,上面的示例应该输出1
。问题是列表和集合的长度是否与10E5一样长。使用过滤器和itertools仍然需要很长时间
编辑:我现在知道了!显然,我必须用
set()
包装输入集!起初我没有这样做(我只是通过input().split()
)获得的),因为输入已经是唯一的,但不知道列表和集合非常不同,集合速度更快!好的,直到我。您可以使用itertools
模块中的chain
和repeat
函数:
>>> from itertools import repeat,chain
>>> a={1,3}
>>> list(chain.from_iterable((repeat(i,l.count(i)) for i in a)))
[1, 1, 1, 3]
注意:作为一种更有效的方法,您可以为a
使用set
容器,该容器的成员身份检查复杂度为O(1),如果不需要结果作为列表,链,则无需调用列表
或者,作为一种非常优化的方法,您可以使用numpy
,当您处理庞大的列表时,这种方法特别强大:
>>> import numpy as np
>>> l = np.array([1, 1 , 1, 2, 3, 4, 5, 5])
>>> a = (1, 3)
>>> l[np.in1d(l,a)]
array([1, 1, 1, 3])
我很想看看这是如何处理您的数据集的
from pandas import Series
l = Series([1, 1 , 1, 2, 3, 4, 5, 5])
a = Series((1, 3))
b = Series((3, 5))
a_answer, b_answer = list(l[l.isin(a)]), list(l[l.isin(b)])
原因:Pandas的调用实现级别低于Python,所以根据数字的数据类型,它可能比纯Python解决方案更适合您 快点?
由于Kasramvd提出了一种智能的np.inad()
方法,同时还提出了Wesley的马力
框架,让我来设定量化标准,以便能够处理单个解决方案的质量
让我们既公平又定量:
更多,一旦游戏中有10E+5项处理效率和速度处理内存处理矢量化潜在的和(可能)隐藏的不良副作用,CPU缓存延迟掩蔽更差或更好的非CPU数据访问时间(以及更多的巨魔)这些都是敌人,我们必须在生产中与之共存:
总结:
内森的set
方法对于所有测试量表来说都要快得多
Nathan的方法处理小型、1E+4
和1E+6
缩放集的速度要快得多,这得益于集合
-s中独特元素的python散列集合中隐藏的优越搜索能力(正如集合
类型正是为其引入的)
然而,O(m*n)
/O(n^2)
无法证明
这些假定的复杂性模型应该意味着,随着m
,n
规模的增长,与基于相同list
/set
的set
方法相比,基于numpy
的方法的不利性能惩罚将随着m
的增加而加速,n
-s
不断增长的set
-尺度表明初始边缘在更大尺度上变得更小
1:3在1E+4
等级上的速度优势降级为仍然适当,但在1E+6
等级上的一些
1:2速度优势较小
实际的代码执行使得任务O(m*n)/O(n^2)-复杂性的先验假设无法在活体内确认。
如何测试它?
下一步重新运行已编译的满量程
getLinA( npL[:1E+9], npA[:1E+9] )
与内森·戴维斯(Nathan Davis)的想法交流激发了事后讨论
计时:请参考实际代码执行工件出现时计时结果的差异(缓存脏度的差异)
对于缩放为1E+4和1E+6大小的对象:
>>> aStopWATCH.start();NathanListPROCESSOR( aLIST, setA, setB );aStopWATCH.stop()
-1
2582L
2673L
2529L
2524L
2888L
2693L
>>> aStopWATCH.start();getLISTinSET( npLIST, npSetA, npSetB );aStopWATCH.stop()
0
129983L
12068L
10699L
10930L
10857L
10999L
10954L
10994L
在numba
的帮助下:
>>> @numba.jit
... def numba_getLISTinSET( npList, npSetA, npSetB ):
... return ( len( npList[ np.in1d( npList, npSetA ) ] ) -
len( npList[ np.in1d( npList, npSetB ) ] )
)
>>> aStopWATCH.start();numba_getLISTinSET( npLIST, npSetA, npSetB );aStopWATCH.stop()
0
165320L
7047L
7328L
7378L
7898L
7519L
7556L
7277L
7296L
7292L
7303L
7302L
7426L
7369L
7307L
最后,几乎是1E+6的规模:
>>> setA = generateSet( 1000000 )
>>> len( setA ) # the cost of uniqueness
235836
>>> setB = generateSet( 1000000 )
>>> npSetA = np.array( list( setA ), dtype = np.int )
>>> npSetB = np.array( list( setB ), dtype = np.int )
>>> aLIST = list( ( np.random.random( 1000000 * 1.1 + 10 ) * 1000000 * 10000 ).astype( np.int ) )[:1000000]
>>> len( aLIST )
1000000
>>> npLIST = np.array( aLIST, dtype = np.int )
#----------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-------------
#---------------------| |-------------
>>> aStopWATCH.start();numba_getLISTinSET( npLIST, npSetA, npSetB );aStopWATCH.stop()
6
406061L
403946L
409831L
409329L
408920L
#----------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-------------
#---------------------| |-------------
>>> aStopWATCH.start();NathanListPROCESSOR( aLIST, setA, setB );aStopWATCH.stop()
785334
200755L
196791L
195540L
196606L
202483L
197094L
199481L
196651L
200969L
198856L
202039L
200152L
202364L
根本的问题是,您没有为作业使用适当的数据结构。
在这种情况下,使用元组表示集合对于小集合可能是合适的,
但对于大型集合,您可以期望搜索平均值
列表中每个元素的集合组合大小的一半
这实际上是其中的一组。
对于列表中不在任一集合中的每个元素,
我们必须搜索这两个集合的所有元素来确定它
所以任何基于这些数据结构的算法
(即,使用元组表示集合)
最多为O(m*n)
,其中m
是列表的大小
而n
是集合的大小
我们真的没有办法减少m
组件
-我们必须检查列表中的每个元素以确定哪一组
(如有的话)它属于
但是,我们可以减少n
组件。
怎么用?通过为我们的集合使用更高效的数据结构
幸运的是,这并不难,因为Python包含一个内置的set
类型。
因此,第一步是构造两个集合:
a = set((1, 3))
b = set((2, 5))
现在,我们可以轻松(高效)确定元素e
是否在以下集合之一中:
e = 1
e in a # => True
e in b # => False
现在,我们只需要循环输入列表并累积结果:
l = [1, 1, 3, 2, 5, 7, 8, 3, 2, 1]
result = 0 # accumulator for result
for e in l:
if e in a:
result += 1
elif e in b:
result -= 1
print result # prints "2"
你能用不同的吗
>>> @numba.jit
... def numba_getLISTinSET( npList, npSetA, npSetB ):
... return ( len( npList[ np.in1d( npList, npSetA ) ] ) -
len( npList[ np.in1d( npList, npSetB ) ] )
)
>>> aStopWATCH.start();numba_getLISTinSET( npLIST, npSetA, npSetB );aStopWATCH.stop()
0
165320L
7047L
7328L
7378L
7898L
7519L
7556L
7277L
7296L
7292L
7303L
7302L
7426L
7369L
7307L
>>> setA = generateSet( 1000000 )
>>> len( setA ) # the cost of uniqueness
235836
>>> setB = generateSet( 1000000 )
>>> npSetA = np.array( list( setA ), dtype = np.int )
>>> npSetB = np.array( list( setB ), dtype = np.int )
>>> aLIST = list( ( np.random.random( 1000000 * 1.1 + 10 ) * 1000000 * 10000 ).astype( np.int ) )[:1000000]
>>> len( aLIST )
1000000
>>> npLIST = np.array( aLIST, dtype = np.int )
#----------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-------------
#---------------------| |-------------
>>> aStopWATCH.start();numba_getLISTinSET( npLIST, npSetA, npSetB );aStopWATCH.stop()
6
406061L
403946L
409831L
409329L
408920L
#----------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-------------
#---------------------| |-------------
>>> aStopWATCH.start();NathanListPROCESSOR( aLIST, setA, setB );aStopWATCH.stop()
785334
200755L
196791L
195540L
196606L
202483L
197094L
199481L
196651L
200969L
198856L
202039L
200152L
202364L
a = set((1, 3))
b = set((2, 5))
e = 1
e in a # => True
e in b # => False
l = [1, 1, 3, 2, 5, 7, 8, 3, 2, 1]
result = 0 # accumulator for result
for e in l:
if e in a:
result += 1
elif e in b:
result -= 1
print result # prints "2"